Skip to content

Commit

Permalink
Update Version 3.5.10
Browse files Browse the repository at this point in the history
  • Loading branch information
shinny-pack authored and shinny-mayanqiong committed May 27, 2024
1 parent 1734bd5 commit 4f6b37c
Show file tree
Hide file tree
Showing 15 changed files with 667 additions and 42 deletions.
2 changes: 1 addition & 1 deletion PKG-INFO
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: tqsdk
Version: 3.5.9
Version: 3.5.10
Summary: TianQin SDK
Home-page: https://www.shinnytech.com/tqsdk
Author: TianQin
Expand Down
4 changes: 2 additions & 2 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@
# built documents.
#
# The short X.Y version.
version = u'3.5.9'
version = u'3.5.10'
# The full version, including alpha/beta/rc tags.
release = u'3.5.9'
release = u'3.5.10'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
7 changes: 7 additions & 0 deletions doc/version.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

版本变更
=============================
3.5.10 (2024/05/27)

* 修复:某些情况下网络连接发生超时错误时,可能无法重连的问题
* 优化::py:meth:`~tqsdk.TqApi.get_kline_serial`、:py:meth:`~tqsdk.TqApi.get_tick_serial` 在请求没有任何成交数据的合约时能够及时退出,避免等待超时
* 更新::py:meth:`~tqsdk.TqApi.get_kline_serial`、:py:meth:`~tqsdk.TqApi.get_tick_serial` 接口去掉 ``chart_id`` 参数,避免用户错误用法导致服务器收到大量重复请求


3.5.9 (2024/05/09)

* 增加::py:meth:`~tqsdk.TqApi.query_all_level_finance_options` 增加中金所股指期权标的
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

