diff --git a/PKG-INFO b/PKG-INFO index c27c484a..5484ed2a 100644 --- a/PKG-INFO +++ b/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: tqsdk -Version: 3.7.6 +Version: 3.7.8 Summary: TianQin SDK Home-page: https://www.shinnytech.com/tqsdk Author: TianQin diff --git a/doc/advanced/index.rst b/doc/advanced/index.rst index 5ec50c18..4f2e0a4e 100644 --- a/doc/advanced/index.rst +++ b/doc/advanced/index.rst @@ -17,4 +17,3 @@ unanttended.rst targetpostask2.rst scheduler.rst - tqsdk2ctptest.rst diff --git a/doc/conf.py b/doc/conf.py index 8138e3e5..4b71b3db 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -40,7 +40,7 @@ # General information about the project. project = u'TianQin Python SDK' -copyright = u'2018-2024, TianQin' +copyright = u'2018-2025, TianQin' author = u'TianQin' # The version info for the project you're documenting, acts as replacement for @@ -48,9 +48,9 @@ # built documents. # # The short X.Y version. -version = u'3.7.6' +version = u'3.7.8' # The full version, including alpha/beta/rc tags. -release = u'3.7.6' +release = u'3.7.8' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/doc/profession.rst b/doc/profession.rst index a5585a88..f0585847 100644 --- a/doc/profession.rst +++ b/doc/profession.rst @@ -20,6 +20,7 @@ TqSdk 中大部分功能是供用户免费使用的, 同时我们也提供了专 .. figure:: images/how-grafana04.gif + `快期专业版官网地址 `_ `快期专业版文档地址 `_ diff --git a/doc/quickstart.rst b/doc/quickstart.rst index d0c600da..e9ba33a3 100644 --- a/doc/quickstart.rst +++ b/doc/quickstart.rst @@ -107,12 +107,6 @@ klines是一个pandas.DataFrame对象. 跟 api.get_quote() 一样, api.get_kline 这部分的完整示例程序请见 :ref:`tutorial-t30` . -我们也可以通过传入一个合约列表作为参数,来获取包含多个合约数据的K线:: - - klines = api.get_kline_serial(["SHFE.au1912", "SHFE.au2006"], 5) # 获取SHFE.au2006向SHFE.au1912对齐的K线 - -详细使用方法及说明请见 :py:meth:`~tqsdk.TqApi.get_kline_serial` 函数说明。 - 到这里为止, 你已经知道了如何获取实时行情和K线数据, 下面一段将介绍如何访问你的交易账户并发送交易指令 .. _quickstart_2_web_gui: diff --git a/doc/usage/backtest.rst b/doc/usage/backtest.rst index 24153c48..86944035 100644 --- a/doc/usage/backtest.rst +++ b/doc/usage/backtest.rst @@ -2,11 +2,9 @@ 策略程序回测 ================================================= -策略程序回测是 TqSdk 专业版中的功能,能让用户在不改变代码的情况下去回测自己的策略在历史行情的表现 +策略程序回测能让用户在不改变代码的情况下去回测自己的策略在历史行情的表现 -如果想使用策略回测该功能,可以点击 `天勤量化专业版 `_ 申请使用或购买 - -用户也可以申请模拟账户后模拟运行来检验策略 :ref:`sim_trading` +用户也可以使用快期模拟账户来模拟运行来检验策略 :ref:`sim_trading` 执行策略回测 ------------------------------------------------- diff --git a/doc/usage/option_trade.rst b/doc/usage/option_trade.rst index 152a3cf7..f611e315 100644 --- a/doc/usage/option_trade.rst +++ b/doc/usage/option_trade.rst @@ -2,10 +2,6 @@ 期权交易 & 交易所官方组合 ==================================================== -TqSdk 中期权交易(商品期权、金融期权和 ETF 期权)和交易所官方组合交易,均是 TqSdk 专业版中的功能 - -用户如果想在 TqSdk 中进行上述操作,可以点击 `天勤量化专业版 `_ 申请使用或购买 - TqSdk 中期权合和交易所官方组合的约代码格式参考如下:: DCE.m1807-C-2450 - 大商所豆粕期权 @@ -20,7 +16,7 @@ TqSdk 中期权合和交易所官方组合的约代码格式参考如下:: -对于交易所官方组合,目前 TqSdk 中只支持交易所官方组合进行实盘交易 +对于交易所官方组合,目前 TqSdk 中只支持交易所官方组合进行实盘交易,不支持在模拟中进行交易所官方组合交易 期权指标计算&序列计算函数 diff --git a/doc/usage/shinny_account.rst b/doc/usage/shinny_account.rst index 567cc0a6..8834b429 100644 --- a/doc/usage/shinny_account.rst +++ b/doc/usage/shinny_account.rst @@ -21,7 +21,7 @@ ------------------------------------------------- 对于 TqSdk 免费版,每个快期账户支持最多绑定一个实盘账户,而天勤量化专业版支持一个快期账户绑定任意多个实盘账户 -快期账户会在用户使用实盘账户时自动进行绑定,直到该快期账户没有能绑定实盘账户的名额(自动绑定功能需要 TqSdk 版本> 1.8.3):: +快期账户会在用户使用实盘账户时自动进行绑定,直到该快期账户没有能绑定实盘账户的名额:: from tqsdk import TqApi, TqAccount, TqAuth, TqKq api = TqApi(TqAccount("H海通期货", "320102", "123456"), auth=TqAuth("快期账户", "账户密码")) diff --git a/doc/usage/targetpostask.rst b/doc/usage/targetpostask.rst index ff7f6efa..26778eb9 100644 --- a/doc/usage/targetpostask.rst +++ b/doc/usage/targetpostask.rst @@ -72,5 +72,3 @@ -:py:class:`~tqsdk.InsertOrderUntilAllTradedTask` 是追价下单task, 该task会在行情变化后自动撤单重下,直到全部成交. - diff --git a/doc/version.rst b/doc/version.rst index 46a3c796..fe0b5d8c 100644 --- a/doc/version.rst +++ b/doc/version.rst @@ -2,6 +2,18 @@ 版本变更 ============================= +3.7.8 (2025/01/23) + +* docs: 修正部分文档 +* 修复: 因郑商所三位合约导致合约信息中部分字段错误的问题 + + +3.7.7 (2025/01/14) + +* docs: 修正部分文档,补充 wait_update 的 deadline 参数说明 +* 自该版本起不再支持使用 api._data['quotes'] 获取全部合约 + + 3.7.6 (2024/11/14) * 修复: 增加依赖库 packaging diff --git a/setup.py b/setup.py index 43b68113..5f7c8bf4 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setuptools.setup( name='tqsdk', - version="3.7.6", + version="3.7.8", description='TianQin SDK', author='TianQin', author_email='tianqincn@gmail.com', diff --git a/tqsdk/__version__.py b/tqsdk/__version__.py index 044fb790..594f2c42 100644 --- a/tqsdk/__version__.py +++ b/tqsdk/__version__.py @@ -1 +1 @@ -__version__ = '3.7.6' +__version__ = '3.7.8' diff --git a/tqsdk/api.py b/tqsdk/api.py index b3c4abab..25e84798 100644 --- a/tqsdk/api.py +++ b/tqsdk/api.py @@ -64,7 +64,7 @@ from tqsdk.entity import Entity from tqsdk.exceptions import TqTimeoutError from tqsdk.log import _clear_logs, _get_log_name, _get_disk_free -from tqsdk.objs import Quote, TradingStatus, Kline, Tick, Account, Position, Order, Trade, QuotesEntity, RiskManagementRule, RiskManagementData +from tqsdk.objs import Quote, TradingStatus, Kline, Tick, Account, Position, Order, Trade, RiskManagementRule, RiskManagementData from tqsdk.objs import SecurityAccount, SecurityOrder, SecurityTrade, SecurityPosition from tqsdk.objs_not_entity import QuoteList, TqDataFrame, TqSymbolDataFrame, SymbolList, SymbolLevelList, \ TqSymbolRankingDataFrame, TqOptionGreeksDataFrame, TqMdSettlementDataFrame @@ -276,8 +276,6 @@ def __init__(self, account: Optional[Union[TqMultiAccount, UnionTradeable]] = No self._klines_update_range = {} self._data = Entity() # 数据存储 self._data._instance_entity([]) - self._data["quotes"] = QuotesEntity(self) - self._data["quotes"]._instance_entity(["quotes"]) self._diffs = [] # 自上次wait_update返回后收到更新数据的数组 (异步代码) self._sync_diffs = [] # 自上次wait_update返回后收到更新数据的数组 (同步代码) self._pending_diffs = [] # 从网络上收到的待处理的 diffs, 只在 wait_update 函数执行过程中才可能为非空 @@ -287,6 +285,7 @@ def __init__(self, account: Optional[Union[TqMultiAccount, UnionTradeable]] = No self._dividend_cache = {} # 缓存合约对应的复权系数矩阵,每个合约只计算一次 self._send_chan, self._recv_chan = TqChan(self), TqChan(self) # 消息收发队列 self._ws_md_recv_chan = None # 记录 ws_md_recv_chan 引用 + self._pre20_ins_info = {} # 20年9月份之前的合约信息 # slave模式的api不需要完整初始化流程 self._is_slave = isinstance(account, TqApi) @@ -542,19 +541,20 @@ async def _ensure_symbol_async(self, symbol: Union[str, List[str]]): if self._stock is False: raise Exception("代码 %s 不存在, 请检查合约代码是否填写正确" % symbol_list) else: - query_pack = _query_for_quote(symbol_list) - self._send_pack(query_pack) + for query in _query_for_quote(symbol_list, self._pre20_ins_info.keys()): + self._send_pack(query) async with self.register_update_notify() as update_chan: async for _ in update_chan: + # 这里用 price_tick 判断是否已经收到了合约信息,是为了兼容 2020年9月份之前上市的合约 + # 合约服务没有提供这些合约,tqsdk 是通过预先加载本地缓存文件的方式提供这些合约的信息 + # 理想的判断标准是 basktest 模块中的 _ensure_query 函数 if all([self._data.get("quotes", {}).get(symbol, {}).get('price_tick', float('nan')) > 0 for symbol in symbol_list]): break # ---------------------------------------------------------------------- def get_trading_status(self, symbol: str) -> TradingStatus: """ - 获取指定合约的交易状态. 此接口为 TqSdk 专业版提供,便于实现开盘抢单功能。 - - 如果想使用此功能,可以点击 `天勤量化专业版 `_ 申请试用或购买 + 获取指定合约的交易状态,便于实现开盘抢单功能。 Args: symbol (str): 合约代码 @@ -1012,7 +1012,7 @@ def _get_data_series(self, call_func: str, symbol_list: Union[str, List[str]], d # ---------------------------------------------------------------------- def get_trading_calendar(self, start_dt: Union[date, datetime], end_dt: Union[date, datetime]) -> pd.DataFrame: """ - 获取一段时间内的交易日历信息,交易日历可以处理的范围为 2003-01-01 ~ 2024-12-31。 + 获取一段时间内的交易日历信息,每年的交易日历将在交易所新一年的节假日安排后更新 Args: start_dt (date/datetime): 起始时间 @@ -1068,7 +1068,7 @@ def get_trading_calendar(self, start_dt: Union[date, datetime], end_dt: Union[da # ---------------------------------------------------------------------- def query_his_cont_quotes(self, symbol: Union[str, List[str]], n: int = 200) -> pd.DataFrame: """ - 获取指定的主连合约最近 n 天的标的,可以处理的范围为 2003-01-01 ~ 2022-12-31。 + 获取指定的主连合约最近 n 天的标的。 Args: symbol (str/list of str): 指定主连合约代码或主连合约代码列表. @@ -1126,9 +1126,7 @@ def query_his_cont_quotes(self, symbol: Union[str, List[str]], n: int = 200) -> # ---------------------------------------------------------------------- def add_risk_rule(self, rule: TqRiskRule) -> None: """ - 添加一项风控规则实例,此接口为 TqSdk 专业版提供。 - - 如需使用此功能,可以点击 `天勤量化专业版 `_ 申请试用或购买 + 添加一项风控规则实例。 Args: rule (TqRiskRule): 风控规则实例,必须是 TqRiskRule 的子类型 @@ -1142,9 +1140,7 @@ def add_risk_rule(self, rule: TqRiskRule) -> None: def delete_risk_rule(self, rule: TqRiskRule) -> None: """ - 删除一项风控规则实例,此接口为 TqSdk 专业版提供。 - - 如需使用此功能,可以点击 `天勤量化专业版 `_ 申请试用或购买 + 删除一项风控规则实例。 Args: rule (TqRiskRule): 风控规则实例,必须是 TqRiskRule 的子类型 @@ -1884,7 +1880,7 @@ def wait_update(self, deadline: Optional[float] = None, _task: Union[asyncio.Tas * 如果没有收到数据包,则挂起等待. Args: - deadline (float): [可选]指定截止时间,自unix epoch(1970-01-01 00:00:00 GMT)以来的秒数(time.time())。默认没有超时(无限等待) + deadline (float): [可选]指定截止时间,自unix epoch(1970-01-01 00:00:00 GMT)以来的秒数(time.time())。默认没有超时(无限等待), 当 wait_update 有数据更新或者任务执行时会返回为 True,否则会陷入阻塞,使用 deadline 参数会让 wait_update 函数在当前时间大于 deadline 参数后返回 False ,避免长时间阻塞。非简单用法,如果设置的 deadline 过小,而计算任务过多,可能会导致处理任务堆积,引发潜在问题。 Returns: bool: 如果收到业务数据更新则返回 True, 如果到截止时间依然没有收到业务数据更新则返回 False @@ -3348,10 +3344,13 @@ def _setup_connection(self): if quote["ins_class"] == "FUTURE_OPTION": quote["exercise_year"] = _timestamp_nano_to_datetime(int(quote["expire_datetime"] * 1000000) * 1000).year quote["exercise_month"] = _timestamp_nano_to_datetime(int(quote["expire_datetime"] * 1000000) * 1000).month - ws_md_recv_chan.send_nowait({ - "aid": "rtn_data", - "data": [{"quotes": quotes}] - }) # 获取合约信息 + if self._stock is False: + ws_md_recv_chan.send_nowait({ + "aid": "rtn_data", + "data": [{"quotes": quotes}] + }) # 获取合约信息 + else: + self._pre20_ins_info = quotes self._ws_md_recv_chan = ws_md_recv_chan # 记录 ws_md_recv_chan 引用 diff --git a/tqsdk/backtest/backtest.py b/tqsdk/backtest/backtest.py index 7d76c2ba..4e2a956b 100644 --- a/tqsdk/backtest/backtest.py +++ b/tqsdk/backtest/backtest.py @@ -160,6 +160,7 @@ async def _run(self, api, sim_send_chan, sim_recv_chan, md_send_chan, md_recv_ch self._diffs.append({ "ins_list": pack["ins_list"] }) + await self._ensure_symbols(pack["ins_list"].split(",")) for ins in pack["ins_list"].split(","): await self._ensure_quote(ins) await self._send_diff() # 处理上一次未处理的 peek_message @@ -451,16 +452,21 @@ async def _ensure_query(self, pack): while not query_pack.items() <= self._data.get("symbols", {}).get(pack["query_id"], {}).items(): await update_chan.recv() - async def _ensure_quote(self, symbol): + async def _ensure_symbols(self, symbols): # 在接新版合约服务器后,合约信息程序运行过程中查询得到的,这里不再能保证合约一定存在,需要添加 quote 默认值 - quote = _get_obj(self._data, ["quotes", symbol], BtQuote(self._api)) - if math.isnan(quote.get("price_tick")): - query_pack = _query_for_quote(symbol) + quotes = [_get_obj(self._data, ["quotes", symbol], BtQuote(self._api)) for symbol in symbols] + quotes = [q for q in quotes if math.isnan(q.get("price_tick"))] + if not quotes: + return + for query_pack in _query_for_quote([q._path[-1] for q in quotes], self._api._pre20_ins_info.keys()): await self._md_send_chan.send(query_pack) - async with TqChan(self._api, last_only=True) as update_chan: - quote["_listener"].add(update_chan) - while math.isnan(quote.get("price_tick")): - await update_chan.recv() + async with TqChan(self._api, last_only=True) as update_chan: + for q in quotes: + q["_listener"].add(update_chan) + while any([math.isnan(q.get("price_tick")) for q in quotes]): + await update_chan.recv() + + async def _ensure_quote(self, symbol): if symbol not in self._quotes or self._quotes[symbol]["min_duration"] > 60000000000: await self._ensure_serial(symbol, 60000000000) diff --git a/tqsdk/objs.py b/tqsdk/objs.py index 3c972e0b..47895128 100644 --- a/tqsdk/objs.py +++ b/tqsdk/objs.py @@ -4,38 +4,10 @@ import copy import json -import warnings from typing import List from tqsdk.diff import _get_obj from tqsdk.entity import Entity -from tqsdk.utils import _query_for_init, _generate_uuid - - -class QuotesEntity(Entity): - - def __init__(self, api): - self._api = api - self._not_send_init_query = True - - def __iter__(self): - message = """ - 不推荐使用 api._data['quotes'] 获取全部合约,该使用方法会在 20201101 之后的版本中放弃维护。 - 需要注意: - * 在同步代码中,初次使用 api._data['quotes'] 获取全部合约会产生一个耗时很长的查询。 - * 在协程中,api._data['quotes'] 这种用法不支持使用。 - 请尽快修改使用新的接口,参考链接 http://doc.shinnytech.com/tqsdk/reference/tqsdk.api.html#tqsdk.api.TqApi.query_quotes - """ - warnings.warn(message, DeprecationWarning, stacklevel=3) - self._api._logger.warning("deprecation", content="Deprecation Warning in api._data['quotes']") - - # 兼容旧版 tqsdk 所做的修改,来支持用户使用 for k,v in api._data.quotes.items() 类似的用法 - # 从 api._init_() 最后 3 行移到这里 - if self._not_send_init_query and self._api._stock: - self._not_send_init_query = False - q = _query_for_init() - self._api.query_graphql(q, {}, _generate_uuid("PYSDK_quote")) - return super().__iter__() class Quote(Entity): @@ -143,7 +115,7 @@ def __init__(self, api): self.strike_price: float = float("nan") #: 合约类型 self.ins_class: str = "" - #: 交易所内的合约代码 + #: 合约代码,包含了交易所代码 self.instrument_id: str = "" #: 合约中文名 self.instrument_name: str = "" diff --git a/tqsdk/objs_not_entity.py b/tqsdk/objs_not_entity.py index 3f42277d..07a7d3a3 100644 --- a/tqsdk/objs_not_entity.py +++ b/tqsdk/objs_not_entity.py @@ -15,7 +15,7 @@ from tqsdk.ins_schema import ins_schema, _add_all_frags from tqsdk.objs import Quote from tqsdk.diff import _get_obj -from tqsdk.utils import _query_for_quote, _generate_uuid +from tqsdk.utils import _generate_uuid from tqsdk.tafunc import _get_t_series, get_impv, _get_d1, get_delta, get_theta, get_gamma, get_vega, get_rho """ @@ -54,22 +54,8 @@ def __init__(self, api, quotes): if not hasattr(quote, '_task'): quote._task = api.create_task(ensure_quote_with_underlying(api, quote), _caller_api=True) - async def _ensure_symbols(self): - if all([q.price_tick > 0 for q in self]): - return - query_symbols = [q._path[-1] for q in self if not q.price_tick > 0] - query_pack = _query_for_quote(query_symbols) - self._api._send_pack(query_pack) - async with self._api.register_update_notify(self) as update_chan: - async for _ in update_chan: - # 这里用 price_tick 判断是否已经收到了合约信息,是为了兼容 2020年9月份之前上市的合约 - # 合约服务没有提供这些合约,tqsdk 是通过预先加载本地缓存文件的方式提供这些合约的信息 - # 理想的判断标准是 basktest 模块中的 _ensure_query 函数 - if all([q.price_tick > 0 for q in self]): - return - async def _ensure_quotes(self): - await self._ensure_symbols() + await self._api._ensure_symbol_async([q._path[-1] for q in self]) self._api._auth._has_md_grants([q._path[-1] for q in self]) # 权限检查 # 发送的请求会请求到所有字段,如果是期权也会请求标的的合约信息 underlying_symbols = set([q.underlying_symbol for q in self if q.underlying_symbol]) diff --git a/tqsdk/risk_rule.py b/tqsdk/risk_rule.py index c6666553..3defa483 100644 --- a/tqsdk/risk_rule.py +++ b/tqsdk/risk_rule.py @@ -35,7 +35,6 @@ class TqRuleOpenCountsLimit(TqRiskRule): """ 风控规则类 - 交易日内开仓次数限制。 - 此功能为 TqSdk 专业版提供,如需使用此功能,可以点击 `天勤量化专业版 `_ 申请试用或购买 """ def __init__(self, api, open_counts_limit, symbol, account=None): @@ -43,7 +42,7 @@ def __init__(self, api, open_counts_limit, symbol, account=None): Args: api (TqApi): TqApi 实例 - open_volumes_limit (int): 交易日内开仓手数上限 + open_counts_limit (int): 交易日内开仓次数上限 symbol (str/list of str): 负责限制的合约代码或合约代码列表. * str: 一个合约代码 @@ -110,7 +109,6 @@ class TqRuleOpenVolumesLimit(TqRiskRule): """ 风控规则类 - 交易日内开仓手数限制 - 此功能为 TqSdk 专业版提供,如需使用此功能,可以点击 `天勤量化专业版 `_ 申请试用或购买 """ def __init__(self, api, open_volumes_limit, symbol, account=None): @@ -207,8 +205,6 @@ class TqRuleAccOpenVolumesLimit(TqRiskRule): 限制合约开仓手数之和。 - 此功能为 TqSdk 专业版提供,如需使用此功能,可以点击 `天勤量化专业版 `_ 申请试用或购买 - """ def __init__(self, api, open_volumes_limit, symbol, account=None): diff --git a/tqsdk/symbols.py b/tqsdk/symbols.py index a8c5494b..5a61fab8 100644 --- a/tqsdk/symbols.py +++ b/tqsdk/symbols.py @@ -38,7 +38,11 @@ async def _run(self, api, sim_send_chan, sim_recv_chan, md_send_chan, md_recv_ch for query_id, query_result in d.get("symbols", {}).items(): if query_result: if query_result.get("error", None): - raise Exception(f"查询合约服务报错 {query_result['error']}") + try: + for ins in query_result['variables']['instrument_id']: + updated_quotes[ins] = self._api._pre20_ins_info[ins] + except KeyError: + raise Exception(f"查询合约服务报错 {query_result['error']}") from None elif query_id.startswith("PYSDK_quote"): quotes = self._api._symbols_to_quotes(query_result, self._quotes_all_keys) for quote in quotes.values(): diff --git a/tqsdk/trade_extension.py b/tqsdk/trade_extension.py index 3733e943..9f9b42c5 100644 --- a/tqsdk/trade_extension.py +++ b/tqsdk/trade_extension.py @@ -116,8 +116,8 @@ async def _md_recv(self, pack): unknown_symbols = self._need_wait_symbol_info - self._query_symbols if len(unknown_symbols) > 0: self._query_symbols = self._query_symbols.union(unknown_symbols) # 所有发送过ins_query的合约 - query_pack = _query_for_quote(list(unknown_symbols)) - await self._md_send_chan.send(query_pack) + for query_pack in _query_for_quote(list(unknown_symbols), self._api._pre20_ins_info.keys()): + await self._md_send_chan.send(query_pack) def _generate_pend_diff(self): """" diff --git a/tqsdk/tradeable/otg/tqkq.py b/tqsdk/tradeable/otg/tqkq.py index 523cdbc5..0a5b07f0 100644 --- a/tqsdk/tradeable/otg/tqkq.py +++ b/tqsdk/tradeable/otg/tqkq.py @@ -15,7 +15,7 @@ def __init__(self, td_url: Optional[str] = None, number: Optional[int] = None): """ 创建快期模拟账户实例 - 快期模拟的账户和交易信息可以在快期专业版查看,可以点击 `快期专业版 `_ 进行下载 + 快期模拟的账户和交易信息可以在快期专业版, 快期v2, 快期v3, 快期APP查看 Args: td_url (str): [可选]指定交易服务器的地址, 默认使用快期账户对应的交易服务地址 diff --git a/tqsdk/tradeable/sim/basesim.py b/tqsdk/tradeable/sim/basesim.py index 4c0ebeb7..75a977c7 100644 --- a/tqsdk/tradeable/sim/basesim.py +++ b/tqsdk/tradeable/sim/basesim.py @@ -171,7 +171,8 @@ async def _ensure_quote_info(self, symbol, quote_chan): if quote.get("price_tick") == quote.get("price_tick"): return quote.copy() if quote.get("price_tick") != quote.get("price_tick"): - await self._md_send_chan.send(_query_for_quote(symbol)) + for query_pack in _query_for_quote(symbol, self._api._pre20_ins_info.keys()): + await self._md_send_chan.send(query_pack) async for _ in quote_chan: quote_chan.task_done() if quote.get("price_tick") == quote.get("price_tick"): @@ -185,7 +186,8 @@ async def _ensure_quote(self, symbol, quote_chan): return quote.copy() if quote.get("price_tick") != quote.get("price_tick"): # 对于没有合约信息的 quote,发送查询合约信息的请求 - await self._md_send_chan.send(_query_for_quote(symbol)) + for query_pack in _query_for_quote(symbol, self._api._pre20_ins_info.keys()): + await self._md_send_chan.send(query_pack) async for _ in quote_chan: quote_chan.task_done() if quote.get("datetime", "") and quote.get("price_tick") == quote.get("price_tick"): diff --git a/tqsdk/utils.py b/tqsdk/utils.py index 33f025ae..e6959059 100644 --- a/tqsdk/utils.py +++ b/tqsdk/utils.py @@ -7,9 +7,10 @@ import random import secrets from bisect import bisect_right -from typing import Callable +from typing import Callable, List, Dict from sgqlc.operation import Operation +from sgqlc.types import Arg, String, Variable, list_of from pandas.core.internals import BlockManager from tqsdk.diff import _get_obj @@ -35,33 +36,39 @@ def _generate_uuid(prefix=''): return f"{prefix + '_' if prefix else ''}{RD.getrandbits(128):032x}" -def _query_for_quote(symbol): +def _query_for_quote(symbol, single_symbol_set) -> List[Dict]: """ - 返回请求某个合约的合约信息的 query_pack + 返回请求某个或多个合约的合约信息的 query_packs 调用次函数应该全部都是sdk的代码主动请求合约信息 用户请求合约信息一定是 PYSDK_api 开头的请求,因为用户请求的合约信息在回测时带有 timestamp 参数,是不应该调用此函数的 + 即:以 PYSDK_quote_ 开头的 query_id 都是 sdk 主动请求合约信息 + 对于在 single_symbol_set 合约表中的待查询合约逐个查询, 即每个查询请求只查询一个合约 + 对于不在 single_symbol_set 的合约表中的待查询合约批量查询一次 """ + results = [] symbol_list = symbol if isinstance(symbol, list) else [symbol] - op = Operation(ins_schema.rootQuery) - query = op.multi_symbol_info(instrument_id=symbol_list) - _add_all_frags(query) - return { - "aid": "ins_query", - "query_id": _generate_uuid(prefix='PYSDK_quote_'), - "query": op.__to_graphql__() - } - - -def _query_for_init(): - """ - 返回某些类型合约的 query - todo: 为了兼容旧版提供给用户的 api._data["quote"].items() 类似用法,应该限制交易所 ["SHFE", "DCE", "CZCE", "INE", "CFFEX", "KQ"] - """ - op = Operation(ins_schema.rootQuery) - query = op.multi_symbol_info(class_=["FUTURE", "INDEX", "OPTION", "COMBINE", "CONT"], - exchange_id=["SHFE", "DCE", "CZCE", "INE", "CFFEX", "KQ", "GFEX"]) - _add_all_frags(query) - return op.__to_graphql__() + batch_set = set(symbol_list) - single_symbol_set + single_set = set(symbol_list) - batch_set + if batch_set: + op = Operation(ins_schema.rootQuery) + query = op.multi_symbol_info(instrument_id=list(batch_set)) + _add_all_frags(query) + results.append({ + "aid": "ins_query", + "query_id": _generate_uuid(prefix='PYSDK_quote_'), + "query": op.__to_graphql__() + }) + for s in single_set: + op = Operation(ins_schema.rootQuery, variables={'instrument_id': Arg(list_of(String), graphql_name='$instrument_id')}) + query = op.multi_symbol_info(instrument_id=Variable('instrument_id', graphql_name='instrument_id')) + _add_all_frags(query) + results.append({ + "aid": "ins_query", + "query_id": _generate_uuid(prefix='PYSDK_quote_'), + "query": op.__to_graphql__(), + "variables": {"instrument_id": [s]} + }) + return results night_trading_table = {