첫 번째 커밋
This commit is contained in:
343
collectors/crypto/c_bithumb.py
Normal file
343
collectors/crypto/c_bithumb.py
Normal file
@@ -0,0 +1,343 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import sys
|
||||
from collectors.collector import Collector
|
||||
import logging
|
||||
|
||||
# import API
|
||||
import pybithumb
|
||||
|
||||
# curl
|
||||
import json
|
||||
import datetime, time
|
||||
import requests
|
||||
from pandas import DataFrame
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
from db import DB
|
||||
|
||||
|
||||
class Bithumb(Collector):
|
||||
|
||||
def __init__(self, access=None, secret=None):
|
||||
self.remaining_req = {}
|
||||
self._heaeder = {}
|
||||
self._mathod = 'get'
|
||||
self._finance = 'crypto' # 금융 종목
|
||||
self._name = 'bithumb' # 거래소 이름
|
||||
self.leverage = 1
|
||||
self._standard_price = 0
|
||||
self._temp_api_url = 'https://api.bithumb.com/' # 임시 api 주소
|
||||
|
||||
# self._api = pybithumb
|
||||
|
||||
if secret is None:
|
||||
self._api = pybithumb.Bithumb
|
||||
else:
|
||||
self._api = pybithumb.Bithumb(access, secret)
|
||||
|
||||
self._api_url = self._temp_api_url
|
||||
|
||||
self._set_allow_items()
|
||||
|
||||
# get filtered markets
|
||||
self.markets = self._load_markets() # 테스트때 주석
|
||||
|
||||
# 빗썸 거래 기준금액 정의
|
||||
def _get_standard_trade_price(self, markets=None):
|
||||
p_list = []
|
||||
for m in markets:
|
||||
res = pybithumb.get_market_detail(m)
|
||||
# 종목 가격 제한 조건 추가 2019-11-12 => 잡코 방지
|
||||
|
||||
p_list.append(float(res[3])*float(res[4]))
|
||||
|
||||
# if res['status'] == '0000':
|
||||
# p_list.append(float(res['data']['acc_trade_value_24H']))
|
||||
|
||||
p_list.sort(reverse=True)
|
||||
|
||||
return float(p_list[0] - p_list[0] * 0.01)
|
||||
# return float(p_list[1] - p_list[1] * 0.01)
|
||||
|
||||
def _return_list_filted_markets(self, markets):
|
||||
filtered_list = []
|
||||
|
||||
for m in markets:
|
||||
f_if = False
|
||||
|
||||
# 리플 제외
|
||||
if 'XRP' in m:
|
||||
continue
|
||||
|
||||
res = pybithumb.get_market_detail(m)
|
||||
|
||||
# 허용 아이템 조건
|
||||
for i in self.allow_items:
|
||||
if i == m.replace('KRW-', ''):
|
||||
f_if = True
|
||||
|
||||
# 24시간 거래금액 및 종목 가격 제한 조건
|
||||
# f_if = float(res[3]) * float(res[4]) > self._standard_price \
|
||||
# and float(res[3]) > float(100)
|
||||
|
||||
if f_if:
|
||||
item = {
|
||||
'market': 'KRW-' + str(m),
|
||||
'acc_trade_price_24h': float(res[3]) * float(res[4]), # 거래 총 금액
|
||||
'acc_trade_volume_24h': float(res[4]), # 거래량
|
||||
}
|
||||
filtered_list.append(item)
|
||||
|
||||
return filtered_list
|
||||
|
||||
def _save_market_list_to_db(self, markets):
|
||||
for m in markets:
|
||||
self._save_item_data_to_item_table(self._finance, # 금융
|
||||
self._name, # 거래소
|
||||
m['market'], # 종목
|
||||
m['acc_trade_price_24h'], # 24시간 거래 금액
|
||||
m['acc_trade_volume_24h'] # 24시간 거래량
|
||||
)
|
||||
|
||||
def _load_markets(self):
|
||||
# DB에서 종목이 없을 경우 아래 로직 실행
|
||||
try:
|
||||
markets = self._api.get_tickers()
|
||||
|
||||
# set exchange min volume
|
||||
self._standard_price = self._get_standard_trade_price(markets)
|
||||
|
||||
filtered_markets = self._return_list_filted_markets(markets)
|
||||
|
||||
# 거래소 기본정보 DB 저장 (거래 기준가, api_url 등)
|
||||
self._add_exchange_info_to_db(self._finance, self._name, self._standard_price, self._api_url)
|
||||
|
||||
# 종목 리스트 DB 저장
|
||||
self._save_market_list_to_db(filtered_markets)
|
||||
|
||||
return filtered_markets
|
||||
|
||||
except Exception as e:
|
||||
logging.error(e)
|
||||
raise Exception(e) # for dev
|
||||
|
||||
return markets
|
||||
|
||||
def get_symbol_for_chart_view(self, market):
|
||||
s = market.split('-')
|
||||
return s[1] + '_' + s[0]
|
||||
|
||||
def save_current_min_data(self, market):
|
||||
time_type = 'min'
|
||||
data = self.get_history_data(self.get_symbol_for_chart_view(market), 'minute')[:-1]
|
||||
self._save_to_db_from_collectors_dataframe(self.finance, self._name, market, data, time_type)
|
||||
|
||||
def save_current_hour_data(self, market):
|
||||
time_type = 'hour'
|
||||
data = self.get_history_data(self.get_symbol_for_chart_view(market), 'hour')[:-1]
|
||||
self._save_to_db_from_collectors_dataframe(self.finance, self._name, market, data, time_type)
|
||||
|
||||
def save_current_day_data(self, market):
|
||||
time_type = 'day'
|
||||
data = self.get_history_data(self.get_symbol_for_chart_view(market), 'day')[:-1]
|
||||
self._save_to_db_from_collectors_dataframe(self.finance, self._name, market, data, time_type)
|
||||
|
||||
def _swap_item_name(self, symbol):
|
||||
symbol = str(symbol).replace('-', '_')
|
||||
|
||||
if 'KRW' in symbol.split('_')[0]:
|
||||
s = symbol.split('_')
|
||||
r_symbol = '_'.join([s[1], s[0]])
|
||||
|
||||
return r_symbol
|
||||
|
||||
return symbol
|
||||
|
||||
def get_history_data(self, symbol="BTC_KRW", interval="day", rm_last=True):
|
||||
symbol = self._swap_item_name(symbol)
|
||||
|
||||
int2type = {
|
||||
# "hour12": "12H",
|
||||
# "hour6": "06H",
|
||||
# "hour4": "01H", # custom hour
|
||||
# "hour": "01H",
|
||||
|
||||
"day": "24H",
|
||||
"hour12": "01H",
|
||||
"hour6": "01H",
|
||||
"hour4": "01H", # custom hour
|
||||
"hour": "01H",
|
||||
"minute30": "30M",
|
||||
"minute10": "10M",
|
||||
"minute5": "05M",
|
||||
"minute3": "03M",
|
||||
"minute": "01M",
|
||||
}
|
||||
|
||||
url = "https://m.bithumb.com/trade/chart/{}".format(symbol)
|
||||
resp = requests.get(url)
|
||||
html = resp.text
|
||||
|
||||
# parsing coin type
|
||||
string = html.split("COIN = ")[1].split(";")[0]
|
||||
coin = json.loads(string)
|
||||
tk2ct = {v['symbol_name']: k for k, v in coin['C0100'].items()}
|
||||
|
||||
# parsing xcoin name
|
||||
selector = "#barcodeForm > input[name=csrf_xcoin_name]"
|
||||
soup = BeautifulSoup(html, 'html5lib')
|
||||
xcoin_name = soup.select(selector)[0]['value']
|
||||
|
||||
url = "https://m.bithumb.com/trade_history/chart_data"
|
||||
headers = {
|
||||
"cookie": 'csrf_xcoin_name={}'.format(xcoin_name),
|
||||
"x-requested-with": "XMLHttpRequest"
|
||||
}
|
||||
|
||||
symbol = symbol.replace("KRW", "")
|
||||
symbol = symbol.replace("_", "")
|
||||
|
||||
data = {
|
||||
"coinType": tk2ct[symbol],
|
||||
"crncCd": "C0100",
|
||||
"tickType": int2type[interval],
|
||||
"csrf_xcoin_name": xcoin_name
|
||||
}
|
||||
|
||||
resp = requests.post(url, data=data, headers=headers).json()
|
||||
for x in resp['data']:
|
||||
x[0] = datetime.datetime.fromtimestamp(x[0] / 1000)
|
||||
|
||||
columns = [symbol, 'open', 'close', 'high', 'low', 'volume']
|
||||
df = DataFrame(resp['data'], columns=columns)
|
||||
df = df.set_index(symbol)
|
||||
|
||||
if not df.empty:
|
||||
# Remove last candle
|
||||
if rm_last:
|
||||
df = df[:-1]
|
||||
|
||||
self.data_columns_init(df)
|
||||
|
||||
if interval == 'hour4' or interval == 'hour6' or interval == 'hour12':
|
||||
return self.packaging_data_per_time_unit(df, interval)
|
||||
|
||||
return df.astype(float)
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_current_price(self, symbol):
|
||||
symbol = self._swap_item_name(symbol)
|
||||
return self._api.get_current_price(symbol)
|
||||
|
||||
def get_last_price_from_orderbook(self, symbol, position):
|
||||
symbol = self._swap_item_name(symbol)
|
||||
res = self._api.get_orderbook(symbol)
|
||||
|
||||
if res:
|
||||
if position == 'long':
|
||||
return int(res['bids'][0]['price'])
|
||||
|
||||
elif position == 'short':
|
||||
return int(res['asks'][0]['price'])
|
||||
|
||||
return None, None
|
||||
|
||||
def get_trading_fee(self):
|
||||
return self._api.get_trading_fee()
|
||||
|
||||
def get_all_balance(self, symbol='ALL'):
|
||||
return self._api.get_balance(symbol)
|
||||
|
||||
def get_now_amount(self, symbol):
|
||||
s = symbol.replace('KRW', '').replace('-', '')
|
||||
res = {}
|
||||
|
||||
c, u, k, u = self.get_all_balance(s)
|
||||
|
||||
res[symbol] = '{:.8f}'.format(c)
|
||||
res['KRW'] = int(k)-int(u)
|
||||
|
||||
return res
|
||||
|
||||
# https://github.com/sharebook-kr/pybithumb
|
||||
# https://wikidocs.net/21887
|
||||
def order_long(self, symbol, order=None, cnt=0):
|
||||
# for test
|
||||
# return True, {'symbol': symbol, 'target_price': 8723000, 'amount': 1, 'seeds': 20000}
|
||||
|
||||
# 이전 주문 취소
|
||||
self.order_cancel(order)
|
||||
|
||||
target_price = self.get_last_price_from_orderbook(symbol, 'long')
|
||||
s = symbol.replace('KRW', '').replace('-', '')
|
||||
seeds = self.get_now_amount(symbol)['KRW']
|
||||
amount = float('{:.4f}'.format(seeds/target_price, 4))
|
||||
|
||||
if cnt > 100:
|
||||
# 시장가 매수
|
||||
self._api.buy_market_order(s, amount)
|
||||
return True, {'symbol': symbol, 'target_price': target_price, 'amount': amount, 'seeds': seeds}
|
||||
else:
|
||||
# 지정가 매수
|
||||
order = self._api.buy_limit_order(s, target_price, amount)
|
||||
|
||||
if order is None or 'message' in order:
|
||||
print('주문 오류 -', order['message'])
|
||||
return False
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
if self._api.get_outstanding_order(order) is not None:
|
||||
cnt += 1
|
||||
return self.order_long(symbol, order, cnt)
|
||||
|
||||
return True, {'symbol': symbol, 'target_price': target_price, 'amount': amount, 'seeds': seeds}
|
||||
|
||||
def order_short(self, symbol, order=None, cnt=0):
|
||||
# for test
|
||||
# return True, {'symbol': symbol, 'target_price': 8952000, 'amount': 1, 'seeds': 22000}
|
||||
|
||||
# 이전 주문 취소
|
||||
self.order_cancel(order)
|
||||
|
||||
target_price = self.get_last_price_from_orderbook(symbol, 'short')
|
||||
s = symbol.replace('KRW', '').replace('-', '')
|
||||
amount = float(format(self._api.get_balance(s)[0], ".4f"))
|
||||
|
||||
if not amount > 0:
|
||||
return True
|
||||
|
||||
if cnt > 100:
|
||||
# 시장가 매도
|
||||
self._api.sell_market_order(s, amount)
|
||||
seeds = self.get_now_amount(symbol)['KRW']
|
||||
|
||||
return True, {'symbol': symbol, 'target_price': target_price, 'amount': amount, 'seeds': seeds}
|
||||
else:
|
||||
# 지정가 매도
|
||||
order = self._api.sell_limit_order(s, target_price, amount)
|
||||
|
||||
if order is None or 'message' in order:
|
||||
print('주문 오류 -', order['message'])
|
||||
return False
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
if self._api.get_outstanding_order(order) is not None:
|
||||
cnt += 1
|
||||
return self.order_short(symbol, order, cnt)
|
||||
|
||||
seeds = self.get_now_amount(symbol)['KRW']
|
||||
return True, {'symbol': symbol, 'target_price': target_price, 'amount': amount, 'seeds': seeds}
|
||||
|
||||
def order_cancel(self, order=None):
|
||||
if order is not None:
|
||||
return self._api.cancel_order(order)
|
||||
|
||||
return False
|
||||
|
||||
# 포지션 종료 => 매도
|
||||
def close_position(self, symbol):
|
||||
return self.order_short(symbol)
|
||||
Reference in New Issue
Block a user