306 lines
15 KiB
Python
306 lines
15 KiB
Python
import pandas as pd
|
|
import jwt
|
|
import uuid
|
|
import time
|
|
import requests
|
|
import json
|
|
import hashlib
|
|
from urllib.parse import urlencode
|
|
|
|
class HTS:
|
|
|
|
bithumb = None
|
|
#accessKey = "a5d33ce55f598185d37cd26272341b7b965c31a59457f7" # 본인의 Connect Key를 입력한다.
|
|
#secretKey = "ODBiYWFmNWE2MTkwYjdhMTNhZTM1YjU5OGY4OGE2MGNkNDY2NzMzMjE2Nzc5NDVlMzBhMDk3NTNmM2M2Mg==" # 본인의 Secret Key를 입력한다.
|
|
accessKey = "1b10570cfaacb728fbdbb0b289c367e95ed937b1bd4157" # 본인의 Connect Key를 입력한다.
|
|
secretKey = "MGU0NzYzMzQyNTJhMDk2MjUxMGFmZWFjYjkyNThlYWJiNmIzOGNjODZjZWE1NmQyMzdiN2JiNDM1Njg1MA==" # 본인의 Secret Key를 입력한다.
|
|
|
|
apiUrl = 'https://api.bithumb.com'
|
|
|
|
def __init__(self):
|
|
#self.bithumb = pybithumb.Bithumb(self.con_key, self.sec_key)
|
|
|
|
self.bithumb = None
|
|
#self.accessKey = "a5d33ce55f598185d37cd26272341b7b965c31a59457f7" # 본인의 Connect Key를 입력한다.
|
|
#self.secretKey = "ODBiYWFmNWE2MTkwYjdhMTNhZTM1YjU5OGY4OGE2MGNkNDY2NzMzMjE2Nzc5NDVlMzBhMDk3NTNmM2M2Mg==" # 본인의 Secret Key를 입력한다.
|
|
self.accessKey = "1b10570cfaacb728fbdbb0b289c367e95ed937b1bd4157" # 본인의 Connect Key를 입력한다.
|
|
self.secretKey = "MGU0NzYzMzQyNTJhMDk2MjUxMGFmZWFjYjkyNThlYWJiNmIzOGNjODZjZWE1NmQyMzdiN2JiNDM1Njg1MA==" # 본인의 Secret Key를 입력한다.
|
|
self.apiUrl = 'https://api.bithumb.com'
|
|
|
|
return
|
|
|
|
def append(self, stock, df=None, data_1=None):
|
|
if df is not None:
|
|
for i in range(len(df)):
|
|
stock['PRICE'].append(
|
|
{
|
|
"ymd": df.index[i],
|
|
"close": df['close'].iloc[i],
|
|
"diff": 0,
|
|
"open": df['open'].iloc[i],
|
|
"high": df['high'].iloc[i],
|
|
"low": df['low'].iloc[i],
|
|
"volume": df['volume'].iloc[i],
|
|
"avg5": -1, "avg20": -1, "avg60": -1, "avg120": -1, "avg240": -1, "avg480": -1,
|
|
"bolingerband_upper": -1, "bolingerband_lower": -1, "bolingerband_middle": -1, "bolingerband_bwi": -1,
|
|
"ichimokucloud_changeLine": -1, "ichimokucloud_baseLine": -1, "ichimokucloud_leadingSpan1": -1, "ichimokucloud_leadingSpan2": -1,
|
|
"stochastic_fast_k_1": -1, "stochastic_slow_k_1": -1, "stochastic_slow_d_1": -1,
|
|
"stochastic_fast_k_2": -1, "stochastic_slow_k_2": -1, "stochastic_slow_d_2": -1,
|
|
"stochastic_fast_k_3": -1, "stochastic_slow_k_3": -1, "stochastic_slow_d_3": -1,
|
|
"rsi": -1, "rsis": -1,
|
|
"macd": -1, "macds": -1, "macdo": -1, "nor_macd": -1, "nor_macds": -1, "nor_macdo": -1,
|
|
})
|
|
|
|
if data_1 is not None:
|
|
stock['PRICE'].append(
|
|
{
|
|
"ymd": data_1.index[-1],
|
|
"close": data_1['close'].iloc[-1],
|
|
"diff": 0,
|
|
"open": data_1['open'].iloc[-1],
|
|
"high": data_1['high'].iloc[-1],
|
|
"low": data_1['low'].iloc[-1],
|
|
"volume": data_1['volume'].iloc[-1],
|
|
"avg5": -1, "avg20": -1, "avg60": -1, "avg120": -1, "avg240": -1, "avg480": -1,
|
|
"bolingerband_upper": -1, "bolingerband_lower": -1, "bolingerband_middle": -1, "bolingerband_bwi": -1, "bolingerband_nor_bwi": -1,
|
|
"envelope_upper": -1, "envelope_lower": -1, "envelope_middle": -1,
|
|
"ichimokucloud_changeLine": -1, "ichimokucloud_baseLine": -1, "ichimokucloud_leadingSpan1": -1,
|
|
"ichimokucloud_leadingSpan2": -1,
|
|
"stochastic_fast_k_1": -1, "stochastic_slow_k_1": -1, "stochastic_slow_d_1": -1,
|
|
"stochastic_fast_k_2": -1, "stochastic_slow_k_2": -1, "stochastic_slow_d_2": -1,
|
|
"stochastic_fast_k_3": -1, "stochastic_slow_k_3": -1, "stochastic_slow_d_3": -1,
|
|
"rsi": -1, "rsis": -1,
|
|
"macd": -1, "macds": -1, "macdo": -1, "nor_macd": -1, "nor_macds": -1, "nor_macdo": -1,
|
|
})
|
|
return
|
|
|
|
def getCoinRawData(self, ticker_code, minute=None, day=False, week=False, month=False, to=None, endpoint='/v1/candles'):
|
|
url = None
|
|
if minute == 0:
|
|
# 현재가 정보
|
|
url = (self.apiUrl + "/v1/ticker?markets=KRW-{}").format(ticker_code)
|
|
|
|
headers = {"accept": "application/json"}
|
|
response = requests.get(url, headers=headers)
|
|
json_data = json.loads(response.text)
|
|
df_temp = pd.DataFrame(json_data)
|
|
if 'trade_date_kst' not in df_temp or 'trade_time_kst' not in df_temp:
|
|
return None
|
|
df = pd.DataFrame()
|
|
df['datetime'] = pd.to_datetime(df_temp['trade_date_kst'], format='%Y-%m-%dT%H:%M:%S')
|
|
df['open'] = df_temp['opening_price']
|
|
df['close'] = df_temp['trade_price']
|
|
df['high'] = df_temp['high_price']
|
|
df['low'] = df_temp['low_price']
|
|
df['volume'] = df_temp['trade_volume']
|
|
df = df.set_index('datetime')
|
|
df = df.astype(float)
|
|
df["datetime"] = df.index
|
|
else:
|
|
# 분봉
|
|
if minute is not None and minute in {1, 3, 5, 10, 15, 30, 60, 240}:
|
|
if to is None:
|
|
url = (self.apiUrl + endpoint + "/minutes/{}?market=KRW-{}&count=3000").format(minute, ticker_code)
|
|
else:
|
|
url = (self.apiUrl + endpoint + "/minutes/{}?market=KRW-{}&count=3000&to={}").format(minute, ticker_code, to)
|
|
if day:
|
|
if to is None:
|
|
url = (self.apiUrl + endpoint + "/days?market=KRW-{}&count=3000").format(ticker_code)
|
|
else:
|
|
url = (self.apiUrl + endpoint + "/days?market=KRW-{}&count=3000&to={}").format(ticker_code, to)
|
|
if week:
|
|
if to is None:
|
|
url = (self.apiUrl + endpoint + "/weeks?market=KRW-{}&count=3000").format(ticker_code)
|
|
else:
|
|
url = (self.apiUrl + endpoint + "/weeks?market=KRW-{}&count=3000&to={}").format(ticker_code, to)
|
|
if month:
|
|
if to is None:
|
|
url = (self.apiUrl + endpoint + "/months?market=KRW-{}&count=3000").format(ticker_code)
|
|
else:
|
|
url = (self.apiUrl + endpoint + "/months?market=KRW-{}&count=3000&to={}").format(ticker_code, to)
|
|
|
|
if url is None:
|
|
return None
|
|
|
|
headers = {"accept": "application/json"}
|
|
response = requests.get(url, headers=headers)
|
|
json_data = json.loads(response.text)
|
|
df_temp = pd.DataFrame(json_data)
|
|
if 'candle_date_time_kst' not in df_temp:
|
|
return None
|
|
df = pd.DataFrame()
|
|
#df.columns = ['datetime', 'open', 'close', 'high', 'low', 'volume']
|
|
#df['datetime'] = pd.to_datetime(df_temp['candle_date_time_kst'])
|
|
df['datetime'] = pd.to_datetime(df_temp['candle_date_time_kst'], format='%Y-%m-%dT%H:%M:%S')
|
|
df['open'] = df_temp['opening_price']
|
|
df['close'] = df_temp['trade_price']
|
|
df['high'] = df_temp['high_price']
|
|
df['low'] = df_temp['low_price']
|
|
df['volume'] = df_temp['candle_acc_trade_volume']
|
|
df = df.set_index('datetime')
|
|
df = df.astype(float)
|
|
df["datetime"] = df.index
|
|
|
|
if df is None:
|
|
return None
|
|
|
|
return df
|
|
|
|
def getTickerList(self):
|
|
url = "https://api.bithumb.com/v1/market/all?isDetails=false"
|
|
headers = {"accept": "application/json"}
|
|
response = requests.get(url, headers=headers)
|
|
|
|
tickets = response.json()
|
|
return tickets
|
|
|
|
def getVirtual_asset_warning(self):
|
|
url = "https://api.bithumb.com/v1/market/virtual_asset_warning"
|
|
headers = {"accept": "application/json"}
|
|
response = requests.get(url, headers=headers)
|
|
warning_list = response.json()
|
|
return warning_list
|
|
|
|
# 거래대금이 많은 순으로 코인리스트를 얻는다.
|
|
def getTopCoinList(self, interval, top):
|
|
return
|
|
|
|
# 현재 가격 얻어오기
|
|
def getCurrentPrice(self, ticker_code, endpoint='/v1/ticker'):
|
|
headers = {"accept": "application/json"}
|
|
url = (self.apiUrl + endpoint + "?markets=KRW-{}").format(ticker_code)
|
|
response = requests.get(url, headers=headers)
|
|
|
|
ticker_state = response.json()
|
|
return ticker_state
|
|
|
|
# 잔고 가져오기
|
|
def getBalances(self, ticker_code=None, endpoint='/v1/accounts'):
|
|
|
|
payload = {
|
|
'access_key': self.accessKey,
|
|
'nonce': str(uuid.uuid4()),
|
|
'timestamp': round(time.time() * 1000)
|
|
}
|
|
jwt_token = jwt.encode(payload, self.secretKey)
|
|
authorization_token = 'Bearer {}'.format(jwt_token)
|
|
headers = {
|
|
'Authorization': authorization_token
|
|
}
|
|
response = requests.get(self.apiUrl + endpoint, headers=headers)
|
|
balances = response.json()
|
|
|
|
"""
|
|
[
|
|
{'currency': 'P', 'balance': '78290', 'locked': '0', 'avg_buy_price': '0', 'avg_buy_price_modified': False, 'unit_currency': 'KRW'},
|
|
{'currency': 'KRW', 'balance': '4218.401653', 'locked': '0', 'avg_buy_price': '0', 'avg_buy_price_modified': False, 'unit_currency': 'KRW'},
|
|
{'currency': 'XRP', 'balance': '13069.27647861', 'locked': '0', 'avg_buy_price': '1917', 'avg_buy_price_modified': False, 'unit_currency': 'KRW'},
|
|
{'currency': 'ADA', 'balance': '6941.65484013', 'locked': '0', 'avg_buy_price': '1260', 'avg_buy_price_modified': False, 'unit_currency': 'KRW'},
|
|
{'currency': 'BSV', 'balance': '0.00005656', 'locked': '0', 'avg_buy_price': '65450', 'avg_buy_price_modified': False, 'unit_currency': 'KRW'},
|
|
{'currency': 'SAND', 'balance': '0.00001158', 'locked': '0', 'avg_buy_price': '544.8', 'avg_buy_price_modified': False, 'unit_currency': 'KRW'},
|
|
{'currency': 'AVAX', 'balance': '26.43960509', 'locked': '0', 'avg_buy_price': '60882', 'avg_buy_price_modified': False, 'unit_currency': 'KRW'},
|
|
{'currency': 'XCORE', 'balance': '0.2119', 'locked': '0', 'avg_buy_price': '0', 'avg_buy_price_modified': False, 'unit_currency': 'KRW'}
|
|
]
|
|
"""
|
|
if ticker_code is None:
|
|
return balances
|
|
else:
|
|
for balance in balances:
|
|
if balance['currency'] == ticker_code:
|
|
return balance
|
|
|
|
return None
|
|
|
|
|
|
def order(self, ticker_code, side, ord_type, volume, price=None, endpoint='/v1/orders'):
|
|
if ord_type=='limit':
|
|
# 지정가 매수 (limit, side=bid) / 매도 (limit, side=ask)
|
|
if price is None:
|
|
return
|
|
|
|
requestBody = dict(market='KRW-'+ticker_code, side=side, volume=volume, price=price, ord_type=ord_type)
|
|
else:
|
|
# 시장가 매수 (price, side=bid) / 매도 (market, side=ask)
|
|
|
|
if ord_type == 'price':
|
|
requestBody = dict(market='KRW-' + ticker_code, side=side, price=price, ord_type=ord_type)
|
|
else:
|
|
requestBody = dict(market='KRW-' + ticker_code, side=side, volume=volume, ord_type=ord_type)
|
|
|
|
# Generate access token
|
|
query = urlencode(requestBody).encode()
|
|
hash = hashlib.sha512()
|
|
hash.update(query)
|
|
query_hash = hash.hexdigest()
|
|
payload = {
|
|
'access_key': self.accessKey,
|
|
'nonce': str(uuid.uuid4()),
|
|
'timestamp': round(time.time() * 1000),
|
|
'query_hash': query_hash,
|
|
'query_hash_alg': 'SHA512',
|
|
}
|
|
jwt_token = jwt.encode(payload, self.secretKey)
|
|
authorization_token = 'Bearer {}'.format(jwt_token)
|
|
headers = {
|
|
'Authorization': authorization_token,
|
|
'Content-Type': 'application/json'
|
|
}
|
|
|
|
response = requests.post(self.apiUrl + endpoint, data=json.dumps(requestBody), headers=headers)
|
|
# handle to success or fail
|
|
#print(response.json())
|
|
if response.status_code == 200:
|
|
return True
|
|
return False
|
|
|
|
# 시장가 매수한다. 2초 뒤 잔고 데이터 리스트 리턴
|
|
def buyCoinMarket(self, ticker_code, price, count=None):
|
|
if price > 5000:
|
|
if price < 50000:
|
|
self.order(ticker_code, side='bid', ord_type='price', volume=count, price=price)
|
|
buy_price = price
|
|
else:
|
|
repeat = 10
|
|
buy_price = int(price / 1000) * 1000
|
|
buy_amount = int(buy_price / repeat)
|
|
while repeat > 0:
|
|
self.order(ticker_code, side='bid', ord_type='price', volume=count, price=buy_amount)
|
|
repeat -= 1
|
|
time.sleep(0.5)
|
|
else:
|
|
buy_price = 0
|
|
return buy_price
|
|
|
|
# 시장가 매도한다. 2초 뒤 잔고 데이터 리스트 리턴
|
|
def sellCoinMarket(self, ticker_code, price, count):
|
|
return self.order(ticker_code, side='ask', ord_type='market', volume=count, price=price)
|
|
|
|
# 지정가 매수한다. 2초 뒤 잔고 데이터 리스트 리턴
|
|
def buyCoinLimit(self, ticker_code, price, count):
|
|
return self.order(ticker_code, side='bid', ord_type='limit', volume=count, price=price)
|
|
|
|
# 지정가 매도한다. 2초 뒤 잔고 데이터 리스트 리턴
|
|
def sellCoinLimit(self, ticker_code, price, count):
|
|
return self.order(ticker_code, side='ask', ord_type='limit', volume=count, price=price)
|
|
|
|
def getOrderBook(self, ticker_code, endpoint='/v1/orderbook'):
|
|
"""
|
|
필드 설명 타입
|
|
market 마켓 코드 String
|
|
timestamp 호가 생성 시각 Long
|
|
total_ask_size 호가 매도 총 잔량 Double
|
|
total_bid_size 호가 매수 총 잔량 Double
|
|
orderbook_units 호가 List of Objects
|
|
> ask_price 매도호가 Double
|
|
> bid_price 매수호가 Double
|
|
> ask_size 매도 잔량 Double
|
|
> bid_size 매수 잔량 Double
|
|
"""
|
|
headers = {"accept": "application/json"}
|
|
url = (self.apiUrl + endpoint + "?markets=KRW-{}").format(ticker_code)
|
|
response = requests.get(url, headers=headers)
|
|
|
|
# 매도 총 잔량: sum([units['ask_size'] for units in orders[0]['orderbook_units']])
|
|
# 매수 총 잔량: sum([units['bid_size'] for units in orders[0]['orderbook_units']])
|
|
orders = response.json()
|
|
return orders |