From 426a3817b02c7cee622fdb9b6533858061173cb1 Mon Sep 17 00:00:00 2001 From: shinny-hongyan Date: Fri, 17 Apr 2026 09:34:16 +0000 Subject: [PATCH] Update Version 3.9.4 --- PKG-INFO | 2 +- doc/conf.py | 4 +- doc/enterprise.rst | 21 ++++++ doc/reference/index.rst | 1 + doc/reference/tqsdk.tqo32.rst | 10 +++ doc/version.rst | 6 ++ setup.py | 2 +- tqsdk/__init__.py | 2 +- tqsdk/__version__.py | 2 +- tqsdk/api.py | 9 ++- tqsdk/multiaccount.py | 2 +- tqsdk/risk_rule.py | 8 +-- tqsdk/tradeable/__init__.py | 2 +- tqsdk/tradeable/otg/__init__.py | 1 + tqsdk/tradeable/otg/tqo32.py | 124 ++++++++++++++++++++++++++++++++ 15 files changed, 181 insertions(+), 15 deletions(-) create mode 100644 doc/reference/tqsdk.tqo32.rst create mode 100644 tqsdk/tradeable/otg/tqo32.py diff --git a/PKG-INFO b/PKG-INFO index 541207ff..fcc78663 100644 --- a/PKG-INFO +++ b/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: tqsdk -Version: 3.9.3 +Version: 3.9.4 Summary: TianQin SDK Home-page: https://www.shinnytech.com/tqsdk Author: TianQin diff --git a/doc/conf.py b/doc/conf.py index 6d66d358..ca784c9c 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -54,9 +54,9 @@ # built documents. # # The short X.Y version. -version = u'3.9.3' +version = u'3.9.4' # The full version, including alpha/beta/rc tags. -release = u'3.9.3' +release = u'3.9.4' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/doc/enterprise.rst b/doc/enterprise.rst index 62688f9c..1b5d1536 100644 --- a/doc/enterprise.rst +++ b/doc/enterprise.rst @@ -53,3 +53,24 @@ front_broker="RohonDemo" 融航实盘情况下将对应信息换成实盘信息即可 融航资管平台连接模式的详细介绍,请点击 :py:class:`~tqsdk.TqRohon` + + +恒生 O32 柜台连接功能 +------------------------------------------------- +TqSdk 提供了恒生 O32 柜台的连接支持,支持用户通过直连模式接入恒生 O32 柜台,详情可以点击 :py:class:`~tqsdk.TqO32` :: + + from tqsdk import TqApi, TqAuth, TqO32, O32Account + + account = TqO32( + account_id=O32Account(user="用户", fund="基金", asset_unit="资产单元", portfolio="组合"), + password="O32 密码", + td_front_url="trade_front_host:trade_front_port", + mc_front_url="query_front_host:query_front_port", + license_file="/path/to/license.dat", + auth_code="O32 授权码", + ) + api = TqApi(account, auth=TqAuth("快期账户", "账户密码")) + +其中 `account_id` 通过 :py:class:`~tqsdk.O32Account` 传入,内部会拼接成 ``用户.基金.资产单元.组合`` + +`td_front_url` / `mc_front_url` 为柜台方提供的前置地址,`license_file` 为本地许可证文件位置 \ No newline at end of file diff --git a/doc/reference/index.rst b/doc/reference/index.rst index 4d5984f2..8e704fe5 100644 --- a/doc/reference/index.rst +++ b/doc/reference/index.rst @@ -27,6 +27,7 @@ TqSdk 模块参考 tqsdk.tqrohon.rst tqsdk.tqjees.rst tqsdk.tqyida.rst + tqsdk.tqo32.rst tqsdk.sim.rst .. toctree:: diff --git a/doc/reference/tqsdk.tqo32.rst b/doc/reference/tqsdk.tqo32.rst new file mode 100644 index 00000000..8528f2a4 --- /dev/null +++ b/doc/reference/tqsdk.tqo32.rst @@ -0,0 +1,10 @@ +.. _tqsdk.tqo32: + +tqsdk.TqO32 - 恒生 O32 交易类 +------------------------------------------------------------------ +.. autoclass:: tqsdk.TqO32 + :members: + :inherited-members: + +.. autoclass:: tqsdk.O32Account + :members: diff --git a/doc/version.rst b/doc/version.rst index 4252fe06..491fc1f2 100644 --- a/doc/version.rst +++ b/doc/version.rst @@ -2,6 +2,12 @@ 版本变更 ============================= +3.9.4 (2026/04/17) + +* 新增: 支持恒生 O32 柜台,详情参考 :py:class:`~tqsdk.TqO32` +* docs: 优化文档 + + 3.9.3 (2026/04/14) * 新增: :py:class:`~tqsdk.objs.Quote` 增加 :py:class:`~tqsdk.objs.Quote.open_limit` 属性,返回合约日内开仓限额,目前只支持期货合约 diff --git a/setup.py b/setup.py index 2aad0fb0..29c5462c 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setuptools.setup( name='tqsdk', - version="3.9.3", + version="3.9.4", description='TianQin SDK', author='TianQin', author_email='tianqincn@gmail.com', diff --git a/tqsdk/__init__.py b/tqsdk/__init__.py index 017f157d..8cf58607 100644 --- a/tqsdk/__init__.py +++ b/tqsdk/__init__.py @@ -4,7 +4,7 @@ name = "tqsdk" from tqsdk.api import TqApi -from tqsdk.tradeable import TqAccount, TqZq, TqKq, TqKqStock, TqSim, TqSimStock, TqCtp, TqRohon, TqJees, TqYida, TqTradingUnit +from tqsdk.tradeable import TqAccount, TqZq, TqKq, TqKqStock, TqSim, TqSimStock, TqCtp, TqRohon, TqJees, TqYida, TqO32, O32Account, TqTradingUnit from tqsdk.auth import TqAuth from tqsdk.channel import TqChan from tqsdk.backtest import TqBacktest, TqReplay diff --git a/tqsdk/__version__.py b/tqsdk/__version__.py index 5db0b18f..b6ad6ae4 100644 --- a/tqsdk/__version__.py +++ b/tqsdk/__version__.py @@ -1 +1 @@ -__version__ = '3.9.3' +__version__ = '3.9.4' diff --git a/tqsdk/api.py b/tqsdk/api.py index 022b91ff..a7e334e8 100644 --- a/tqsdk/api.py +++ b/tqsdk/api.py @@ -72,7 +72,7 @@ from tqsdk.risk_rule import TqRiskRule from tqsdk.ins_schema import ins_schema, basic, derivative, future, option from tqsdk.symbols import TqSymbols -from tqsdk.tradeable import TqAccount, TqZq, TqKq, TqKqStock, TqSim, TqSimStock, BaseSim, BaseOtg, TqCtp, TqRohon, TqJees, TqYida, TqTradingUnit +from tqsdk.tradeable import TqAccount, TqZq, TqKq, TqKqStock, TqSim, TqSimStock, BaseSim, BaseOtg, TqCtp, TqRohon, TqJees, TqYida, TqO32, TqTradingUnit from tqsdk.trading_status import TqTradingStatus from tqsdk.tqwebhelper import TqWebHelper from tqsdk.utils import _generate_uuid, _query_for_quote, BlockManagerUnconsolidated, _quotes_add_night, _bisect_value, \ @@ -86,7 +86,7 @@ # 在 python 文档中对 type alias 的定义有多种:TypeAliasType, TypeAlias 以及 simple assignment https://docs.python.org/3.13/library/typing.html#type-aliases # Union 类型支持嵌套 Union 类型,但是不支持嵌套 Union TypeAliasType 类型:https://docs.python.org/3.13/library/typing.html#typing.Union # 但是 Union 文档没有明说是否支持嵌套 Union simple assignment 类型,从实现上看,目前所有版本都支持(最新 3.13) -UnionTradeable = Union[TqAccount, TqKq, TqZq, TqKqStock, TqSim, TqSimStock, TqCtp, TqRohon, TqJees, TqYida, TqTradingUnit] +UnionTradeable = Union[TqAccount, TqKq, TqZq, TqKqStock, TqSim, TqSimStock, TqCtp, TqRohon, TqJees, TqYida, TqO32, TqTradingUnit] class TqApi(TqBaseApi): @@ -106,7 +106,7 @@ def __init__(self, account: Optional[Union[TqMultiAccount, UnionTradeable]] = No 创建天勤接口实例 Args: - account (None/TqAccount/TqKq/TqKqStock/TqSim/TqSimStock/TqZq/TqMultiAccount): [可选]交易账号: + account (None/TqAccount/TqKq/TqKqStock/TqSim/TqSimStock/TqZq/TqCtp/TqRohon/TqJees/TqYida/TqO32/TqTradingUnit/TqMultiAccount): [可选]交易账号: * None: 账号将根据环境变量决定, 默认为 :py:class:`~tqsdk.TqSim` * :py:class:`~tqsdk.TqAccount` : 使用实盘账号, 直连行情和交易服务器, 需提供期货公司/帐号/密码 @@ -129,12 +129,15 @@ def __init__(self, account: Optional[Union[TqMultiAccount, UnionTradeable]] = No * :py:class:`~tqsdk.TqYida` : 使用易达账号 + * :py:class:`~tqsdk.TqO32` : 使用恒生 O32 账号 + * :py:class:`~tqsdk.TqTradingUnit` : 使用交易单元账号 * :py:class:`~tqsdk.TqMultiAccount` : 多账户列表,列表中支持 :py:class:`~tqsdk.TqAccount`、:py:class:`~tqsdk.TqKq`、:py:class:`~tqsdk.TqKqStock`、 :py:class:`~tqsdk.TqSim`、:py:class:`~tqsdk.TqSimStock`、:py:class:`~tqsdk.TqZq`、 :py:class:`~tqsdk.TqRohon`、:py:class:`~tqsdk.TqJees`、:py:class:`~tqsdk.TqYida`、 + :py:class:`~tqsdk.TqO32`、 :py:class:`~tqsdk.TqTradingUnit` 和 :py:class:`~tqsdk.TqCtp` 中的 0 至 N 个或者组合 auth (TqAuth/str): [必填]用户快期账户: diff --git a/tqsdk/multiaccount.py b/tqsdk/multiaccount.py index 80aee8c3..99def63f 100644 --- a/tqsdk/multiaccount.py +++ b/tqsdk/multiaccount.py @@ -35,7 +35,7 @@ def __init__(self, accounts: Optional[List['UnionTradeable']] = None): 创建 TqMultiAccount 实例 Args: - accounts (List[Union[TqAccount, TqKq, TqKqStock, TqSim, TqSimStock, TqZq, TqCtp, TqRohon, TqJees, TqYida, TqTradingUnit]]): [可选] 多账户列表, 若未指定任何账户, 则为 [TqSim()] + accounts (List[Union[TqAccount, TqKq, TqKqStock, TqSim, TqSimStock, TqZq, TqCtp, TqRohon, TqJees, TqYida, TqO32, TqTradingUnit]]): [可选] 多账户列表, 若未指定任何账户, 则为 [TqSim()] Example1:: diff --git a/tqsdk/risk_rule.py b/tqsdk/risk_rule.py index 3813f90d..b0a6dadf 100644 --- a/tqsdk/risk_rule.py +++ b/tqsdk/risk_rule.py @@ -77,7 +77,7 @@ def __init__(self, api: 'TqApi', open_counts_limit: int, symbol: Union[str, List * str: 一个合约代码 * list of str: 合约代码列表 - account (TqAccount/TqKq/TqZq/TqKqStock/TqSim/TqSimStock/TqCtp/TqRohon/TqJees/TqYida/TqTradingUnit): [可选] 指定发送下单指令的账户实例, 多账户模式下,该参数必须指定 + account (TqAccount/TqKq/TqZq/TqKqStock/TqSim/TqSimStock/TqCtp/TqRohon/TqJees/TqYida/TqO32/TqTradingUnit): [可选] 指定发送下单指令的账户实例, 多账户模式下,该参数必须指定 Example1:: @@ -151,7 +151,7 @@ def __init__(self, api: 'TqApi', open_volumes_limit: int, symbol: Union[str, Lis * str: 一个合约代码 * list of str: 合约代码列表 - account (TqAccount/TqKq/TqZq/TqKqStock/TqSim/TqSimStock/TqCtp/TqRohon/TqJees/TqYida/TqTradingUnit): [可选] 指定发送下单指令的账户实例, 多账户模式下,该参数必须指定 + account (TqAccount/TqKq/TqZq/TqKqStock/TqSim/TqSimStock/TqCtp/TqRohon/TqJees/TqYida/TqO32/TqTradingUnit): [可选] 指定发送下单指令的账户实例, 多账户模式下,该参数必须指定 Example1:: @@ -248,7 +248,7 @@ def __init__(self, api: 'TqApi', open_volumes_limit: int, symbol: Union[str, Lis * str: 一个合约代码 * list of str: 合约代码列表 - account (TqAccount/TqKq/TqZq/TqKqStock/TqSim/TqSimStock/TqCtp/TqRohon/TqJees/TqYida/TqTradingUnit): [可选] 指定发送下单指令的账户实例, 多账户模式下,该参数必须指定 + account (TqAccount/TqKq/TqZq/TqKqStock/TqSim/TqSimStock/TqCtp/TqRohon/TqJees/TqYida/TqO32/TqTradingUnit): [可选] 指定发送下单指令的账户实例, 多账户模式下,该参数必须指定 Example:: @@ -328,7 +328,7 @@ def __init__(self, api: 'TqApi', limit_per_second: int, exchange_id: Union[str, * str: 指定交易所代码,如 "SHFE", "DCE", "CZCE", "CFFEX" 等 * list of str: 交易所代码列表,如 ["DCE", "SHFE"],每个交易所分别限制 - account (TqAccount/TqKq/TqZq/TqKqStock/TqSim/TqSimStock/TqCtp/TqRohon/TqJees/TqYida/TqTradingUnit): [可选] 指定发送下单指令的账户实例, 多账户模式下,该参数必须指定 + account (TqAccount/TqKq/TqZq/TqKqStock/TqSim/TqSimStock/TqCtp/TqRohon/TqJees/TqYida/TqO32/TqTradingUnit): [可选] 指定发送下单指令的账户实例, 多账户模式下,该参数必须指定 Example1:: diff --git a/tqsdk/tradeable/__init__.py b/tqsdk/tradeable/__init__.py index f807a726..4db0f9c4 100644 --- a/tqsdk/tradeable/__init__.py +++ b/tqsdk/tradeable/__init__.py @@ -5,6 +5,6 @@ from tqsdk.tradeable.otg.base_otg import BaseOtg -from tqsdk.tradeable.otg import TqAccount, TqZq, TqKq, TqKqStock, TqCtp, TqRohon, TqJees, TqYida, TqTradingUnit +from tqsdk.tradeable.otg import TqAccount, TqZq, TqKq, TqKqStock, TqCtp, TqRohon, TqJees, TqYida, TqO32, O32Account, TqTradingUnit from tqsdk.tradeable.sim.basesim import BaseSim from tqsdk.tradeable.sim import TqSim, TqSimStock diff --git a/tqsdk/tradeable/otg/__init__.py b/tqsdk/tradeable/otg/__init__.py index ef05bb17..93506a46 100644 --- a/tqsdk/tradeable/otg/__init__.py +++ b/tqsdk/tradeable/otg/__init__.py @@ -10,4 +10,5 @@ from tqsdk.tradeable.otg.tqrohon import TqRohon from tqsdk.tradeable.otg.tqjees import TqJees from tqsdk.tradeable.otg.tqyida import TqYida +from tqsdk.tradeable.otg.tqo32 import TqO32, O32Account from tqsdk.tradeable.otg.tqtradingunit import TqTradingUnit diff --git a/tqsdk/tradeable/otg/tqo32.py b/tqsdk/tradeable/otg/tqo32.py new file mode 100644 index 00000000..0a410a9b --- /dev/null +++ b/tqsdk/tradeable/otg/tqo32.py @@ -0,0 +1,124 @@ +# -*- coding:utf-8 -*- +__author__ = 'chenli' + +import hashlib +from dataclasses import dataclass + +from tqsdk.tradeable.otg.base_otg import BaseOtg +from tqsdk.tradeable.mixin import FutureMixin + + +@dataclass +class O32Account: + user: str # 用户 + fund: str # 基金 + asset_unit: str # 资产单元 + portfolio: str # 组合 + + def __post_init__(self) -> None: + if not isinstance(self.user, str): + raise Exception("user 参数类型应该是 str") + if not isinstance(self.fund, str): + raise Exception("fund 参数类型应该是 str") + if not isinstance(self.asset_unit, str): + raise Exception("asset_unit 参数类型应该是 str") + if not isinstance(self.portfolio, str): + raise Exception("portfolio 参数类型应该是 str") + + @property + def user_name(self) -> str: + return ".".join([self.user, self.fund, self.asset_unit, self.portfolio]) + + +class TqO32(BaseOtg, FutureMixin): + """恒生 O32 账户类""" + + def __init__(self, account_id: O32Account, password: str, td_front_url: str, mc_front_url: str, + license_file: str, auth_code: str) -> None: + """ + 创建恒生 O32 账户实例 + + Args: + account_id (O32Account): O32 组合账户 + + password (str): O32 用户密码 + + td_front_url (str): O32 交易前置地址,格式如 112.54.165.180:9003 + + mc_front_url (str): O32 查询前置地址,格式如 110.54.163.160:8003 + + license_file (str): O32 许可证文件绝对路径 + + auth_code (str): O32 授权码 + + Example1:: + + from tqsdk import TqApi, TqAuth, TqO32, O32Account + + def create_account(): + return TqO32( + account_id=O32Account(user="用户", fund="基金", asset_unit="资产单元", portfolio="组合"), + password="password", + td_front_url="trade_front_host:trade_front_port", + mc_front_url="query_front_host:query_front_port", + license_file="/path/to/license.dat", + auth_code="auth_code", + ) + + with TqApi(account=create_account(), auth=TqAuth("快期账户", "账户密码")) as api: + account_info = api.get_account() + positions = api.get_position() + + print("当前账户信息:", account_info) + print("当前持仓信息:", positions) + + 注意: + 1. 使用 TqO32 账户需要安装 tqsdk_zq_otg 包: pip install -U tqsdk_zq_otg + 2. td_front_url、mc_front_url、license_file 和 auth_code 信息需要向柜台方获取 + + """ + if not isinstance(account_id, O32Account): + raise Exception("account_id 参数类型应该是 O32Account") + if not isinstance(td_front_url, str): + raise Exception("td_front_url 参数类型应该是 str") + if not isinstance(mc_front_url, str): + raise Exception("mc_front_url 参数类型应该是 str") + if not isinstance(license_file, str): + raise Exception("license_file 参数类型应该是 str") + if not isinstance(auth_code, str): + raise Exception("auth_code 参数类型应该是 str") + self._td_front_url = td_front_url + self._mc_front_url = mc_front_url + self._license_file = license_file + self._auth_code = auth_code + if not self._license_file: + raise Exception("license_file 参数不能为空字符串") + super(TqO32, self).__init__(broker_id="", account_id=account_id.user_name, password=password, td_url="zqotg://127.0.0.1:0/trade") + + @property + def _account_auth(self): + return { + "feature": "tq_direct", + "account_id": self._account_id, + "auto_add": True, + } + + def _get_account_key(self): + s = self._broker_id + self._account_id + s += self._td_front_url if self._td_front_url else "" + s += self._mc_front_url if self._mc_front_url else "" + s += self._license_file if self._license_file else "" + return hashlib.md5(s.encode('utf-8')).hexdigest() + + async def _send_login_pack(self): + req = { + "aid": "req_login", + "backend": "o32", + "user_name": self._account_id, + "password": self._password, + "trading_fronts": [self._td_front_url, self._mc_front_url], + "license_file_addr": self._license_file, + "auth_code": self._auth_code, + "app_id": "tqsdk_o32", + } + await self._td_send_chan.send(req)