setuptools.setup(
name='tqsdk',
version="3.5.9",
version="3.5.10",
description='TianQin SDK',
author='TianQin',
author_email='[email protected]',
Expand Down
2 changes: 1 addition & 1 deletion tqsdk/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '3.5.9'
__version__ = '3.5.10'
66 changes: 36 additions & 30 deletions tqsdk/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@
from tqsdk.tradeable import TqAccount, TqZq, TqKq, TqKqStock, TqSim, TqSimStock, BaseSim, BaseOtg
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
from tqsdk.utils import _generate_uuid, _query_for_quote, BlockManagerUnconsolidated, _quotes_add_night, _bisect_value, \
deprecated_chart_id
from tqsdk.utils_symbols import _symbols_to_quotes
from tqsdk.tafunc import get_dividend_df, get_dividend_factor
from .__version__ import __version__
Expand Down Expand Up @@ -584,9 +585,10 @@ async def _handle_trading_status(self, symbol, ts):
if ts.trade_status != "":
return ts

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

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

# ----------------------------------------------------------------------
def get_tick_serial(self, symbol: str, data_length: int = 200, chart_id: Optional[str] = None,
adj_type: Union[str, None] = None) -> pd.DataFrame:
@deprecated_chart_id("symbol", "data_length", "adj_type")
def get_tick_serial(self, symbol: str, data_length: int = 200, adj_type: Union[str, None] = None,
**kwargs) -> pd.DataFrame:
"""
获取tick序列数据
Expand All @@ -749,11 +752,11 @@ def get_tick_serial(self, symbol: str, data_length: int = 200, chart_id: Optiona
data_length (int): 需要获取的序列长度。每个序列最大支持请求 8000 个数据
chart_id (str): [可选]指定序列id, 默认由 api 自动生成
adj_type (str/None): [可选]指定复权类型,默认为 None。adj_type 参数只对股票和基金类型合约有效。\
"F" 表示前复权;"B" 表示后复权;None 表示不做处理。
chart_id (str): [Deprecated] 由 api 自动生成,此参数不再使用
Returns:
pandas.DataFrame: 本函数总是返回一个 pandas.DataFrame 实例. 行数=data_length, 包含以下列:
Expand Down Expand Up @@ -798,17 +801,17 @@ def get_tick_serial(self, symbol: str, data_length: int = 200, chart_id: Optiona
adj_type = adj_type[0] if adj_type else adj_type
if data_length > 8964:
data_length = 8964
request = (symbol, data_length, adj_type, chart_id)
request = (symbol, data_length, adj_type)
serial = self._requests["ticks"].get(request, None)
pack = {
"aid": "set_chart",
"chart_id": chart_id if chart_id is not None else _generate_uuid("PYSDK_realtime"),
"chart_id": _generate_uuid("PYSDK_realtime"),
"ins_list": symbol,
"duration": 0,
"view_width": data_length,
}
} if serial is None else {}
# pack 的副本数据和所有权转移给TqChan
task = self.create_task(self._get_serial_async(symbol, chart_id, serial, pack.copy()), _caller_api=True)
task = self.create_task(self._get_serial_async(symbol, pack.copy()), _caller_api=True)
if serial is None:
serial = self._init_serial([_get_obj(self._data, ["ticks", symbol])], data_length,
self._prototype["ticks"]["*"]["data"]["@"], adj_type)
Expand Down Expand Up @@ -3425,11 +3428,11 @@ def _update_serial_single(self, serial):
ext[serial["width"] - shift:] = np.nan
serial["update_row"] = max(serial["width"] - shift - 1, 0)
else:
left_id = serial["chart"].get("left_id", -1)
right_id = serial["chart"].get("right_id", -1)
if (left_id != -1 or right_id != -1) and not serial["chart"].get("more_data", True) and serial["root"][
0].get("last_id", -1) != -1:
if serial["chart"].get("ready", False) is True:
serial["init"] = True
if serial["root"][0].get("last_id", -1) == -1:
# 该 kline 没有任何数据,直接退出
return
symbol = serial["chart"]["ins_list"].split(",")[0] # 合约列表
quote = self._data.quotes.get(symbol, {})
duration = serial["chart"]["duration"] # 周期
Expand Down Expand Up @@ -3476,18 +3479,19 @@ def _ensure_dividend_factor(self, symbol):
def _update_serial_multi(self, serial):
"""处理订阅多个合约时K线的数据更新"""
# 判断初始化数据是否接收完全, 否: 则返回
left_id = serial["chart"].get("left_id", -1) # 主合约的left_id
right_id = serial["chart"].get("right_id", -1) # 主合约的right_id
if (left_id == -1 and right_id == -1) or serial["chart"].get("more_data", True):
if serial["chart"].get("ready", False) is False:
return
for root in serial["root"]:
if root.get("last_id", -1) == -1:
return

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

if not serial["init"]: # 未初始化完成则进行初始化处理. init完成状态: 订阅K线后获取所有数据并填满df序列.
if serial["root"][0].get("last_id", -1) == -1:
serial["init"] = True
# 该 kline 没有任何数据,直接退出
return
update_row = serial["width"] - 1 # 起始更新数据行,局部变量
current_id = right_id # 当前数据指针
while current_id >= left_id and current_id >= 0 and update_row >= 0: # 如果当前id >= left_id 且 数据尚未填满width长度
Expand Down Expand Up @@ -3527,12 +3531,14 @@ def _update_serial_multi(self, serial):
"view_width": len(array) if len(array) >= 30 else 30,
})
else: # 正常行情更新处理
if serial["root"][0].get("last_id", -1) == -1:
# 该 kline 没有任何数据,直接退出
return
serial["update_row"] = serial["width"] - 1
new_kline_range = None
new_data_index = serial["width"] - 1 # 记录更新数据位置
# 从 left_id 或 已有数据的最后一个 last_id 到服务器发回的最新数据的 last_id: 每次循环更新一行。max: 避免数据更新过多时产生大量多余循环判断
for i in range(max(serial["chart"].get("left_id", -1), int(array[-1, 1])),
serial["root"][0].get("last_id", -1) + 1):
for i in range(max(left_id, int(array[-1, 1])), serial["root"][0].get("last_id", -1) + 1):
# 如果某条主K线和某条副K线之间的 binding 映射数据存在: 则对应副合约数据也存在; 遍历主合约与所有副合约的binding信息, 如果都存在, 则将此K线填入array保存.
master_item = serial["root"][0]["data"][str(i)] # 主合约数据
# array更新的一行数据: 初始化填入主合约数据
Expand Down
7 changes: 4 additions & 3 deletions tqsdk/backtest/backtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,10 @@ async def _run(self, api, sim_send_chan, sim_recv_chan, md_send_chan, md_recv_ch
self._diffs.append({
"charts": {
pack["chart_id"]: {
# 两个id设置为0:保证api在回测中判断此值时不是-1,即直接通过对数据接收完全的验证
"left_id": 0,
"right_id": 0,
# 回测中 ready 置为 True,保证api在回测中直接通过对数据接收完全的验证
"left_id": -1,
"right_id": -1,
"ready": True,
"more_data": False, # 直接发送False给api,表明数据发送完全,使api中通过数据接收完全的验证
"state": pack
}
Expand Down
3 changes: 2 additions & 1 deletion tqsdk/connect.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,8 @@ async def _run(self, api, url, send_chan, recv_chan):
# 希望做到的效果是遇到网络问题可以断线重连, 但是可能抛出的例外太多了(TimeoutError,socket.gaierror等), 又没有文档或工具可以理出 try 代码中所有可能遇到的例外
# 而这里的 except 又需要处理所有子函数及子函数的子函数等等可能抛出的例外, 因此这里只能遇到问题之后再补, 并且无法避免 false positive 和 false negative
except (websockets.exceptions.ConnectionClosed, websockets.exceptions.InvalidStatusCode, websockets.exceptions.InvalidURI,
websockets.exceptions.InvalidState, websockets.exceptions.ProtocolError, OSError, EOFError,
websockets.exceptions.InvalidState, websockets.exceptions.ProtocolError, asyncio.TimeoutError,
OSError, EOFError,
TqBacktestPermissionError) as e:
in_ops_time = _cst_now().hour == 19 and 0 <= _cst_now().minute <= 30
# 发送网络连接断开的通知,code = 2019112911
Expand Down
24 changes: 24 additions & 0 deletions tqsdk/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
# -*- coding:utf-8 -*-
__author__ = 'yanqiong'

import functools
import os
import random
import secrets
from bisect import bisect_right
from typing import Callable

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


def deprecated_chart_id(*expect_args) -> Callable:
"""
废弃 chart_id 参数后,希望支持用户不修改如下的代码
api.get_kline_serial(symbol, 3600, 1000, "xxx", "F")
api.get_tick_serial(symbol, 200, "xxx", "F")
"""

def decorator(f):
@functools.wraps(f)
def wrapper(self, *args, **kwargs):
kwargs.pop("chart_id", None)
if len(args) == len(expect_args) + 1:
# 用户传入的 args 参数比 expect_args 多一个,说明用户主动在 position args 位置上传入了 chart_id 参数,需要去掉
# 如果长度一样或者比 expect_args 少,就按照用户传入原本的参数处理
# 倒数第二个是 chart_id 参数
args = args[:-2] + args[-1:]
return f(self, *args, **kwargs)
return wrapper

return decorator
1 change: 1 addition & 0 deletions tqsdk/web/css/chunk-vendors.1f44729d.css

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions tqsdk/web/index.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!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
<!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
const FetchUrl = '/url'</script><script>const GetTqsdkUrl = function () {
return new Promise(function (resolve, reject) {
fetch(FetchUrl).then(function(response) {
Expand All @@ -7,4 +7,4 @@
})
});
})
}</script><div id="app"></div><script src="web/js/chunk-vendors.d7fceff6.js"></script><script src="web/js/app.2c843c86.js"></script></body></html>
}</script><div id="app"></div><script src="web/js/chunk-vendors.fc3d6c6c.js"></script><script src="web/js/app.5eb857be.js"></script></body></html>
1 change: 1 addition & 0 deletions tqsdk/web/js/app.5eb857be.js

Large diffs are not rendered by default.

Loading

0 comments on commit 4f6b37c

Please sign in to comment.