Skip to content

Commit 4f6b37c

Browse files
shinny-packshinny-mayanqiong
authored andcommitted
Update Version 3.5.10
1 parent 1734bd5 commit 4f6b37c

15 files changed

+667
-42
lines changed

PKG-INFO

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Metadata-Version: 2.1
22
Name: tqsdk
3-
Version: 3.5.9
3+
Version: 3.5.10
44
Summary: TianQin SDK
55
Home-page: https://www.shinnytech.com/tqsdk
66
Author: TianQin

doc/conf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@
4848
# built documents.
4949
#
5050
# The short X.Y version.
51-
version = u'3.5.9'
51+
version = u'3.5.10'
5252
# The full version, including alpha/beta/rc tags.
53-
release = u'3.5.9'
53+
release = u'3.5.10'
5454

5555
# The language for content autogenerated by Sphinx. Refer to documentation
5656
# for a list of supported languages.

doc/version.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
版本变更
44
=============================
5+
3.5.10 (2024/05/27)
6+
7+
* 修复:某些情况下网络连接发生超时错误时,可能无法重连的问题
8+
* 优化::py:meth:`~tqsdk.TqApi.get_kline_serial`、:py:meth:`~tqsdk.TqApi.get_tick_serial` 在请求没有任何成交数据的合约时能够及时退出,避免等待超时
9+
* 更新::py:meth:`~tqsdk.TqApi.get_kline_serial`、:py:meth:`~tqsdk.TqApi.get_tick_serial` 接口去掉 ``chart_id`` 参数,避免用户错误用法导致服务器收到大量重复请求
10+
11+
512
3.5.9 (2024/05/09)
613

