def QA_fetch_get_future_transaction_realtime(code, ip=None, port=None): '期货历史成交分笔' ip, port = get_extensionmarket_ip(ip, port) apix = TdxExHq_API() global extension_market_list extension_market_list = QA_fetch_get_extensionmarket_list( ) if extension_market_list is None else extension_market_list code_market = extension_market_list.query('code=="{}"'.format(code)) with apix.connect(ip, port): data = pd.DataFrame() data = pd.concat([apix.to_df(apix.get_transaction_data( int(code_market.market), code, (30 - i) * 1800)) for i in range(31)], axis=0) return data.assign(datetime=pd.to_datetime(data['date'])).assign(date=lambda x: str(x)[0:10])\ .assign(code=str(code)).assign(order=range(len(data.index))).set_index('datetime', drop=False, inplace=False)
def QA_fetch_get_future_transaction_realtime(code,ip=None, port=None): '期货历史成交分笔' global best_ip if ip is None and port is None and best_ip['future']['ip'] is None and best_ip['future']['port'] is None: best_ip = select_best_ip() ip = best_ip['future']['ip'] port = best_ip['future']['port'] elif ip is None and port is None and best_ip['future']['ip'] is not None and best_ip['future']['port'] is not None: ip = best_ip['future']['ip'] port = best_ip['future']['port'] else: pass apix = TdxExHq_API() global extension_market_info extension_market_info = QA_fetch_get_future_list( ) if extension_market_info is None else extension_market_info code_market = extension_market_info.query('code=="{}"'.format(code)) with apix.connect(ip, port): data = pd.DataFrame() data = pd.concat([apix.to_df(apix.get_transaction_data( int(code_market.market), code, (30 - i) * 1800)) for i in range(31)], axis=0) return data.assign(datetime=pd.to_datetime(data['date'])).assign(date=lambda x: str(x)[0:10])\ .assign(code=str(code)).assign(order=range(len(data.index))).set_index('datetime', drop=False, inplace=False)
def test_all_functions(): symbol_params = [[47, "IF1709"], [8, "10000889"], [31, "00020"], [47, "IFL0"], [31, "00700"]] api = TdxExHq_API(auto_retry=True) try: with api.connect('121.14.110.210', 7727, time_out=30): log.info("获取市场代码") data = api.get_markets() assert data is not None assert type(data) is list assert len(data) > 0 log.info("查询市场中商品数量") data = api.get_instrument_count() assert data is not None assert data > 0 log.info("查询五档行情") for params in symbol_params: data = api.get_instrument_quote(*params) print(data) assert data is not None assert type(data) is list assert len(data) > 0 # log.info("查询分时行情") for params in symbol_params: data = api.get_minute_time_data(*params) assert data is not None assert type(data) is list assert len(data) >= 0 log.info("查询历史分时行情") for params in symbol_params: data = api.get_history_minute_time_data( params[0], params[1], 20170811) assert data is not None assert type(data) is list assert len(data) >= 0 log.info("查询分时成交") for params in symbol_params: data = api.get_transaction_data(*params) assert data is not None assert type(data) is list assert len(data) >= 0 log.info("查询历史分时成交") for params in symbol_params: data = api.get_history_transaction_data( params[0], params[1], 20170811) assert data is not None assert type(data) is list assert len(data) >= 0 log.info("查询k线") for params in symbol_params: data = api.get_instrument_bars(TDXParams.KLINE_TYPE_DAILY, params[0], params[1]) assert data is not None assert type(data) is list assert len(data) >= 0 log.info("查询代码列表") data = api.get_instrument_info(10000, 98) assert data is not None assert type(data) is list assert len(data) > 0 except socket.timeout as e: pass
class TdxFutureData(object): # ---------------------------------------------------------------------- def __init__(self, strategy=None, best_ip={}): """ 构造函数 :param strategy: 上层策略,主要用与使用write_log() """ self.api = None self.connection_status = False # 连接状态 self.best_ip = best_ip self.symbol_exchange_dict = {} # tdx合约与vn交易所的字典 self.symbol_market_dict = copy.copy( INIT_TDX_MARKET_MAP) # tdx合约与tdx市场的字典 self.strategy = strategy self.future_contracts = get_future_contracts() def write_log(self, content): if self.strategy: self.strategy.write_log(content) else: print(content) def write_error(self, content): if self.strategy: self.strategy.write_log(content, level=ERROR) else: print(content) def connect(self, is_reconnect=False): """ 连接API :return: """ # 创建api连接对象实例 try: if self.api is None or not self.connection_status: self.write_log(u"开始连接通达信行情服务器") self.api = TdxExHq_API(heartbeat=True, auto_retry=True, raise_exception=True) # 选取最佳服务器 if is_reconnect or len(self.best_ip) == 0: self.best_ip = get_cache_json(TDX_FUTURE_CONFIG) last_datetime_str = self.best_ip.get("datetime", None) if last_datetime_str: try: last_datetime = datetime.strptime( last_datetime_str, "%Y-%m-%d %H:%M:%S") if (datetime.now() - last_datetime ).total_seconds() > 60 * 60 * 2: self.best_ip = {} except Exception as ex: self.best_ip = {} else: self.best_ip = {} if len(self.best_ip) == 0: self.best_ip = self.select_best_ip() self.api.connect(self.best_ip["ip"], self.best_ip["port"]) # 尝试获取市场合约统计 c = self.api.get_instrument_count() if c < 10: err_msg = u"该服务器IP {}/{}无响应".format( self.best_ip["ip"], self.best_ip["port"]) self.write_error(err_msg) else: self.write_log(u"创建tdx连接, IP: {}/{}".format( self.best_ip["ip"], self.best_ip["port"])) # print(u"创建tdx连接, IP: {}/{}".format(self.best_ip["ip"], self.best_ip["port"])) self.connection_status = True # if not is_reconnect: # 更新 symbol_exchange_dict , symbol_market_dict # self.qryInstrument() except Exception as ex: self.write_log(u"连接服务器tdx异常:{},{}".format(str(ex), traceback.format_exc())) return # ---------------------------------------------------------------------- def ping(self, ip, port=7709): """ ping行情服务器 :param ip: :param port: :param type_: :return: """ apix = TdxExHq_API() __time1 = datetime.now() try: with apix.connect(ip, port): if apix.get_instrument_count() > 10000: _timestamp = datetime.now() - __time1 self.write_log("服务器{ip}:{port},耗时:{_timestamp}") return _timestamp else: self.write_log("该服务器IP {ip}无响应") return timedelta(9, 9, 0) except Exception: self.write_error("tdx ping服务器,异常的响应{ip}") return timedelta(9, 9, 0) # ---------------------------------------------------------------------- def select_best_ip(self): """ 选择行情服务器 :return: """ self.write_log(u"选择通达信行情服务器") data_future = [self.ping(x["ip"], x["port"]) for x in TDX_FUTURE_HOSTS] best_future_ip = TDX_FUTURE_HOSTS[data_future.index(min(data_future))] self.write_log(u"选取 {}:{}".format(best_future_ip["ip"], best_future_ip["port"])) # print(u"选取 {}:{}".format(best_future_ip["ip"], best_future_ip["port"])) best_future_ip.update( {"datetime": datetime.now().strftime("%Y-%m-%d %H:%M:%S")}) save_cache_json(best_future_ip, TDX_FUTURE_CONFIG) return best_future_ip def _get_vn_exchange(self, symbol): """获取""" underlying_symbol = get_underlying_symbol(symbol).upper() info = self.future_contracts.get(underlying_symbol, None) if info: return Exchange(info.get("exchange")) else: market_id = get_tdx_marketid(symbol) return Tdx_Vn_Exchange_Map.get(str(market_id), Exchange.INE) def qry_instrument(self): """ 查询/更新合约信息 :return: """ # 取得所有的合约信息 num = self.api.get_instrument_count() if not isinstance(num, int): return all_contacts = sum([ self.api.get_instrument_info((int(num / 500) - i) * 500, 500) for i in range(int(num / 500) + 1) ], []) # [{"category":category,"market": int,"code":sting,"name":string,"desc":string},{}] # 对所有合约处理,更新字典 指数合约-tdx市场,指数合约-交易所 for tdx_contract in all_contacts: tdx_symbol = tdx_contract.get("code", None) if tdx_symbol is None: continue tdx_market_id = tdx_contract.get("market") if str(tdx_market_id) in Tdx_Vn_Exchange_Map: self.symbol_exchange_dict.update( {tdx_symbol: Tdx_Vn_Exchange_Map.get(str(tdx_market_id))}) self.symbol_market_dict.update({tdx_symbol: tdx_market_id}) # ---------------------------------------------------------------------- def get_bars(self, symbol, period, callback=None, bar_freq=1, start_dt=None, end_dt=None, return_bar=True): """ 返回k线数据 symbol:合约 period: 周期: 1min,3min,5min,15min,30min,1day,3day,1hour,2hour,4hour,6hour,12hour callback: 逐一bar去驱动回调函数, 只有 return_bar = True时才回调 bar_freq: 回调时的参数 start_dt: 取数据的开始时间 end_dt: 取数据的结束时间 return_bar: 返回 第二个数据内容,True:BarData, False:dict """ ret_bars = [] tdx_symbol = symbol.upper().replace("_", "") tdx_symbol = tdx_symbol.replace("99", "L9") underlying_symbol = get_underlying_symbol(symbol).upper() tdx_index_symbol = underlying_symbol + "L9" vn_exchange = self._get_vn_exchange(underlying_symbol) self.connect() if self.api is None: return False, ret_bars if period not in PERIOD_MAPPING.keys(): self.write_error(u"{} 周期{}不在下载清单中: {}".format( datetime.now(), period, list(PERIOD_MAPPING.keys()))) return False, ret_bars tdx_period = PERIOD_MAPPING.get(period) if start_dt is None: self.write_log(u"没有设置开始时间,缺省为10天前") qry_start_date = datetime.now() - timedelta(days=10) else: qry_start_date = start_dt if end_dt is None: self.write_log(u"没有设置结束时间,缺省为当日") end_date = datetime.combine(datetime.now() + timedelta(days=1), time(ALL_MARKET_END_HOUR, 0)) else: end_date = end_dt if qry_start_date > end_date: qry_start_date = end_date self.write_log("{}开始下载tdx:{} {}数据, {} to {}.".format( datetime.now(), tdx_symbol, period, qry_start_date, end_date)) # print("{}开始下载tdx:{} {}数据, {} to {}.".format(datetime.now(), tdx_symbol, tdx_period, last_date, end_date)) try: _start_date = end_date _bars = [] _pos = 0 while _start_date > qry_start_date: _res = self.api.get_instrument_bars( tdx_period, self.symbol_market_dict.get(tdx_index_symbol, 0), tdx_symbol, _pos, QSIZE) if _res is not None: _bars = _res + _bars _pos += QSIZE if _res is not None and len(_res) > 0: _start_date = _res[0]["datetime"] _start_date = datetime.strptime(_start_date, "%Y-%m-%d %H:%M") self.write_log(u"分段取数据开始:{}".format(_start_date)) else: break if len(_bars) == 0: self.write_error("{} Handling {}, len1={}..., continue".format( str(datetime.now()), tdx_symbol, len(_bars))) return False, ret_bars current_datetime = datetime.now() data = self.api.to_df(_bars) data = data.assign(datetime=to_datetime(data["datetime"])) data = data.assign(ticker=symbol) data["instrument_id"] = data["ticker"] data["symbol"] = symbol data = data.drop([ "year", "month", "day", "hour", "minute", "price", "amount", "ticker" ], errors="ignore", axis=1) data = data.rename(index=str, columns={ "position": "open_interest", "trade": "volume", }) if len(data) == 0: print("{} Handling {}, len2={}..., continue".format( str(datetime.now()), tdx_symbol, len(data))) return False, ret_bars data["total_turnover"] = data["volume"] * data["close"] data["limit_down"] = 0 data["limit_up"] = 999999 data["trading_day"] = data["datetime"] data["trading_day"] = data["trading_day"].apply( lambda x: (x.strftime("%Y-%m-%d"))) monday_ts = data["datetime"].dt.weekday == 0 # 星期一 night_ts1 = data["datetime"].dt.hour > ALL_MARKET_END_HOUR night_ts2 = data["datetime"].dt.hour < ALL_MARKET_BEGIN_HOUR data.loc[night_ts1, "datetime"] -= timedelta( days=1) # 所有日期的夜盘(21:00~24:00), 减一天 monday_ts1 = monday_ts & night_ts1 # 星期一的夜盘(21:00~24:00), 再减两天 data.loc[monday_ts1, "datetime"] -= timedelta(days=2) monday_ts2 = monday_ts & night_ts2 # 星期一的夜盘(00:00~04:00), 再减两天 data.loc[monday_ts2, "datetime"] -= timedelta(days=2) # data["datetime"] -= timedelta(minutes=1) # 直接给Strategy使用, RiceQuant格式, 不需要减1分钟 # data["dt_datetime"] = data["datetime"] data["date"] = data["datetime"].apply(lambda x: (x.strftime("%Y-%m-%d"))) data["time"] = data["datetime"].apply(lambda x: (x.strftime("%H:%M:%S"))) # data["datetime"] = data["datetime"].apply(lambda x: float(x.strftime("%Y%m%d%H%M%S"))) data = data.set_index("datetime", drop=False) if return_bar: self.write_log("dataframe => [bars]") for index, row in data.iterrows(): add_bar = BarData(gateway_name="tdx", symbol=symbol, exchange=vn_exchange, datetime=index) try: add_bar.date = row["date"] add_bar.time = row["time"] add_bar.trading_day = row["trading_day"] add_bar.open_price = float(row["open"]) add_bar.high_price = float(row["high"]) add_bar.low_price = float(row["low"]) add_bar.close_price = float(row["close"]) add_bar.volume = float(row["volume"]) add_bar.openInterest = float(row["open_interest"]) except Exception as ex: self.write_error( "error when convert bar:{},ex:{},t:{}".format( row, str(ex), traceback.format_exc())) # print("error when convert bar:{},ex:{},t:{}".format(row, str(ex), traceback.format_exc())) return False, ret_bars if start_dt is not None and index < start_dt: continue ret_bars.append(add_bar) if callback is not None: freq = bar_freq bar_is_completed = True if period != "1min" and index == data["datetime"][-1]: # 最后一个bar,可能是不完整的,强制修改 # - 5min修改后freq基本正确 # - 1day在VNPY合成时不关心已经收到多少Bar, 所以影响也不大 # - 但其它分钟周期因为不好精确到每个品种, 修改后的freq可能有错 if index > current_datetime: bar_is_completed = False # 根据秒数算的话,要+1,例如13:31,freq=31,第31根bar freq = NUM_MINUTE_MAPPING[period] - int( (index - current_datetime).total_seconds() / 60) callback(add_bar, bar_is_completed, freq) else: self.write_log("dataframe => [ dict ]") ret_bars = list(data.T.to_dict().values()) return True, ret_bars except Exception as ex: self.write_error("exception in get:{},{},{}".format( tdx_symbol, str(ex), traceback.format_exc())) # print("exception in get:{},{},{}".format(tdx_symbol,str(ex), traceback.format_exc())) self.write_log(u"重置连接") self.api = None self.connect(is_reconnect=True) return False, ret_bars def get_price(self, symbol): """获取最新价格""" tdx_symbol = symbol.upper().replace("_", "") short_symbol = get_underlying_symbol(tdx_symbol).upper() if tdx_symbol.endswith("99"): query_symbol = tdx_symbol.replace("99", "L9") else: query_symbol = get_full_symbol(tdx_symbol) if query_symbol != tdx_symbol: self.write_log("转换合约:{}=>{}".format(tdx_symbol, query_symbol)) tdx_index_symbol = short_symbol + "L9" self.connect() if self.api is None: return 0 market_id = self.symbol_market_dict.get(tdx_index_symbol, 0) _res = self.api.get_instrument_quote(market_id, query_symbol) if not isinstance(_res, list): return 0 if len(_res) == 0: return 0 return float(_res[0].get("price", 0)) def get_99_contracts(self): """ 获取指数合约 :return: dict list """ self.connect() result = self.api.get_instrument_quote_list(42, 3, 0, 100) return result def get_mi_contracts(self): """ 获取主力合约 :return: dict list """ self.connect() result = self.api.get_instrument_quote_list(60, 3, 0, 100) return result def get_contracts(self, exchange): self.connect() market_id = Vn_Tdx_Exchange_Map.get(exchange, None) if market_id is None: print(u"市场:{}配置不在Vn_Tdx_Exchange_Map:{}中,不能取市场下所有合约".format( exchange, Vn_Tdx_Exchange_Map)) return [] index = 0 count = 100 results = [] while (True): print(u"查询{}下:{}~{}个合约".format(exchange, index, index + count)) #print("helloabc",self.api.get_instrument_quote_list(47,"IF2003")) result = self.api.get_instrument_quote_list( int(market_id), 3, index, count) results.extend(result) index += count if len(result) < count: break return results def get_mi_contracts2(self): """ 获取主力合约""" self.connect() contracts = [] for exchange in Vn_Tdx_Exchange_Map.keys(): contracts.extend(self.get_mi_contracts_from_exchange(exchange)) return contracts def get_mi_contracts_from_exchange(self, exchange): contracts = self.get_contracts(exchange) if len(contracts) == 0: print(u"异常,未能获取{}下合约信息".format(exchange)) return [] mi_contracts = [] short_contract_dict = {} for contract in contracts: # 排除指数合约 code = contract.get("code") if code[-2:] in ["L9", "L8", "L0", "L1", "L2", "L3", "50"] or \ (exchange == Exchange.CFFEX and code[-3:] in ["300", "500"]): continue short_symbol = get_underlying_symbol(code).upper() contract_list = short_contract_dict.get(short_symbol, []) contract_list.append(contract) short_contract_dict.update({short_symbol: contract_list}) for k, v in short_contract_dict.items(): sorted_list = sorted(v, key=lambda c: c["ZongLiang"]) mi_contracts.append(sorted_list[-1]) return mi_contracts def get_markets(self): """ 获取市场代码 :return: """ self.connect() result = self.api.get_markets() return result def get_transaction_data(self, symbol): """获取当前交易日的历史成交记录""" ret_datas = [] max_data_size = sys.maxsize symbol = symbol.upper() if "99" in symbol: # 查询的是指数合约 symbol = symbol.replace("99", "L9") tdx_index_symbol = symbol else: # 查询的是普通合约 tdx_index_symbol = get_underlying_symbol(symbol).upper() + "L9" self.connect() q_size = QSIZE * 5 # 每秒 2个, 10小时 max_data_size = 1000000 self.write_log(u"开始下载{}当日分笔数据".format(symbol)) try: _datas = [] _pos = 0 while True: _res = self.api.get_transaction_data( market=self.symbol_market_dict.get(tdx_index_symbol, 0), code=symbol, start=_pos, count=q_size) if _res is not None: for d in _res: dt = d.pop("date") # 星期1~星期6 if dt.hour >= 20 and 1 < dt.isoweekday() <= 6: dt = dt - timedelta(days=1) elif dt.hour >= 20 and dt.isoweekday() == 1: # 星期一取得20点后数据 dt = dt - timedelta(days=3) elif dt.hour < 8 and dt.isoweekday() == 1: # 星期一取得8点前数据 dt = dt - timedelta(days=3) elif dt.hour >= 20 and dt.isoweekday() == 7: # 星期天取得20点后数据,肯定是星期五夜盘 dt = dt - timedelta(days=2) elif dt.isoweekday() == 7: # 星期日取得其他时间,必然是 星期六凌晨的数据 dt = dt - timedelta(days=1) d.update({"datetime": dt}) # 接口有bug,返回价格*1000,所以要除以1000 d.update({"price": d.get("price", 0) / 1000}) _datas = sorted(_res, key=lambda s: s["datetime"]) + _datas _pos += min(q_size, len(_res)) if _res is not None and len(_res) > 0: self.write_log(u"分段取分笔数据:{} ~{}, {}条,累计:{}条".format( _res[0]["datetime"], _res[-1]["datetime"], len(_res), _pos)) else: break if len(_datas) >= max_data_size: break if len(_datas) == 0: self.write_error(u"{}分笔成交数据获取为空") return True, _datas except Exception as ex: self.write_error( "exception in get_transaction_data:{},{},{}".format( symbol, str(ex), traceback.format_exc())) self.write_error(u"当前异常服务器信息:{}".format(self.best_ip)) self.write_log(u"重置连接") self.api = None self.connect(is_reconnect=True) return False, ret_datas def save_cache(self, cache_folder, cache_symbol, cache_date, data_list): """保存文件到缓存""" os.makedirs(cache_folder, exist_ok=True) if not os.path.exists(cache_folder): self.write_error("缓存目录不存在:{},不能保存".format(cache_folder)) return cache_folder_year_month = os.path.join(cache_folder, cache_date[:6]) os.makedirs(cache_folder_year_month, exist_ok=True) save_file = os.path.join(cache_folder_year_month, "{}_{}.pkz2".format(cache_symbol, cache_date)) try: with bz2.BZ2File(save_file, "wb") as f: pickle.dump(data_list, f) self.write_log(u"缓存成功:{}".format(save_file)) except Exception as ex: self.write_error(u"缓存写入异常:{}".format(str(ex))) def load_cache(self, cache_folder, cache_symbol, cache_date): """加载缓存数据""" if not os.path.exists(cache_folder): self.write_error("缓存目录:{}不存在,不能读取".format(cache_folder)) return None cache_folder_year_month = os.path.join(cache_folder, cache_date[:6]) if not os.path.exists(cache_folder_year_month): self.write_error("缓存目录:{}不存在,不能读取".format(cache_folder_year_month)) return None cache_file = os.path.join( cache_folder_year_month, "{}_{}.pkz2".format(cache_symbol, cache_date)) if not os.path.isfile(cache_file): self.write_error("缓存文件:{}不存在,不能读取".format(cache_file)) return None with bz2.BZ2File(cache_file, "rb") as f: data = pickle.load(f) return data return None def get_history_transaction_data(self, symbol, trading_date, cache_folder=None): """获取当某一交易日的历史成交记录""" ret_datas = [] # trading_date, 转换为数字类型得日期 if isinstance(trading_date, datetime): trading_date = trading_date.strftime("%Y%m%d") if isinstance(trading_date, str): trading_date = int(trading_date.replace("-", "")) self.connect() cache_symbol = symbol cache_date = str(trading_date) max_data_size = sys.maxsize symbol = symbol.upper() if "99" in symbol: # 查询的是指数合约 symbol = symbol.replace("99", "L9") tdx_index_symbol = symbol else: # 查询的是普通合约 tdx_index_symbol = get_underlying_symbol(symbol).upper() + "L9" q_size = QSIZE * 5 # 每秒 2个, 10小时 max_data_size = 1000000 # 优先从缓存加载 if cache_folder: buffer_data = self.load_cache(cache_folder, cache_symbol, cache_date) if buffer_data: self.write_log(u"使用缓存文件") return True, buffer_data self.write_log(u"开始下载{} 历史{}分笔数据".format(trading_date, symbol)) cur_trading_date = get_trading_date() if trading_date == int(cur_trading_date.replace("-", "")): return self.get_transaction_data(symbol) try: _datas = [] _pos = 0 while True: _res = self.api.get_history_transaction_data( market=self.symbol_market_dict.get(tdx_index_symbol, 0), date=trading_date, code=symbol, start=_pos, count=q_size) if _res is not None: for d in _res: dt = d.pop("date") # 星期1~星期6 if dt.hour >= 20 and 1 < dt.isoweekday() <= 6: dt = dt - timedelta(days=1) d.update({"datetime": dt}) elif dt.hour >= 20 and dt.isoweekday() == 1: # 星期一取得20点后数据 dt = dt - timedelta(days=3) d.update({"datetime": dt}) elif dt.hour < 8 and dt.isoweekday() == 1: # 星期一取得8点前数据 dt = dt - timedelta(days=3) d.update({"datetime": dt}) elif dt.hour >= 20 and dt.isoweekday() == 7: # 星期天取得20点后数据,肯定是星期五夜盘 dt = dt - timedelta(days=2) d.update({"datetime": dt}) elif dt.isoweekday() == 7: # 星期日取得其他时间,必然是 星期六凌晨的数据 dt = dt - timedelta(days=1) d.update({"datetime": dt}) else: d.update({"datetime": dt}) # 接口有bug,返回价格*1000,所以要除以1000 d.update({"price": d.get("price", 0) / 1000}) _datas = sorted(_res, key=lambda s: s["datetime"]) + _datas _pos += min(q_size, len(_res)) if _res is not None and len(_res) > 0: self.write_log(u"分段取分笔数据:{} ~{}, {}条,累计:{}条".format( _res[0]["datetime"], _res[-1]["datetime"], len(_res), _pos)) else: break if len(_datas) >= max_data_size: break if len(_datas) == 0: self.write_error(u"{}分笔成交数据获取为空".format(trading_date)) return False, _datas # 缓存文件 if cache_folder: self.save_cache(cache_folder, cache_symbol, cache_date, _datas) return True, _datas except Exception as ex: self.write_error( "exception in get_transaction_data:{},{},{}".format( symbol, str(ex), traceback.format_exc())) self.write_error(u"当前异常服务器信息:{}".format(self.best_ip)) self.write_log(u"重置连接") self.api = None self.connect(is_reconnect=True) return False, ret_datas def update_mi_contracts(self): # 连接通达信,获取主力合约 if not self.api: self.connect() mi_contract_quote_list = self.get_mi_contracts2() self.write_log(u"一共获取:{}个主力合约:{}".format( len(mi_contract_quote_list), [c.get("code") for c in mi_contract_quote_list])) should_save = False # 逐一更新主力合约数据 for mi_contract in mi_contract_quote_list: tdx_market_id = mi_contract.get("market") full_symbol = mi_contract.get("code") underlying_symbol = get_underlying_symbol(full_symbol).upper() if underlying_symbol in ["SC", "NR"]: vn_exchange = Exchange.INE else: vn_exchange = Tdx_Vn_Exchange_Map.get(str(tdx_market_id)) mi_symbol = get_real_symbol_by_exchange(full_symbol, vn_exchange) # 更新登记 短合约:真实主力合约 self.write_log("{},{},{},{},{}".format(tdx_market_id, full_symbol, underlying_symbol, mi_symbol, vn_exchange)) if underlying_symbol in self.future_contracts: info = self.future_contracts.get(underlying_symbol) if mi_symbol > info.get("mi_symbol"): self.write_log(u"主力合约变化:{} =>{}".format( info.get("mi_symbol"), mi_symbol)) info.update({ "mi_symbol": mi_symbol, "full_symbol": full_symbol }) self.future_contracts.update({underlying_symbol: info}) should_save = True else: # 添加到新合约中 # 这里缺少size和price_tick, margin_rate,当ctp_gateway启动时,会自动补充和修正完毕 info = { "underlying_symbol": underlying_symbol, "mi_symbol": mi_symbol, "full_symbol": full_symbol, "exchange": vn_exchange.value } self.write_log( u"新合约:{}, 需要待ctp连接后更新合约的size/price_tick/margin_rate". format(info)) self.future_contracts.update({underlying_symbol: info}) should_save = True if should_save: save_future_contracts(self.future_contracts)
class ExtQuotes(object): """扩展市场实时行情""" config = None client = None bestip = ('112.74.214.43', 7727) def __init__(self, **kwargs): try: default = settings.get('SERVER').get('EX')[0] self.bestip = config.get('BESTIP').get('EX', default) except ValueError: self.bestip = ('112.74.214.43', 7727) self.client = TdxExHq_API(**kwargs) def validate(self, market, symbol): if not market: if len(symbol.split('#')) > 1: market = symbol.split('#')[0] symbol = symbol.split('#')[1] if not market: raise ValueError('市场参数错误, 市场参数不能为空.') return int(market), symbol def markets(self): ''' 获取实时市场列表 :return: pd.dataFrame or None ''' with self.client.connect(*self.bestip) as client: result = client.get_markets() return to_data(result) def instrument(self, start=0, offset=100): ''' 查询代码列表 :param offset: :param start: :return: ''' with self.client.connect(*self.bestip): result = self.client.get_instrument_info(start=start, count=offset) return to_data(result) def instrument_count(self): ''' 市场商品数量 :return: ''' with self.client.connect(*self.bestip): result = self.client.get_instrument_count() return result def instruments(self): ''' 查询所有代码列表 :return: ''' with self.client.connect(*self.bestip): count = self.client.get_instrument_count() pages = math.ceil(count / 100) result = [] for page in tqdm(range(0, pages)): result += self.client.get_instrument_info( page * 100, (page + 1) * 100) return to_data(result) def quote(self, market='', symbol=''): ''' 查询五档行情 :param market: :param symbol: :return: ''' market, symbol = self.validate(market, symbol) with self.client.connect(*self.bestip): result = self.client.get_instrument_quote(market, symbol) return to_data(result) def minute(self, market='', symbol=''): ''' 查询分时行情 :param market: :param symbol: :return: ''' market, symbol = self.validate(market, symbol) with self.client.connect(*self.bestip): result = self.client.get_minute_time_data(market, symbol) return to_data(result) def minutes(self, market=None, symbol='', date=''): ''' 查询历史分时行情 :param market: :param symbol: :param date: :return: ''' market, symbol = self.validate(market, symbol) with self.client.connect(*self.bestip): result = self.client.get_history_minute_time_data( market, symbol, date) return to_data(result) def bars(self, frequency='', market='', symbol='', start='', offset=0): ''' 查询k线数据 参数: K线周期, 市场ID, 证券代码,起始位置, 数量 :param frequency: K线周期 :param market: 市场ID :param symbol: 证券代码 :param start: 起始位置 :param offset: 数量 :return: ''' market, symbol = self.validate(market, symbol) with self.client.connect(*self.bestip): result = self.client.get_instrument_bars(frequency=frequency, market=market, code=symbol, start=start, count=offset) return to_data(result) def transaction(self, market=None, symbol='', start=0, offset=1800): ''' 查询分笔成交 :param market: :param symbol: :param start: :param offset: :return: ''' market, symbol = self.validate(market, symbol) with self.client.connect(*self.bestip): result = self.client.get_transaction_data(market=market, code=symbol, start=start, count=offset) return to_data(result) def transactions(self, market=None, symbol='', date='', start=0, offset=1800): ''' 查询历史分笔成交 :param market: :param symbol: :param date: :param start: :param offset: :return: ''' market, symbol = self.validate(market, symbol) with self.client.connect(*self.bestip): result = self.client.get_history_transaction_data(market=market, code=symbol, date=date, start=start, count=offset) return to_data(result)
from pytdx.exhq import TdxExHq_API import ta api = TdxExHq_API() with api.connect('124.74.236.94', 7721): #查询市场中商品数量 marketsID = api.to_df(api.get_markets()) #查询五档行情 quote =api.to_df(api.get_instrument_quote(47, "IFL8")) #查询分时行情 markets_fenshi = api.to_df(api.get_minute_time_data(47, "IFL8")) #查询历史分时行情 historydata = api.to_df(api.get_history_minute_time_data(47, "IFL8", 20170811)) #查询k线数据 kdata = api.to_df(api.get_instrument_bars(0, 47, "IFL8", 0, 700)) #查询当前分笔成交 fenbi = api.to_df(api.get_transaction_data(47, "IFL8")) #查询历史分笔成交 fenbi_history = api.to_df(api.get_history_transaction_data(47, "IFL8", 20191204, start=1800)) #查询当天分笔成交 fenbi_chenjiao = api.to_df(api.get_history_transaction_data(47, "IFL8", 20191204,)) print (marketsID) print (quote) print (kdata) df = kdata df["rsi"] = ta.momentum.rsi(df.close,n=14,) df['stoch'] = ta.momentum.stoch(df.high,df.low,df.close,14)
else: #512 direction = 0 nature_name = '' result.append( OrderedDict([ ("date", date), ("hour", hour), ("minute", minute), ("second", second), ("price", price), ("volume", volume), ("zengcang", zengcang), ("nature", nature), ("nature_mark", nature // 10000), ("nature_value", nature % 10000), ("nature_name", nature_name), ("direction", direction), ])) return result if __name__ == "__main__": from pytdx.exhq import TdxExHq_API api = TdxExHq_API() with api.connect('121.14.110.210', 7727): print(api.to_df(api.get_transaction_data(47, 'IFL9'))) # print(api.to_df(api.get_transaction_data(31, "00020")))
class TdxFutureData(object): # ---------------------------------------------------------------------- def __init__(self, strategy, best_ip={}): """ 构造函数 :param strategy: 上层策略,主要用与使用write_log() """ self.api = None self.connection_status = False # 连接状态 self.best_ip = best_ip self.symbol_exchange_dict = {} # tdx合约与vn交易所的字典 self.symbol_market_dict = copy.copy( INIT_TDX_MARKET_MAP) # tdx合约与tdx市场的字典 self.strategy = strategy def write_log(self, content): if self.strategy: self.strategy.write_log(content) else: print(content) def write_error(self, content): if self.strategy: self.strategy.write_log(content, level=ERROR) else: print(content, file=sys.stderr) def connect(self, is_reconnect=False): """ 连接API :return: """ # 创建api连接对象实例 try: if self.api is None or not self.connection_status: self.write_log(u'开始连接通达信行情服务器') self.api = TdxExHq_API(heartbeat=True, auto_retry=True, raise_exception=True) # 选取最佳服务器 if is_reconnect or len(self.best_ip) == 0: self.best_ip = get_cache_ip() if len(self.best_ip) == 0: self.best_ip = self.select_best_ip() self.api.connect(self.best_ip['ip'], self.best_ip['port']) # 尝试获取市场合约统计 c = self.api.get_instrument_count() if c < 10: err_msg = u'该服务器IP {}/{}无响应'.format( self.best_ip['ip'], self.best_ip['port']) self.write_error(err_msg) else: self.write_log(u'创建tdx连接, IP: {}/{}'.format( self.best_ip['ip'], self.best_ip['port'])) # print(u'创建tdx连接, IP: {}/{}'.format(self.best_ip['ip'], self.best_ip['port'])) self.connection_status = True # if not is_reconnect: # 更新 symbol_exchange_dict , symbol_market_dict # self.qryInstrument() except Exception as ex: self.write_log(u'连接服务器tdx异常:{},{}'.format(str(ex), traceback.format_exc())) return # ---------------------------------------------------------------------- def ping(self, ip, port=7709): """ ping行情服务器 :param ip: :param port: :param type_: :return: """ apix = TdxExHq_API() __time1 = datetime.now() try: with apix.connect(ip, port): if apix.get_instrument_count() > 10000: _timestamp = datetime.now() - __time1 self.write_log(f'服务器{ip}:{port},耗时:{_timestamp}') return _timestamp else: self.write_log(f'该服务器IP {ip}无响应') return timedelta(9, 9, 0) except Exception: self.write_error(f'tdx ping服务器,异常的响应{ip}') return timedelta(9, 9, 0) # ---------------------------------------------------------------------- def select_best_ip(self): """ 选择行情服务器 :return: """ self.write_log(u'选择通达信行情服务器') data_future = [self.ping(x['ip'], x['port']) for x in TDX_FUTURE_HOSTS] best_future_ip = TDX_FUTURE_HOSTS[data_future.index(min(data_future))] self.write_log(u'选取 {}:{}'.format(best_future_ip['ip'], best_future_ip['port'])) # print(u'选取 {}:{}'.format(best_future_ip['ip'], best_future_ip['port'])) save_cache_ip(best_future_ip) return best_future_ip # ---------------------------------------------------------------------- def qryInstrument(self): """ 查询/更新合约信息 :return: """ # 取得所有的合约信息 num = self.api.get_instrument_count() if not isinstance(num, int): return all_contacts = sum([ self.api.get_instrument_info((int(num / 500) - i) * 500, 500) for i in range(int(num / 500) + 1) ], []) # [{"category":category,"market": int,"code":sting,"name":string,"desc":string},{}] # 对所有合约处理,更新字典 指数合约-tdx市场,指数合约-交易所 for tdx_contract in all_contacts: tdx_symbol = tdx_contract.get('code', None) if tdx_symbol is None: continue tdx_market_id = tdx_contract.get('market') if str(tdx_market_id) in Tdx_Vn_Exchange_Map: self.symbol_exchange_dict.update( {tdx_symbol: Tdx_Vn_Exchange_Map.get(str(tdx_market_id))}) self.symbol_market_dict.update({tdx_symbol: tdx_market_id}) # ---------------------------------------------------------------------- def get_bars(self, symbol, period, callback, bar_is_completed=False, bar_freq=1, start_dt=None): """ 返回k线数据 symbol:合约 period: 周期: 1min,3min,5min,15min,30min,1day,3day,1hour,2hour,4hour,6hour,12hour """ ret_bars = [] tdx_symbol = symbol.upper().replace('_', '') tdx_symbol = tdx_symbol.replace('99', 'L9') tdx_index_symbol = get_underlying_symbol(symbol) + 'L9' self.connect() if self.api is None: return False, ret_bars if period not in PERIOD_MAPPING.keys(): self.write_error(u'{} 周期{}不在下载清单中: {}'.format( datetime.now(), period, list(PERIOD_MAPPING.keys()))) return False, ret_bars # tdx_period = PERIOD_MAPPING.get(period) if start_dt is None: self.write_log(u'没有设置开始时间,缺省为10天前') qry_start_date = datetime.now() - timedelta(days=10) else: qry_start_date = start_dt end_date = datetime.combine(datetime.now() + timedelta(days=1), time(ALL_MARKET_END_HOUR, 0)) if qry_start_date > end_date: qry_start_date = end_date self.write_log('{}开始下载tdx:{} {}数据, {} to {}.'.format( datetime.now(), tdx_symbol, period, qry_start_date, end_date)) # print('{}开始下载tdx:{} {}数据, {} to {}.'.format(datetime.now(), tdx_symbol, tdx_period, last_date, end_date)) try: _start_date = end_date _bars = [] _pos = 0 while _start_date > qry_start_date: _res = self.api.get_instrument_bars( PERIOD_MAPPING[period], self.symbol_market_dict.get(tdx_index_symbol, 0), tdx_symbol, _pos, QSIZE) if _res is not None: _bars = _res + _bars _pos += QSIZE if _res is not None and len(_res) > 0: _start_date = _res[0]['datetime'] _start_date = datetime.strptime(_start_date, '%Y-%m-%d %H:%M') self.write_log(u'分段取数据开始:{}'.format(_start_date)) else: break if len(_bars) == 0: self.write_error('{} Handling {}, len1={}..., continue'.format( str(datetime.now()), tdx_symbol, len(_bars))) return False, ret_bars current_datetime = datetime.now() data = self.api.to_df(_bars) data = data.assign(datetime=to_datetime(data['datetime'])) data = data.assign(ticker=symbol) data['instrument_id'] = data['ticker'] # if future['market'] == 28 or future['market'] == 47: # # 大写字母: 郑州商品 or 中金所期货 # data['instrument_id'] = data['ticker'] # else: # data['instrument_id'] = data['ticker'].apply(lambda x: x.lower()) data['symbol'] = symbol data = data.drop([ 'year', 'month', 'day', 'hour', 'minute', 'price', 'amount', 'ticker' ], errors='ignore', axis=1) data = data.rename(index=str, columns={ 'position': 'open_interest', 'trade': 'volume', }) if len(data) == 0: print('{} Handling {}, len2={}..., continue'.format( str(datetime.now()), tdx_symbol, len(data))) return False, ret_bars data['total_turnover'] = data['volume'] data["limit_down"] = 0 data["limit_up"] = 999999 data['trading_date'] = data['datetime'] data['trading_date'] = data['trading_date'].apply( lambda x: (x.strftime('%Y-%m-%d'))) monday_ts = data['datetime'].dt.weekday == 0 # 星期一 night_ts1 = data['datetime'].dt.hour > ALL_MARKET_END_HOUR night_ts2 = data['datetime'].dt.hour < ALL_MARKET_BEGIN_HOUR data.loc[night_ts1, 'datetime'] -= timedelta( days=1) # 所有日期的夜盘(21:00~24:00), 减一天 monday_ts1 = monday_ts & night_ts1 # 星期一的夜盘(21:00~24:00), 再减两天 data.loc[monday_ts1, 'datetime'] -= timedelta(days=2) monday_ts2 = monday_ts & night_ts2 # 星期一的夜盘(00:00~04:00), 再减两天 data.loc[monday_ts2, 'datetime'] -= timedelta(days=2) # data['datetime'] -= timedelta(minutes=1) # 直接给Strategy使用, RiceQuant格式, 不需要减1分钟 data['dt_datetime'] = data['datetime'] data['date'] = data['datetime'].apply(lambda x: (x.strftime('%Y-%m-%d'))) data['time'] = data['datetime'].apply(lambda x: (x.strftime('%H:%M:%S'))) data['datetime'] = data['datetime'].apply( lambda x: float(x.strftime('%Y%m%d%H%M%S'))) data = data.set_index('dt_datetime', drop=False) # data = data[int(last_date.strftime('%Y%m%d%H%M%S')):int(end_date.strftime('%Y%m%d%H%M%S'))] # data = data[str(last_date):str(end_date)] for index, row in data.iterrows(): add_bar = BarData() try: add_bar.symbol = row['symbol'] add_bar.datetime = index add_bar.date = row['date'] add_bar.time = row['time'] add_bar.trading_date = row['trading_date'] add_bar.open = float(row['open']) add_bar.high = float(row['high']) add_bar.low = float(row['low']) add_bar.close = float(row['close']) add_bar.volume = float(row['volume']) add_bar.openInterest = float(row['open_interest']) except Exception as ex: self.write_error( 'error when convert bar:{},ex:{},t:{}'.format( row, str(ex), traceback.format_exc())) # print('error when convert bar:{},ex:{},t:{}'.format(row, str(ex), traceback.format_exc())) return False if start_dt is not None and index < start_dt: continue ret_bars.append(add_bar) if callback is not None: freq = bar_freq bar_is_completed = True if period != '1min' and index == data['dt_datetime'][-1]: # 最后一个bar,可能是不完整的,强制修改 # - 5min修改后freq基本正确 # - 1day在VNPY合成时不关心已经收到多少Bar, 所以影响也不大 # - 但其它分钟周期因为不好精确到每个品种, 修改后的freq可能有错 if index > current_datetime: bar_is_completed = False # 根据秒数算的话,要+1,例如13:31,freq=31,第31根bar freq = NUM_MINUTE_MAPPING[period] - int( (index - current_datetime).total_seconds() / 60) callback(add_bar, bar_is_completed, freq) return True, ret_bars except Exception as ex: self.write_error('exception in get:{},{},{}'.format( tdx_symbol, str(ex), traceback.format_exc())) # print('exception in get:{},{},{}'.format(tdx_symbol,str(ex), traceback.format_exc())) self.write_log(u'重置连接') self.api = None self.connect(is_reconnect=True) return False, ret_bars def get_price(self, symbol): """获取最新价格""" tdx_symbol = symbol.upper().replace('_', '') short_symbol = get_underlying_symbol(tdx_symbol).upper() if tdx_symbol.endswith('99'): query_symbol = tdx_symbol.replace('99', 'L9') else: query_symbol = get_full_symbol(tdx_symbol) if query_symbol != tdx_symbol: self.write_log('转换合约:{}=>{}'.format(tdx_symbol, query_symbol)) tdx_index_symbol = short_symbol + 'L9' self.connect() if self.api is None: return 0 market_id = self.symbol_market_dict.get(tdx_index_symbol, 0) _res = self.api.get_instrument_quote(market_id, query_symbol) if not isinstance(_res, list): return 0 if len(_res) == 0: return 0 return float(_res[0].get('price', 0)) def get_99_contracts(self): """ 获取指数合约 :return: dict list """ self.connect() result = self.api.get_instrument_quote_list(42, 3, 0, 100) return result def get_mi_contracts(self): """ 获取主力合约 :return: dict list """ self.connect() result = self.api.get_instrument_quote_list(60, 3, 0, 100) return result def get_contracts(self, exchange): self.connect() market_id = Vn_Tdx_Exchange_Map.get(exchange, None) if market_id is None: print(u'市场:{}配置不在Vn_Tdx_Exchange_Map:{}中,不能取市场下所有合约'.format( exchange, Vn_Tdx_Exchange_Map)) return [] index = 0 count = 100 results = [] while (True): print(u'查询{}下:{}~{}个合约'.format(exchange, index, index + count)) result = self.api.get_instrument_quote_list( int(market_id), 3, index, count) results.extend(result) index += count if len(result) < count: break return results def get_mi_contracts2(self): """ 获取主力合约""" self.connect() contracts = [] for exchange in Vn_Tdx_Exchange_Map.keys(): contracts.extend(self.get_mi_contracts_from_exchange(exchange)) return contracts def get_mi_contracts_from_exchange(self, exchange): contracts = self.get_contracts(exchange) if len(contracts) == 0: print(u'异常,未能获取{}下合约信息'.format(exchange)) return [] mi_contracts = [] short_contract_dict = {} for contract in contracts: # 排除指数合约 code = contract.get('code') if code[-2:] in ['L9', 'L8', 'L0', 'L1', 'L2', 'L3', '50'] or\ (exchange == Exchange.CFFEX and code[-3:] in ['300', '500']): continue short_symbol = get_underlying_symbol(code).upper() contract_list = short_contract_dict.get(short_symbol, []) contract_list.append(contract) short_contract_dict.update({short_symbol: contract_list}) for k, v in short_contract_dict.items(): sorted_list = sorted(v, key=lambda c: c['ZongLiang']) mi_contracts.append(sorted_list[-1]) return mi_contracts def get_markets(self): """ 获取市场代码 :return: """ self.connect() result = self.api.get_markets() return result def get_transaction_data(self, symbol): """获取当前交易日的历史成交记录""" ret_datas = [] max_data_size = sys.maxsize symbol = symbol.upper() if '99' in symbol: # 查询的是指数合约 symbol = symbol.replace('99', 'L9') tdx_index_symbol = symbol else: # 查询的是普通合约 tdx_index_symbol = get_underlying_symbol(symbol).upper() + 'L9' self.connect() q_size = QSIZE * 5 # 每秒 2个, 10小时 max_data_size = 1000000 self.write_log(u'开始下载{}当日分笔数据'.format(symbol)) try: _datas = [] _pos = 0 while (True): _res = self.api.get_transaction_data( market=self.symbol_market_dict.get(tdx_index_symbol, 0), code=symbol, start=_pos, count=q_size) if _res is not None: for d in _res: dt = d.pop('date') # 星期1~星期6 if dt.hour >= 20 and 1 < dt.isoweekday() <= 6: dt = dt - timedelta(days=1) elif dt.hour >= 20 and dt.isoweekday() == 1: # 星期一取得20点后数据 dt = dt - timedelta(days=3) elif dt.hour < 8 and dt.isoweekday() == 1: # 星期一取得8点前数据 dt = dt - timedelta(days=3) elif dt.hour >= 20 and dt.isoweekday() == 7: # 星期天取得20点后数据,肯定是星期五夜盘 dt = dt - timedelta(days=2) elif dt.isoweekday() == 7: # 星期日取得其他时间,必然是 星期六凌晨的数据 dt = dt - timedelta(days=1) d.update({'datetime': dt}) # 接口有bug,返回价格*1000,所以要除以1000 d.update({'price': d.get('price', 0) / 1000}) _datas = sorted(_res, key=lambda s: s['datetime']) + _datas _pos += min(q_size, len(_res)) if _res is not None and len(_res) > 0: self.write_log(u'分段取分笔数据:{} ~{}, {}条,累计:{}条'.format( _res[0]['datetime'], _res[-1]['datetime'], len(_res), _pos)) else: break if len(_datas) >= max_data_size: break if len(_datas) == 0: self.write_error(u'{}分笔成交数据获取为空') return True, _datas except Exception as ex: self.write_error( 'exception in get_transaction_data:{},{},{}'.format( symbol, str(ex), traceback.format_exc())) self.write_error(u'当前异常服务器信息:{}'.format(self.best_ip)) self.write_log(u'重置连接') self.api = None self.connect(is_reconnect=True) return False, ret_datas def save_cache(self, cache_folder, cache_symbol, cache_date, data_list): """保存文件到缓存""" os.makedirs(cache_folder, exist_ok=True) if not os.path.exists(cache_folder): self.write_error('缓存目录不存在:{},不能保存'.format(cache_folder)) return cache_folder_year_month = os.path.join(cache_folder, cache_date[:6]) os.makedirs(cache_folder_year_month, exist_ok=True) save_file = os.path.join(cache_folder_year_month, '{}_{}.pkz2'.format(cache_symbol, cache_date)) try: with bz2.BZ2File(save_file, 'wb') as f: pickle.dump(data_list, f) self.write_log(u'缓存成功:{}'.format(save_file)) except Exception as ex: self.write_error(u'缓存写入异常:{}'.format(str(ex))) def load_cache(self, cache_folder, cache_symbol, cache_date): """加载缓存数据""" if not os.path.exists(cache_folder): self.write_error('缓存目录:{}不存在,不能读取'.format(cache_folder)) return None cache_folder_year_month = os.path.join(cache_folder, cache_date[:6]) if not os.path.exists(cache_folder_year_month): self.write_error('缓存目录:{}不存在,不能读取'.format(cache_folder_year_month)) return None cache_file = os.path.join( cache_folder_year_month, '{}_{}.pkz2'.format(cache_symbol, cache_date)) if not os.path.isfile(cache_file): self.write_error('缓存文件:{}不存在,不能读取'.format(cache_file)) return None with bz2.BZ2File(cache_file, 'rb') as f: data = pickle.load(f) return data return None def get_history_transaction_data(self, symbol, date, cache_folder=None): """获取当某一交易日的历史成交记录""" ret_datas = [] if isinstance(date, datetime): date = date.strftime('%Y%m%d') if isinstance(date, str): date = int(date) self.connect() cache_symbol = symbol cache_date = str(date) max_data_size = sys.maxsize symbol = symbol.upper() if '99' in symbol: # 查询的是指数合约 symbol = symbol.replace('99', 'L9') tdx_index_symbol = symbol else: # 查询的是普通合约 tdx_index_symbol = get_underlying_symbol(symbol).upper() + 'L9' q_size = QSIZE * 5 # 每秒 2个, 10小时 max_data_size = 1000000 # 优先从缓存加载 if cache_folder: buffer_data = self.load_cache(cache_folder, cache_symbol, cache_date) if buffer_data: self.write_log(u'使用缓存文件') return True, buffer_data self.write_log(u'开始下载{} 历史{}分笔数据'.format(date, symbol)) cur_trading_date = get_trading_date() if date == int(cur_trading_date.replace('-', '')): return self.get_transaction_data(symbol) try: _datas = [] _pos = 0 while (True): _res = self.api.get_history_transaction_data( market=self.symbol_market_dict.get(tdx_index_symbol, 0), date=date, code=symbol, start=_pos, count=q_size) if _res is not None: for d in _res: dt = d.pop('date') # 星期1~星期6 if dt.hour >= 20 and 1 < dt.isoweekday() <= 6: dt = dt - timedelta(days=1) d.update({'datetime': dt}) elif dt.hour >= 20 and dt.isoweekday() == 1: # 星期一取得20点后数据 dt = dt - timedelta(days=3) d.update({'datetime': dt}) elif dt.hour < 8 and dt.isoweekday() == 1: # 星期一取得8点前数据 dt = dt - timedelta(days=3) d.update({'datetime': dt}) elif dt.hour >= 20 and dt.isoweekday() == 7: # 星期天取得20点后数据,肯定是星期五夜盘 dt = dt - timedelta(days=2) d.update({'datetime': dt}) elif dt.isoweekday() == 7: # 星期日取得其他时间,必然是 星期六凌晨的数据 dt = dt - timedelta(days=1) d.update({'datetime': dt}) else: d.update({'datetime': dt}) # 接口有bug,返回价格*1000,所以要除以1000 d.update({'price': d.get('price', 0) / 1000}) _datas = sorted(_res, key=lambda s: s['datetime']) + _datas _pos += min(q_size, len(_res)) if _res is not None and len(_res) > 0: self.write_log(u'分段取分笔数据:{} ~{}, {}条,累计:{}条'.format( _res[0]['datetime'], _res[-1]['datetime'], len(_res), _pos)) else: break if len(_datas) >= max_data_size: break if len(_datas) == 0: self.write_error(u'{}分笔成交数据获取为空'.format(date)) return False, _datas # 缓存文件 if cache_folder: self.save_cache(cache_folder, cache_symbol, cache_date, _datas) return True, _datas except Exception as ex: self.write_error( 'exception in get_transaction_data:{},{},{}'.format( symbol, str(ex), traceback.format_exc())) self.write_error(u'当前异常服务器信息:{}'.format(self.best_ip)) self.write_log(u'重置连接') self.api = None self.connect(is_reconnect=True) return False, ret_datas
else: #512 direction = 0 nature_name = '' result.append(OrderedDict([ ("date", date), ("hour", hour), ("minute", minute), ("second", second), ("price", price), ("volume", volume), ("zengcang", zengcang), ("nature", nature), ("nature_mark", nature // 10000), ("nature_value", nature % 10000), ("nature_name", nature_name), ("direction", direction), ])) return result if __name__ == "__main__": from pytdx.exhq import TdxExHq_API api = TdxExHq_API() with api.connect('121.14.110.210', 7727): print(api.to_df(api.get_transaction_data(47, 'IFL9'))) # print(api.to_df(api.get_transaction_data(31, "00020")))
nature_name = "双平" else: nature_name = "换手" result.append( OrderedDict([ ("date", date), ("hour", hour), ("minute", minute), ("second", second), ("price", price), ("volume", volume), ("zengcang", zengcang), ("nature", nature), ("nature_mark", nature // 10000), ("nature_value", nature % 10000), ("nature_name", nature_name), ("direction", direction), ])) return result if __name__ == "__main__": from pytdx.exhq import TdxExHq_API api = TdxExHq_API() with api.connect('121.14.110.210', 7727): print(api.to_df(api.get_transaction_data(47, 'IF1709'))) print(api.to_df(api.get_transaction_data(31, "00020")))