def __init__(self, init_symbols=False, do_fill_history=False): super().__init__(name='huobi websocket', daemon=True) self.hb = HBWebsocket() self.api = HBRestAPI() self.init_symbols = init_symbols self.logger = logging.getLogger(self.__class__.__name__) self.do_fill_history = do_fill_history
def __init__(self, stg_run_id, run_mode_params: dict): super().__init__(stg_run_id, run_mode_params) self.trader_api = HBRestAPI() self.currency_balance_dic = {} self.currency_balance_last_get_datetime = None self.symbol_currency_dic = None self._datetime_last_rtn_trade_dic = {} self._datetime_last_update_position_dic = {}
def __init__(self, init_symbols=False, do_fill_history=False): super().__init__(name='huobi websocket', daemon=True) self.hb = HBWebsocket() self.api = HBRestAPI() self.init_symbols = init_symbols self.logger = logging.getLogger(self.__class__.__name__) self.do_fill_history = do_fill_history
class MDFeeder(Thread): """接受订阅数据想redis发送数据""" def __init__(self, init_symbols=False, do_fill_history=False): super().__init__(name='huobi websocket', daemon=True) self.hb = HBWebsocket() self.api = HBRestAPI() self.init_symbols = init_symbols self.logger = logging.getLogger(self.__class__.__name__) self.do_fill_history = do_fill_history self.heart_beat = HeartBeatHandler() # 加载数据库表模型(已经废弃,因为需要支持多周期到不同的数据库表) # self.table_name = MDMin1Temp.__tablename__ # self.md_orm_table = MDMin1Temp.__table__ # self.md_orm_table_insert = self.md_orm_table.insert(on_duplicate_key_update=True) def init(self, periods=['1min', '60min', '1day'], symbol_partition_set={'main', 'innovation', 'bifurcation'}): """ 初始化,订阅行情 默认1分钟、1小时、1日 包含 {'main', 'innovation', 'bifurcation'} 全部币种 :param periods: :param symbol_partition_set: :return: """ if self.init_symbols: # 获取有效的交易对信息保存(更新)数据库 ret = self.api.get_symbols() key_mapping = { 'base-currency': 'base_currency', 'quote-currency': 'quote_currency', 'price-precision': 'price_precision', 'amount-precision': 'amount_precision', 'symbol-partition': 'symbol_partition', } # 获取支持的交易对 data_dic_list = [] for d in ret['data']: d['market'] = Config.MARKET_NAME # 'huobi' data_dic_list.append( {key_mapping.setdefault(k, k): v for k, v in d.items()}) with with_db_session(engine_md) as session: session.execute( SymbolPair.__table__.insert(on_duplicate_key_update=True), data_dic_list) available_pairs = [ d['base_currency'] + d['quote_currency'] for d in data_dic_list if d['symbol_partition'] in symbol_partition_set ] # 通过 on_open 方式进行订阅总是无法成功 for pair, period in itertools.product(available_pairs, periods): self.hb.sub_dict[pair + period] = { 'id': '', 'topic': f'market.{pair}.kline.{period}' } else: self.hb.sub_dict['ethbtc60'] = { 'id': '', 'topic': 'market.ethbtc.kline.60min' } # self.hb.sub_dict['ethusdt'] = {'id': '', 'topic': 'market.ethusdt.kline.1min'} self.hb.sub_dict['ethusdt60'] = { 'id': '', 'topic': 'market.ethusdt.kline.60min' } # handler = SimpleHandler('simple handler') if Config.ENABLE_DB_HANDLER: # Tick 数据插入 handler = DBHandler(period='1min', db_model=MDTick, save_tick=True) self.hb.register_handler(handler) time.sleep(1) # 其他周期数据插入 for period in periods: save_tick = False if period == '1min': db_model = MDMin1 elif period == '60min': db_model = MDMin60 # save_tick = True elif period == '1day': db_model = MDMinDaily else: self.logger.warning(f'{period} 不是有效的周期') continue handler = DBHandler(period=period, db_model=db_model, save_tick=save_tick) self.hb.register_handler(handler) logger.info('注册 %s 处理句柄', handler.name) time.sleep(1) # 数据redis广播 if Config.ENABLE_REDIS_HANDLER and check_redis(): handler = PublishHandler(market=Config.MARKET_NAME) self.hb.register_handler(handler) logger.info('注册 %s 处理句柄', handler.name) # Heart Beat self.hb.register_handler(self.heart_beat) logger.info('注册 %s 处理句柄', self.heart_beat.name) server_datetime = self.get_server_datetime() logger.info("api.服务期时间 %s 与本地时间差: %f 秒", server_datetime, (datetime.now() - server_datetime).total_seconds()) self.check_state() def stop(self): self.hb.stop() self.logger.info('结束订阅') @property def is_working(self): return (datetime.now() - self.heart_beat.time).total_seconds() < 3600 def check_state(self): self.check_accounts() data = self.get_balance() for bal in data: self.logger.info(f"{bal['currency']} : {bal['balance']}") def get_server_datetime(self): ret_data = self.api.get_timestamp() server_datetime = datetime.fromtimestamp(ret_data['data'] / 1000) return server_datetime def get_accounts(self): ret_data = self.api.get_accounts() account_info = [acc for acc in ret_data['data']] return account_info def check_accounts(self): account_info = self.get_accounts() is_ok = True for acc in account_info: if acc['state'] != 'working': self.logger.error(f'账户[%d] %s %s 状态异常:%s', acc['id'], acc['type'], acc['subtype'], acc['state']) is_ok = False return is_ok def get_balance(self, no_zero_only=True): ret_data = self.api.get_balance() acc_balance = ret_data['data']['list'] if no_zero_only: ret_acc_balance = [ balance for balance in acc_balance if balance['balance'] != '0' ] else: ret_acc_balance = acc_balance return ret_acc_balance # def get_orders_info(self, symbol, states='submitted'): # ret_data = self.api.get_orders_info(symbol=symbol, states=states) # return ret_data['data'] def run(self): self.hb.run() self.logger.info('启动') # 补充历史数据 if self.do_fill_history: self.logger.info('开始补充历史数据') self.fill_history() while self.is_working: time.sleep(5) def fill_history(self, periods=['1day', '1min', '60min']): for period in periods: if period == '1min': model_tot, model_tmp = MDMin1, MDMin1Temp elif period == '60min': model_tot, model_tmp = MDMin60, MDMin60Temp elif period == '1day': model_tot, model_tmp = MDMinDaily, MDMinDailyTemp else: self.logger.warning(f'{period} 不是有效的周期') self.fill_history_period(period, model_tot, model_tmp) def fill_history_period(self, period, model_tot, model_tmp: MDMin1Temp): """ 根据数据库中的支持 symbol 补充历史数据 :param period: :param model_tot: :param model_tmp: :return: """ with with_db_session(engine_md) as session: data = session.query(SymbolPair).filter( SymbolPair.market == Config.MARKET_NAME).all( ) # , SymbolPair.symbol_partition == 'main' pair_datetime_latest_dic = dict( session.query(model_tmp.symbol, func.max( model_tmp.ts_start)).filter( model_tmp.market == Config.MARKET_NAME).group_by( model_tmp.symbol).all()) # 循环获取每一个交易对的历史数据 for symbol_info in data: symbol = f'{symbol_info.base_currency}{symbol_info.quote_currency}' if symbol in pair_datetime_latest_dic: datetime_latest = pair_datetime_latest_dic[symbol] if period == '1min': second_of_period = 60 elif period == '60min': second_of_period = 60 * 60 elif period == '1day': second_of_period = 60 * 60 * 24 else: self.logger.warning(f'{period} 不是有效的周期') continue size = min([ 2000, int((datetime.now() - datetime_latest).seconds / second_of_period * 1.2) ]) else: size = 2000 if size <= 1: continue ret = self.get_kline(symbol, period, size=size) # for n in range(1, 3): # try: # ret = self.api.get_kline(symbol, period, size=size) # except ProxyError: # self.logger.exception('symbol:%s, period:%s, size=%d', symbol, period, size) # ret = None # time.sleep(5) # continue # break if ret is None: continue if ret['status'] == 'ok': data_list = ret['data'] data_dic_list = [] for data in data_list: ts_start = datetime.fromtimestamp(data.pop('id')) data['ts_start'] = ts_start data['market'] = Config.MARKET_NAME data['ts_curr'] = ts_start + timedelta( seconds=59) # , microseconds=999999 data['symbol'] = symbol data_dic_list.append(data) self._save_md(data_dic_list, symbol, model_tot, model_tmp) else: self.logger.error(ret) # 过于频繁方位可能导致链接失败 time.sleep(5) # 已经包含在 try_n_times 里面 @try_n_times(5, sleep_time=5, logger=logger) def get_kline(self, symbol, period, size): ret = self.api.get_kline(symbol, period, size=size) return ret def _save_md(self, data_dic_list, symbol, model_tot: MDMin1, model_tmp: MDMin1Temp): """ 保存md数据到数据库及文件 :param data_dic_list: :param symbol: :param model_tot: :param model_tmp: :return: """ if data_dic_list is None or len(data_dic_list) == 0: self.logger.warning("data_dic_list 为空") return md_count = len(data_dic_list) # 保存到数据库 with with_db_session(engine_md) as session: try: # session.execute(self.md_orm_table_insert, data_dic_list) session.execute( model_tmp.__table__.insert(on_duplicate_key_update=True), data_dic_list) self.logger.info('%d 条 %s 历史数据 -> %s 完成', md_count, symbol, model_tmp.__tablename__) sql_str = f"""insert into {model_tot.__tablename__} select * from {model_tmp.__tablename__} where market=:market and symbol=:symbol ON DUPLICATE KEY UPDATE open=VALUES(open), high=VALUES(high), low=VALUES(low), close=VALUES(close) , amount=VALUES(amount), vol=VALUES(vol), count=VALUES(count)""" session.execute(sql_str, params={ "symbol": symbol, "market": Config.MARKET_NAME }) datetime_latest = session.query( func.max( model_tmp.ts_start).label('ts_start_latest')).filter( model_tmp.symbol == symbol, model_tmp.market == Config.MARKET_NAME).scalar() # issue: # https://stackoverflow.com/questions/9882358/how-to-delete-rows-from-a-table-using-an-sqlalchemy-query-without-orm delete_count = session.query(model_tmp).filter( model_tmp.market == Config.MARKET_NAME, model_tmp.symbol == symbol, model_tmp.ts_start < datetime_latest).delete() self.logger.debug('%d 条 %s 历史数据被清理,最新数据日期 %s', delete_count, symbol, datetime_latest) session.commit() except: self.logger.exception('%d 条 %s 数据-> %s 失败', md_count, symbol, model_tot.__tablename__)
class MDFeeder(Thread): """接受订阅数据想redis发送数据""" def __init__(self, init_symbols=False, do_fill_history=False): super().__init__(name='huobi websocket', daemon=True) self.hb = HBWebsocket() self.api = HBRestAPI() self.init_symbols = init_symbols self.logger = logging.getLogger(self.__class__.__name__) self.do_fill_history = do_fill_history # 加载数据库表模型(已经废弃,因为需要支持多周期到不同的数据库表) # self.table_name = MDMin1Temp.__tablename__ # self.md_orm_table = MDMin1Temp.__table__ # self.md_orm_table_insert = self.md_orm_table.insert(on_duplicate_key_update=True) def init(self, periods=['1min', '60min', '1day'], symbol_partition_set={'main', 'innovation', 'bifurcation'}): """ 初始化,订阅行情 默认1分钟、1小时、1日 包含 {'main', 'innovation', 'bifurcation'} 全部币种 :param periods: :param symbol_partition_set: :return: """ if self.init_symbols: # 获取有效的交易对信息保存(更新)数据库 ret = self.api.get_symbols() key_mapping = { 'base-currency': 'base_currency', 'quote-currency': 'quote_currency', 'price-precision': 'price_precision', 'amount-precision': 'amount_precision', 'symbol-partition': 'symbol_partition', } # 获取支持的交易对 data_dic_list = [] for d in ret['data']: d['market'] = 'huobi' data_dic_list.append({key_mapping.setdefault(k, k): v for k, v in d.items()}) with with_db_session(engine_md) as session: session.execute(SymbolPair.__table__.insert(on_duplicate_key_update=True), data_dic_list) available_pairs = [d['base_currency'] + d['quote_currency'] for d in data_dic_list if d['symbol_partition'] in symbol_partition_set] # 通过 on_open 方式进行订阅总是无法成功 for pair, period in itertools.product(available_pairs, periods): self.hb.sub_dict[pair+period] = {'id': '', 'topic': f'market.{pair}.kline.{period}'} else: self.hb.sub_dict['ethbtc60'] = {'id': '', 'topic': 'market.ethbtc.kline.60min'} # self.hb.sub_dict['ethusdt'] = {'id': '', 'topic': 'market.ethusdt.kline.1min'} self.hb.sub_dict['ethusdt60'] = {'id': '', 'topic': 'market.ethusdt.kline.60min'} # handler = SimpleHandler('simple handler') # Tick 数据插入 handler = DBHandler(period='1min', db_model=MDTick, save_tick=True) self.hb.register_handler(handler) time.sleep(1) # 其他周期数据插入 for period in periods: save_tick = False if period == '1min': db_model = MDMin1 elif period == '60min': db_model = MDMin60 # save_tick = True elif period == '1day': db_model = MDMinDaily else: self.logger.warning(f'{period} 不是有效的周期') continue handler = DBHandler(period=period, db_model=db_model, save_tick=save_tick) self.hb.register_handler(handler) time.sleep(1) # 数据redis广播 handler = PublishHandler(market=Config.MARKET_NAME) self.hb.register_handler(handler) server_datetime = self.get_server_datetime() logger.info("api.服务期时间 %s 与本地时间差: %f 秒", server_datetime, (datetime.now() - server_datetime).total_seconds()) self.check_state() def check_state(self): self.check_accounts() data = self.get_balance() for bal in data: self.logger.info(f"{bal['currency']} : {bal['balance']}") def get_server_datetime(self): ret_data = self.api.get_timestamp() server_datetime = datetime.fromtimestamp(ret_data['data']/1000) return server_datetime def get_accounts(self): ret_data = self.api.get_accounts() account_info = [acc for acc in ret_data['data']] return account_info def check_accounts(self): account_info = self.get_accounts() is_ok = True for acc in account_info: if acc['state'] != 'working': self.logger.error(f'账户[%d] %s %s 状态异常:%s', acc['id'], acc['type'], acc['subtype'], acc['state']) is_ok = False return is_ok def get_balance(self, no_zero_only=True): ret_data = self.api.get_balance() acc_balance = ret_data['data']['list'] if no_zero_only: ret_acc_balance = [balance for balance in acc_balance if balance['balance'] != '0'] else: ret_acc_balance = acc_balance return ret_acc_balance def get_orders_info(self, symbol, states='submitted'): ret_data = self.api.get_orders_info(symbol=symbol, states=states) return ret_data['data'] def run(self): self.hb.run() self.logger.info('启动') # 补充历史数据 if self.do_fill_history: self.logger.info('开始补充历史数据') self.fill_history() while True: time.sleep(1) def fill_history(self, periods=['1day', '1min', '60min']): for period in periods: if period == '1min': model_tot, model_tmp = MDMin1, MDMin1Temp elif period == '60min': model_tot, model_tmp = MDMin60, MDMin60Temp elif period == '1day': model_tot, model_tmp = MDMinDaily, MDMinDailyTemp else: self.logger.warning(f'{period} 不是有效的周期') self.fill_history_period(period, model_tot, model_tmp) def fill_history_period(self, period, model_tot, model_tmp: MDMin1Temp): """ 根据数据库中的支持 symbol 补充历史数据 :param period: :param model_tot: :param model_tmp: :return: """ with with_db_session(engine_md) as session: data = session.query(SymbolPair).filter( SymbolPair.market == Config.MARKET_NAME).all() # , SymbolPair.symbol_partition == 'main' pair_datetime_latest_dic = dict( session.query( model_tmp.symbol, func.max(model_tmp.ts_start) ).filter(model_tmp.market == Config.MARKET_NAME).group_by(model_tmp.symbol).all() ) # 循环获取每一个交易对的历史数据 for symbol_info in data: symbol = f'{symbol_info.base_currency}{symbol_info.quote_currency}' if symbol in pair_datetime_latest_dic: datetime_latest = pair_datetime_latest_dic[symbol] if period == '1min': second_of_period = 60 elif period == '60min': second_of_period = 60 * 60 elif period == '1day': second_of_period = 60 * 60 * 24 else: self.logger.warning(f'{period} 不是有效的周期') continue size = min([2000, int((datetime.now() - datetime_latest).seconds / second_of_period * 1.2)]) else: size = 2000 if size <= 1: continue ret = self.get_kline(symbol, period, size=size) # for n in range(1, 3): # try: # ret = self.api.get_kline(symbol, period, size=size) # except ProxyError: # self.logger.exception('symbol:%s, period:%s, size=%d', symbol, period, size) # ret = None # time.sleep(5) # continue # break if ret is None: continue if ret['status'] == 'ok': data_list = ret['data'] data_dic_list = [] for data in data_list: ts_start = datetime.fromtimestamp(data.pop('id')) data['ts_start'] = ts_start data['market'] = Config.MARKET_NAME data['ts_curr'] = ts_start + timedelta(seconds=59) # , microseconds=999999 data['symbol'] = symbol data_dic_list.append(data) self._save_md(data_dic_list, symbol, model_tot, model_tmp) else: self.logger.error(ret) # 过于频繁方位可能导致链接失败 time.sleep(5) # 已经包含在 try_n_times 里面 @try_n_times(5, sleep_time=5, logger=logger) def get_kline(self, symbol, period, size): ret = self.api.get_kline(symbol, period, size=size) return ret def _save_md(self, data_dic_list, symbol, model_tot: MDMin1, model_tmp: MDMin1Temp): """ 保存md数据到数据库及文件 :param data_dic_list: :param symbol: :param model_tot: :param model_tmp: :return: """ if data_dic_list is None or len(data_dic_list) == 0: self.logger.warning("data_dic_list 为空") return md_count = len(data_dic_list) # 保存到数据库 with with_db_session(engine_md) as session: try: # session.execute(self.md_orm_table_insert, data_dic_list) session.execute(model_tmp.__table__.insert(on_duplicate_key_update=True), data_dic_list) self.logger.info('%d 条 %s 历史数据保存到 %s 完成', md_count, symbol, model_tmp.__tablename__) sql_str = f"""insert into {model_tot.__tablename__} select * from {model_tmp.__tablename__} where market=:market and symbol=:symbol ON DUPLICATE KEY UPDATE open=VALUES(open), high=VALUES(high), low=VALUES(low), close=VALUES(close) , amount=VALUES(amount), vol=VALUES(vol), count=VALUES(count)""" session.execute(sql_str, params={"symbol": symbol, "market": Config.MARKET_NAME}) datetime_latest = session.query( func.max(model_tmp.ts_start).label('ts_start_latest') ).filter( model_tmp.symbol == symbol, model_tmp.market == Config.MARKET_NAME ).scalar() # issue: # https://stackoverflow.com/questions/9882358/how-to-delete-rows-from-a-table-using-an-sqlalchemy-query-without-orm delete_count = session.query(model_tmp).filter( model_tmp.market == Config.MARKET_NAME, model_tmp.symbol == symbol, model_tmp.ts_start < datetime_latest ).delete() self.logger.debug('%d 条 %s 历史数据被清理,最新数据日期 %s', delete_count, symbol, datetime_latest) session.commit() except: self.logger.exception('%d 条 %s 数据保存到 %s 失败', md_count, symbol, model_tot.__tablename__)
from huobitrade.handler import BaseHandler from huobitrade import setKey, logger from functools import partial import time # logger.setLevel('DEBUG') setKey('access_key', 'secret_key') class MyHandler(BaseHandler): def __init__(self, topic, *args, **kwargs): BaseHandler.__init__(self, 'just Thread name', topic) def handle(self, topic, msg): # 实现handle来处理websocket推送的msg print(topic) # 初始化restapi restapi = HBRestAPI(get_acc=True) print(restapi.get_accounts()) # 请求账户 # 构造异步请求 rep1 = restapi.get_timestamp(_async=True) rep2 = restapi.get_all_last_24h_kline(_async=True) result = restapi.async_request([rep1, rep2]) # 一起发起请求 for r in result: print(r) # 初始化多个ws auhb = HBWebsocket(auth=True) hb = HBWebsocket() hb2 = HBWebsocket()
class RealTimeTraderAgent(TraderAgent): """ 供调用实时交易接口使用 """ def __init__(self, stg_run_id, run_mode_params: dict): super().__init__(stg_run_id, run_mode_params) self.trader_api = HBRestAPI() self.currency_balance_dic = {} self.currency_balance_last_get_datetime = None self.symbol_currency_dic = None self.symbol_precision_dic = None self._datetime_last_rtn_trade_dic = {} self._datetime_last_update_position_dic = {} def connect(self): with with_db_session(engine_md) as session: data = session.query(SymbolPair).all() self.symbol_currency_dic = { f'{sym.base_currency}{sym.quote_currency}': sym.base_currency for sym in data } self.symbol_precision_dic = { f'{sym.base_currency}{sym.quote_currency}': (int(sym.price_precision), int(sym.amount_precision)) for sym in data } # @try_n_times(times=3, sleep_time=2, logger=logger) def open_long(self, symbol, price, vol): """买入多头""" price_precision, amount_precision = self.symbol_precision_dic[symbol] if isinstance(price, float): price = format(price, f'.{price_precision}f') if isinstance(vol, float): if vol < 10**-amount_precision: logger.warning('%s open_long 订单量 %f 太小,忽略', symbol, vol) return vol = format(floor(vol, amount_precision), f'.{amount_precision}f') self.trader_api.send_order(vol, symbol, OrderType.buy_limit.value, price) self._datetime_last_rtn_trade_dic[symbol] = datetime.now() def close_long(self, symbol, price, vol): """卖出多头""" price_precision, amount_precision = self.symbol_precision_dic[symbol] if isinstance(price, float): price = format(price, f'.{price_precision}f') if isinstance(vol, float): if vol < 10**-amount_precision: logger.warning('%s close_long 订单量 %f 太小,忽略', symbol, vol) return vol = format(floor(vol, amount_precision), f'.{amount_precision}f') self.trader_api.send_order(vol, symbol, OrderType.sell_limit.value, price) self._datetime_last_rtn_trade_dic[symbol] = datetime.now() def open_short(self, instrument_id, price, vol): # self.trader_api.open_short(instrument_id, price, vol) raise NotImplementedError() def close_short(self, instrument_id, price, vol): # self.trader_api.close_short(instrument_id, price, vol) raise NotImplementedError() def get_position(self, instrument_id, force_refresh=False) -> dict: """ instrument_id(相当于 symbol ) symbol ethusdt, btcusdt currency eth, btc :param instrument_id: :param force_refresh: :return: """ symbol = instrument_id currency = self.get_currency(symbol) # currency = instrument_id # self.logger.debug('symbol:%s force_refresh=%s', symbol, force_refresh) position_date_inv_pos_dic = self.get_balance( currency=currency, force_refresh=force_refresh) return position_date_inv_pos_dic def get_currency(self, symbol): """ 根据 symbol 找到对应的 currency symbol: ethusdt, btcusdt currency: eth, btc :param symbol: :return: """ return self.symbol_currency_dic[symbol] def get_balance(self, non_zero_only=False, trade_type_only=True, currency=None, force_refresh=False): """ 调用接口 查询 各个币种仓位 :param non_zero_only: 只保留非零币种 :param trade_type_only: 只保留 trade 类型币种,frozen 类型的不保存 :param currency: 只返回制定币种 usdt eth 等 :param force_refresh: 强制刷新,默认没30秒允许重新查询一次 :return: {'usdt': {<PositionDateType.History: 2>: {'currency': 'usdt', 'type': 'trade', 'balance': 144.09238}}} """ if force_refresh or self.currency_balance_last_get_datetime is None or \ self.currency_balance_last_get_datetime < datetime.now() - timedelta(seconds=30): ret_data = self.trader_api.get_balance() acc_balance = ret_data['data']['list'] self.logger.debug('更新持仓数据: %d 条', len(acc_balance)) acc_balance_new_dic = defaultdict(dict) for balance_dic in acc_balance: currency_curr = balance_dic['currency'] self._datetime_last_update_position_dic[ currency_curr] = datetime.now() if non_zero_only and balance_dic['balance'] == '0': continue if trade_type_only and balance_dic['type'] != 'trade': continue balance_dic['balance'] = float(balance_dic['balance']) # self.logger.debug(balance_dic) if PositionDateType.History in acc_balance_new_dic[ currency_curr]: balance_dic_old = acc_balance_new_dic[currency_curr][ PositionDateType.History] balance_dic_old['balance'] += balance_dic['balance'] # TODO: 日后可以考虑将 PositionDateType.History 替换为 type acc_balance_new_dic[currency_curr][ PositionDateType.History] = balance_dic else: acc_balance_new_dic[currency_curr] = { PositionDateType.History: balance_dic } self.currency_balance_dic = acc_balance_new_dic self.currency_balance_last_get_datetime = datetime.now() if currency is not None: if currency in self.currency_balance_dic: ret_data = self.currency_balance_dic[currency] # for position_date_type, data in self.currency_balance_dic[currency].items(): # if data['currency'] == currency: # ret_data = data # break else: ret_data = None else: ret_data = self.currency_balance_dic return ret_data @property def datetime_last_update_position(self) -> datetime: return self.currency_balance_last_get_datetime @property def datetime_last_rtn_trade_dic(self) -> dict: return self._datetime_last_rtn_trade_dic @property def datetime_last_update_position_dic(self) -> dict: return self._datetime_last_update_position_dic @property def datetime_last_send_order_dic(self) -> dict: raise NotImplementedError() def get_order(self, instrument_id, states='submitted') -> list: """ :param instrument_id: :param states: :return: 格式如下: [{'id': 603164274, 'symbol': 'ethusdt', 'account-id': 909325, 'amount': '4.134700000000000000', 'price': '983.150000000000000000', 'created-at': 1515166787246, 'type': 'buy-limit', 'field-amount': '4.134700000000000000', 'field-cash-amount': '4065.030305000000000000', 'field-fees': '0.008269400000000000', 'finished-at': 1515166795669, 'source': 'web', 'state': 'filled', 'canceled-at': 0}, ... ] """ symbol = instrument_id ret_data = self.trader_api.get_orders_info(symbol=symbol, states=states) return ret_data['data'] def cancel_order(self, instrument_id): symbol = instrument_id order_list = self.get_order(symbol) order_id_list = [data['id'] for data in order_list] return self.trader_api.batchcancel_order(order_id_list) def release(self): pass
buy_price = ... # 买入价 api.send_order(api.acc_id, buy_amount, symbol, 'buy-limit', buy_price) if sell_cond: sell_amount = ... sell_price = ... api.send_order(api.acc_id, sell_amount, symbol, 'sell-limit', sell_price) if __name__ == '__main__': from huobitrade.service import HBWebsocket, HBRestAPI from huobitrade import setUrl, setKey setKey('', '') # setUrl('https://api.huobi.pro', 'https://api.huobi.pro') ws = HBWebsocket('api.huobi.br.com') # 生产环境请不要用api.huobi.br.com api = HBRestAPI( get_acc=True) # get_acc为true,初始化时候会获取acount_id中的第一个id并赋值给acc_id属性 @ws.after_open # 连接成功后会调用此函数,一般在这个位置进行初始化订阅 def sub_depth(): ws.sub_kline('dcreth', '1min') ws.run() demo_handler = DemoHandler('dcreth') ws.register_handler(demo_handler)
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2018/6/8 0008 13:47 # @Author : Hadrianl # @File : example.py # @Contact : [email protected] from huobitrade.service import HBWebsocket, HBRestAPI from huobitrade.handler import DBHandler from huobitrade import setKey import time setKey('access_key', 'secret_key') hb = HBWebsocket() hb.run() time.sleep(1) # run之后连接需要一丢丢时间,sleep一下再订阅 hb.sub_kline('ethbtc', '1min') handler = DBHandler() hb.register_handler(handler) @hb.register_handle_func('market.ethbtc.kline.1min') def handle(msg): print('handle:', msg) api = HBRestAPI() print(api.get_timestamp())