714
* 增加::py:meth:`~tqsdk.TqApi.query_all_level_finance_options` 增加中金所股指期权标的

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
setuptools.setup(
1010
name='tqsdk',
11-
version="3.5.9",
11+
version="3.5.10",
1212
description='TianQin SDK',
1313
author='TianQin',
1414
author_email='[email protected]',

tqsdk/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '3.5.9'
1+
__version__ = '3.5.10'

tqsdk/api.py

Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@
7474
from tqsdk.tradeable import TqAccount, TqZq, TqKq, TqKqStock, TqSim, TqSimStock, BaseSim, BaseOtg
7575
from tqsdk.trading_status import TqTradingStatus
7676
from tqsdk.tqwebhelper import TqWebHelper
77-
from tqsdk.utils import _generate_uuid, _query_for_quote, BlockManagerUnconsolidated, _quotes_add_night, _bisect_value
77+
from tqsdk.utils import _generate_uuid, _query_for_quote, BlockManagerUnconsolidated, _quotes_add_night, _bisect_value, \
78+
deprecated_chart_id
7879
from tqsdk.utils_symbols import _symbols_to_quotes
7980
from tqsdk.tafunc import get_dividend_df, get_dividend_factor
8081
from .__version__ import __version__
@@ -584,9 +585,10 @@ async def _handle_trading_status(self, symbol, ts):
584585
if ts.trade_status != "":
585586
return ts
586587

588+
@deprecated_chart_id("symbol", "duration_seconds", "data_length", "adj_type")
587589
# ----------------------------------------------------------------------
588590
def get_kline_serial(self, symbol: Union[str, List[str]], duration_seconds: int, data_length: int = 200,
589-
chart_id: Optional[str] = None, adj_type: Union[str, None] = None) -> pd.DataFrame:
591+
adj_type: Union[str, None] = None, **kwargs) -> pd.DataFrame:
590592
"""
591593
获取k线序列数据
592594
@@ -603,11 +605,11 @@ def get_kline_serial(self, symbol: Union[str, List[str]], duration_seconds: int,
603605
data_length (int): 需要获取的序列长度。默认200根, 返回的K线序列数据是从当前最新一根K线开始往回取data_length根。\
604606
每个序列最大支持请求 8000 个数据
605607
606-
chart_id (str): [可选]指定序列id, 默认由 api 自动生成
607-
608608
adj_type (str/None): [可选]指定复权类型,默认为 None。adj_type 参数只对股票和基金类型合约有效。\
609609
"F" 表示前复权;"B" 表示后复权;None 表示不做处理。
610610
611+
chart_id (str): [Deprecated] 由 api 自动生成,此参数不再使用
612+
611613
**注:关于传入合约代码列表 获取多合约K线的说明:**
612614
613615
1. 主合约的字段名为原始K线数据字段,从第一个副合约开始,字段名在原始字段后加数字,如第一个副合约的开盘价为 "open1" , 第二个副合约的收盘价为 "close2"。
@@ -699,18 +701,18 @@ def get_kline_serial(self, symbol: Union[str, List[str]], duration_seconds: int,
699701
if data_length > 8964:
700702
data_length = 8964
701703
dur_id = duration_seconds * 1000000000
702-
request = (tuple(symbol), duration_seconds, data_length, adj_type, chart_id) # request 中 symbols 为 tuple 序列
704+
request = (tuple(symbol), duration_seconds, data_length, adj_type) # request 中 symbols 为 tuple 序列
703705
serial = self._requests["klines"].get(request, None)
704706
pack = {
705707
"aid": "set_chart",
706-
"chart_id": chart_id if chart_id is not None else _generate_uuid("PYSDK_realtime"),
708+
"chart_id": _generate_uuid("PYSDK_realtime"),
707709
"ins_list": ",".join(symbol),
708710
"duration": dur_id,
709711
"view_width": data_length if len(symbol) == 1 else 8964,
710712
# 如果同时订阅了两个以上合约K线,初始化数据时默认获取 1w 根K线(初始化完成后修改指令为设定长度)
711-
}
713+
} if serial is None else {}
712714
# 将数据权转移给TqChan时其所有权也随之转移,因pack还需要被用到,所以传入副本
713-
task = self.create_task(self._get_serial_async(symbol, chart_id, serial, pack.copy()), _caller_api=True)
715+
task = self.create_task(self._get_serial_async(symbol, pack.copy()), _caller_api=True)
714716
if serial is None:
715717
serial = self._init_serial([_get_obj(self._data, ["klines", s, str(dur_id)]) for s in symbol],
716718
data_length, self._prototype["klines"]["*"]["*"]["data"]["@"], adj_type)
@@ -729,16 +731,17 @@ def get_kline_serial(self, symbol: Union[str, List[str]], duration_seconds: int,
729731
raise TqTimeoutError("获取 %s (%d) 的K线超时,请检查客户端及网络是否正常" % (symbol, duration_seconds))
730732
return serial["df"]
731733

732-
async def _get_serial_async(self, symbol, chart_id, serial, pack):
734+
async def _get_serial_async(self, symbol, pack):
733735
await self._ensure_symbol_async(symbol)
734736
self._auth._has_md_grants(symbol)
735737
# 判断用户是否指定了 chart_id(参数), 如果指定了,则一定会发送新的请求。
736-
if serial is None or chart_id is not None:
738+
if pack:
737739
self._send_pack(pack)
738740

739741
# ----------------------------------------------------------------------
740-
def get_tick_serial(self, symbol: str, data_length: int = 200, chart_id: Optional[str] = None,
741-
adj_type: Union[str, None] = None) -> pd.DataFrame:
742+
@deprecated_chart_id("symbol", "data_length", "adj_type")
743+
def get_tick_serial(self, symbol: str, data_length: int = 200, adj_type: Union[str, None] = None,
744+
**kwargs) -> pd.DataFrame:
742745
"""
743746
获取tick序列数据
744747
@@ -749,11 +752,11 @@ def get_tick_serial(self, symbol: str, data_length: int = 200, chart_id: Optiona
749752
750753
data_length (int): 需要获取的序列长度。每个序列最大支持请求 8000 个数据
751754
752-
chart_id (str): [可选]指定序列id, 默认由 api 自动生成
753-
754755
adj_type (str/None): [可选]指定复权类型,默认为 None。adj_type 参数只对股票和基金类型合约有效。\
755756
"F" 表示前复权;"B" 表示后复权;None 表示不做处理。
756757
758+
chart_id (str): [Deprecated] 由 api 自动生成,此参数不再使用
759+
757760
Returns:
758761
pandas.DataFrame: 本函数总是返回一个 pandas.DataFrame 实例. 行数=data_length, 包含以下列:
759762
@@ -798,17 +801,17 @@ def get_tick_serial(self, symbol: str, data_length: int = 200, chart_id: Optiona
798801
adj_type = adj_type[0] if adj_type else adj_type
799802
if data_length > 8964:
800803
data_length = 8964
801-
request = (symbol, data_length, adj_type, chart_id)
804+
request = (symbol, data_length, adj_type)
802805
serial = self._requests["ticks"].get(request, None)
803806
pack = {
804807
"aid": "set_chart",
805-
"chart_id": chart_id if chart_id is not None else _generate_uuid("PYSDK_realtime"),
808+
"chart_id": _generate_uuid("PYSDK_realtime"),
806809
"ins_list": symbol,
807810
"duration": 0,
808811
"view_width": data_length,
809-
}
812+
} if serial is None else {}
810813
# pack 的副本数据和所有权转移给TqChan
811-
task = self.create_task(self._get_serial_async(symbol, chart_id, serial, pack.copy()), _caller_api=True)
814+
task = self.create_task(self._get_serial_async(symbol, pack.copy()), _caller_api=True)
812815
if serial is None:
813816
serial = self._init_serial([_get_obj(self._data, ["ticks", symbol])], data_length,
814817
self._prototype["ticks"]["*"]["data"]["@"], adj_type)
@@ -3425,11 +3428,11 @@ def _update_serial_single(self, serial):
34253428
ext[serial["width"] - shift:] = np.nan
34263429
serial["update_row"] = max(serial["width"] - shift - 1, 0)
34273430
else:
3428-
left_id = serial["chart"].get("left_id", -1)
3429-
right_id = serial["chart"].get("right_id", -1)
3430-
if (left_id != -1 or right_id != -1) and not serial["chart"].get("more_data", True) and serial["root"][
3431-
0].get("last_id", -1) != -1:
3431+
if serial["chart"].get("ready", False) is True:
34323432
serial["init"] = True
3433+
if serial["root"][0].get("last_id", -1) == -1:
3434+
# 该 kline 没有任何数据,直接退出
3435+
return
34333436
symbol = serial["chart"]["ins_list"].split(",")[0] # 合约列表
34343437
quote = self._data.quotes.get(symbol, {})
34353438
duration = serial["chart"]["duration"] # 周期
@@ -3476,18 +3479,19 @@ def _ensure_dividend_factor(self, symbol):
34763479
def _update_serial_multi(self, serial):
34773480
"""处理订阅多个合约时K线的数据更新"""
34783481
# 判断初始化数据是否接收完全, 否: 则返回
3479-
left_id = serial["chart"].get("left_id", -1) # 主合约的left_id
3480-
right_id = serial["chart"].get("right_id", -1) # 主合约的right_id
3481-
if (left_id == -1 and right_id == -1) or serial["chart"].get("more_data", True):
3482+
if serial["chart"].get("ready", False) is False:
34823483
return
3483-
for root in serial["root"]:
3484-
if root.get("last_id", -1) == -1:
3485-
return
34863484

3485+
left_id = serial["chart"].get("left_id", -1) # 主合约的left_id
3486+
right_id = serial["chart"].get("right_id", -1) # 主合约的right_id
34873487
array = serial["array"]
34883488
ins_list = serial["chart"]["ins_list"].split(",") # 合约列表
34893489

34903490
if not serial["init"]: # 未初始化完成则进行初始化处理. init完成状态: 订阅K线后获取所有数据并填满df序列.
3491+
if serial["root"][0].get("last_id", -1) == -1:
3492+
serial["init"] = True
3493+
# 该 kline 没有任何数据,直接退出
3494+
return
34913495
update_row = serial["width"] - 1 # 起始更新数据行,局部变量
34923496
current_id = right_id # 当前数据指针
34933497
while current_id >= left_id and current_id >= 0 and update_row >= 0: # 如果当前id >= left_id 且 数据尚未填满width长度
@@ -3527,12 +3531,14 @@ def _update_serial_multi(self, serial):
35273531
"view_width": len(array) if len(array) >= 30 else 30,
35283532
})
35293533
else: # 正常行情更新处理
3534+
if serial["root"][0].get("last_id", -1) == -1:
3535+
# 该 kline 没有任何数据,直接退出
3536+
return
35303537
serial["update_row"] = serial["width"] - 1
35313538
new_kline_range = None
35323539
new_data_index = serial["width"] - 1 # 记录更新数据位置
35333540
# 从 left_id 或 已有数据的最后一个 last_id 到服务器发回的最新数据的 last_id: 每次循环更新一行。max: 避免数据更新过多时产生大量多余循环判断
3534-
for i in range(max(serial["chart"].get("left_id", -1), int(array[-1, 1])),
3535-
serial["root"][0].get("last_id", -1) + 1):
3541+
for i in range(max(left_id, int(array[-1, 1])), serial["root"][0].get("last_id", -1) + 1):
35363542
# 如果某条主K线和某条副K线之间的 binding 映射数据存在: 则对应副合约数据也存在; 遍历主合约与所有副合约的binding信息, 如果都存在, 则将此K线填入array保存.
35373543
master_item = serial["root"][0]["data"][str(i)] # 主合约数据
35383544
# array更新的一行数据: 初始化填入主合约数据

