# -*- 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)