tqsdk/backtest/backtest.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -169,9 +169,10 @@ async def _run(self, api, sim_send_chan, sim_recv_chan, md_send_chan, md_recv_ch
169169
self._diffs.append({
170170
"charts": {
171171
pack["chart_id"]: {
172-
# 两个id设置为0:保证api在回测中判断此值时不是-1,即直接通过对数据接收完全的验证
173-
"left_id": 0,
174-
"right_id": 0,
172+
# 回测中 ready 置为 True,保证api在回测中直接通过对数据接收完全的验证
173+
"left_id": -1,
174+
"right_id": -1,
175+
"ready": True,
175176
"more_data": False, # 直接发送False给api,表明数据发送完全,使api中通过数据接收完全的验证
176177
"state": pack
177178
}

tqsdk/connect.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,8 @@ async def _run(self, api, url, send_chan, recv_chan):
201201
# 希望做到的效果是遇到网络问题可以断线重连, 但是可能抛出的例外太多了(TimeoutError,socket.gaierror等), 又没有文档或工具可以理出 try 代码中所有可能遇到的例外
202202
# 而这里的 except 又需要处理所有子函数及子函数的子函数等等可能抛出的例外, 因此这里只能遇到问题之后再补, 并且无法避免 false positive 和 false negative
203203
except (websockets.exceptions.ConnectionClosed, websockets.exceptions.InvalidStatusCode, websockets.exceptions.InvalidURI,
204-
websockets.exceptions.InvalidState, websockets.exceptions.ProtocolError, OSError, EOFError,
204+
websockets.exceptions.InvalidState, websockets.exceptions.ProtocolError, asyncio.TimeoutError,
205+
OSError, EOFError,
205206
TqBacktestPermissionError) as e:
206207
in_ops_time = _cst_now().hour == 19 and 0 <= _cst_now().minute <= 30
207208
# 发送网络连接断开的通知,code = 2019112911

tqsdk/utils.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
# -*- coding:utf-8 -*-
33
__author__ = 'yanqiong'
44

5+
import functools
56
import os
67
import random
78
import secrets
89
from bisect import bisect_right
10+
from typing import Callable
911

1012
from sgqlc.operation import Operation
1113
from pandas.core.internals import BlockManager
@@ -197,3 +199,25 @@ async def _get_dividend_factor(api, quote, start_dt_nano, end_dt_nano, chart_id_
197199
df["factor"].fillna(1.0, inplace=True)
198200
return df
199201

202+
203+
def deprecated_chart_id(*expect_args) -> Callable:
204+
"""
205+
废弃 chart_id 参数后,希望支持用户不修改如下的代码
206+
207+
api.get_kline_serial(symbol, 3600, 1000, "xxx", "F")
208+
api.get_tick_serial(symbol, 200, "xxx", "F")
209+
"""
210+
211+
def decorator(f):
212+
@functools.wraps(f)
213+
def wrapper(self, *args, **kwargs):
214+
kwargs.pop("chart_id", None)
215+
if len(args) == len(expect_args) + 1:
216+
# 用户传入的 args 参数比 expect_args 多一个,说明用户主动在 position args 位置上传入了 chart_id 参数,需要去掉
217+
# 如果长度一样或者比 expect_args 少,就按照用户传入原本的参数处理
218+
# 倒数第二个是 chart_id 参数
219+
args = args[:-2] + args[-1:]
220+
return f(self, *args, **kwargs)
221+
return wrapper
222+
223+
return decorator

tqsdk/web/css/chunk-vendors.1f44729d.css

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tqsdk/web/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><!--[if IE]><link rel="icon" href="web/favicon.ico"><![endif]--><title>tqsdk-python-web</title><link href="web/css/app.d72d8978.css" rel="preload" as="style"><link href="web/css/chunk-vendors.c93e9127.css" rel="preload" as="style"><link href="web/js/app.2c843c86.js" rel="preload" as="script"><link href="web/js/chunk-vendors.d7fceff6.js" rel="preload" as="script"><link href="web/css/chunk-vendors.c93e9127.css" rel="stylesheet"><link href="web/css/app.d72d8978.css" rel="stylesheet"><link rel="icon" type="image/png" sizes="32x32" href="web/img/icons/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="web/img/icons/favicon-16x16.png"><link rel="manifest" href="web/manifest.json"><meta name="theme-color" content="#4DBA87"><meta name="apple-mobile-web-app-capable" content="no"><meta name="apple-mobile-web-app-status-bar-style" content="default"><meta name="apple-mobile-web-app-title" content="tqsdk-python-web"><link rel="apple-touch-icon" href="web/img/icons/apple-touch-icon-152x152.png"><link rel="mask-icon" href="web/img/icons/safari-pinned-tab.svg" color="#4DBA87"><meta name="msapplication-TileImage" content="web/img/icons/msapplication-icon-144x144.png"><meta name="msapplication-TileColor" content="#000000"></head><body><noscript><strong>We're sorry but tqsdk web doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><script>const TqsdkAddress = location.host
1+
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><!--[if IE]><link rel="icon" href="web/favicon.ico"><![endif]--><title>tqsdk-python-web</title><link href="web/css/app.d72d8978.css" rel="preload" as="style"><link href="web/css/chunk-vendors.1f44729d.css" rel="preload" as="style"><link href="web/js/app.5eb857be.js" rel="preload" as="script"><link href="web/js/chunk-vendors.fc3d6c6c.js" rel="preload" as="script"><link href="web/css/chunk-vendors.1f44729d.css" rel="stylesheet"><link href="web/css/app.d72d8978.css" rel="stylesheet"><link rel="icon" type="image/png" sizes="32x32" href="web/img/icons/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="web/img/icons/favicon-16x16.png"><link rel="manifest" href="web/manifest.json"><meta name="theme-color" content="#4DBA87"><meta name="apple-mobile-web-app-capable" content="no"><meta name="apple-mobile-web-app-status-bar-style" content="default"><meta name="apple-mobile-web-app-title" content="tqsdk-python-web"><link rel="apple-touch-icon" href="web/img/icons/apple-touch-icon-152x152.png"><link rel="mask-icon" href="web/img/icons/safari-pinned-tab.svg" color="#4DBA87"><meta name="msapplication-TileImage" content="web/img/icons/msapplication-icon-144x144.png"><meta name="msapplication-TileColor" content="#000000"></head><body><noscript><strong>We're sorry but tqsdk web doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><script>const TqsdkAddress = location.host
22
const FetchUrl = '/url'</script><script>const GetTqsdkUrl = function () {
33
return new Promise(function (resolve, reject) {
44
fetch(FetchUrl).then(function(response) {
@@ -7,4 +7,4 @@
77
})
88
});
99
})
10-
}</script><div id="app"></div><script src="web/js/chunk-vendors.d7fceff6.js"></script><script src="web/js/app.2c843c86.js"></script></body></html>
10+
}</script><div id="app"></div><script src="web/js/chunk-vendors.fc3d6c6c.js"></script><script src="web/js/app.5eb857be.js"></script></body></html>

tqsdk/web/js/app.5eb857be.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)