Skip to content

Commit 138c302

Browse files
shinny-packshinny-mayanqiong
authored andcommitted
Update Version 3.4.11
1 parent 596dada commit 138c302

21 files changed

+176
-134
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.4.10
3+
Version: 3.4.11
44
Summary: TianQin SDK
55
Home-page: https://www.shinnytech.com/tqsdk
66
Author: TianQin

doc/conf.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,17 +40,17 @@
4040

4141
# General information about the project.
4242
project = u'TianQin Python SDK'
43-
copyright = u'2018-2023, TianQin'
43+
copyright = u'2018-2024, TianQin'
4444
author = u'TianQin'
4545

4646
# The version info for the project you're documenting, acts as replacement for
4747
# |version| and |release|, also used in various other places throughout the
4848
# built documents.
4949
#
5050
# The short X.Y version.
51-
version = u'3.4.10'
51+
version = u'3.4.11'
5252
# The full version, including alpha/beta/rc tags.
53-
release = u'3.4.10'
53+
release = u'3.4.11'
5454

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

doc/profession.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ TqSdk 中大部分功能是供用户免费使用的, 同时我们也提供了专
2020

2121
.. figure:: images/how-grafana04.gif
2222

23-
`快期专业版官网地址 <https://www.shinnytech.com/qpro/>`_
23+
`快期专业版官网地址 <https://www.shinnytech.com/qpro>`_
2424

25-
`快期专业版文档地址 <https://publish2.shinnytech.com/doc/qpro/latest/quickstart.html/>`_
25+
`快期专业版文档地址 <https://publish2.shinnytech.com/doc/qpro/latest/quickstart.html>`_
2626

2727
更稳定的行情服务器
2828
-------------------------------------------------

doc/usage/backtest.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ TqSdk回测框架使用一套复杂的规则来推进行情:
202202
规则3: quote按照以下规则更新::
203203

204204
if 策略程序中使用了这个合约的tick序列:
205-
每次tick序列推进时会更新quote的这些字段 datetime/ask&bid_price1/ask&bid_volume1/last_price/highest/lowest/average/volume/amount/open_interest/price_tick/price_decs/volume_multiple/max&min_limit&market_order_volume/underlying_symbol/strike_price
205+
每次tick序列推进时会更新quote的这些字段 datetime/ask&bid_price1至ask&bid_price5/ask&bid_volume1至ask&bid_volume5/last_price/highest/lowest/average/volume/amount/open_interest/price_tick/price_decs/volume_multiple/max&min_limit&market_order_volume/underlying_symbol/strike_price
206206
elif 策略程序中使用了这个合约的K线序列:
207207
每次K线序列推进时会更新quote. 使用 k线生成的 quote 的盘口由收盘价分别加/减一个最小变动单位, 并且 highest/lowest/average/amount 始终为 nan, volume 始终为0.
208208
每次K线序列推进时会更新quote的这些字段 datetime/ask&bid_price1/ask&bid_volume1/last_price/open_interest/price_tick/price_decs/volume_multiple/max&min_limit&market_order_volume/underlying_symbol/strike_price

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.4.11 (2024/01/03)
6+
7+
* 优化:支持天勤在不同时区设置的操作系统上使用。tqsdk 内部时间表示全部使用北京时间。
8+
对于以下接口,用户输入 datetime 类型参数时,如果未指定时区信息,tqsdk 会指定为北京时间;如果指定了时区信息,会转为北京时间,保证 Unix Timestamp 时间戳不变。
9+
:py:meth:`~tqsdk.TqApi.get_kline_data_series`、:py:meth:`~tqsdk.TqApi.get_tick_data_series`、:py:class:`~tqsdk.tools.DataDownloader`、:py:class:`~tqsdk.TqBacktest`。
10+
11+
512
3.4.10 (2023/09/22)
613

714
* 修复:pandas 2.1.0 版本 fillna 、NumericBlock 会报 deprecated warning 的问题

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.4.10",
11+
version="3.4.11",
1212
description='TianQin SDK',
1313
author='TianQin',
1414
author_email='tianqincn@gmail.com',

tqsdk/__version__.py

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

tqsdk/algorithm/time_table_generater.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# -*- coding: utf-8 -*-
33
__author__ = 'mayanqiong'
44

5-
from datetime import datetime, time, timedelta
5+
from datetime import time, timedelta
66
from typing import Optional, Union
77

88
import numpy as np
@@ -12,7 +12,7 @@
1212
from tqsdk.api import TqApi
1313
from tqsdk import utils
1414
from tqsdk.datetime import _get_trading_timestamp, _get_trade_timestamp, _get_trading_day_from_timestamp, \
15-
_datetime_to_timestamp_nano
15+
_datetime_to_timestamp_nano, _timestamp_nano_to_datetime
1616
from tqsdk.rangeset import _rangeset_slice, _rangeset_head
1717
from tqsdk.tradeable import TqAccount, TqKq, TqSim
1818

@@ -214,8 +214,8 @@ def vwap_table(api: TqApi, symbol: str, target_pos: int, duration: float,
214214

215215
# 获取 Kline
216216
klines = api.get_kline_serial(symbol, TIME_CELL, data_length=int(10 * 60 * 60 / TIME_CELL * HISTORY_DAY_LENGTH))
217-
klines["time"] = klines.datetime.apply(lambda x: datetime.fromtimestamp(x // 1000000000).time()) # k线时间
218-
klines["date"] = klines.datetime.apply(lambda x: datetime.fromtimestamp(_get_trading_day_from_timestamp(x) // 1000000000).date()) # k线交易日
217+
klines["time"] = klines.datetime.apply(lambda x: _timestamp_nano_to_datetime(x).time()) # k线时间
218+
klines["date"] = klines.datetime.apply(lambda x: _timestamp_nano_to_datetime(_get_trading_day_from_timestamp(x)).date()) # k线交易日
219219

220220
quote = api.get_quote(symbol)
221221
# 当前交易日完整的交易时间段
@@ -226,16 +226,16 @@ def vwap_table(api: TqApi, symbol: str, target_pos: int, duration: float,
226226
if not trading_timestamp_nano_range[0][0] <= current_timestamp_nano < trading_timestamp_nano_range[-1][1]:
227227
raise Exception("当前时间不在指定的交易时间段内")
228228

229-
current_datetime = datetime.fromtimestamp(current_timestamp_nano//1000000000)
229+
current_datetime = _timestamp_nano_to_datetime(current_timestamp_nano)
230230
# 下一分钟的开始时间
231231
next_datetime = current_datetime.replace(second=0) + timedelta(minutes=1)
232232
start_datetime_nano = _datetime_to_timestamp_nano(next_datetime)
233233
r = _rangeset_head(_rangeset_slice(trading_timestamp_nano_range, start_datetime_nano), int(duration * 1e9))
234234
if not (r and trading_timestamp_nano_range[0][0] <= r[-1][-1] < trading_timestamp_nano_range[-1][1]):
235235
raise Exception("指定时间段超出当前交易日")
236236

237-
start_datetime = datetime.fromtimestamp(start_datetime_nano // 1000000000)
238-
end_datetime = datetime.fromtimestamp((r[-1][-1] - 1) // 1000000000)
237+
start_datetime = _timestamp_nano_to_datetime(start_datetime_nano)
238+
end_datetime = _timestamp_nano_to_datetime((r[-1][-1] - 1))
239239
time_slot_start = time(start_datetime.hour, start_datetime.minute) # 计划交易时段起始时间点
240240
time_slot_end = time(end_datetime.hour, end_datetime.minute) # 计划交易时段终点时间点
241241
if time_slot_end > time_slot_start: # 判断是否类似 23:00:00 开始, 01:00:00 结束这样跨天的情况

tqsdk/api.py

Lines changed: 45 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@
5656
from tqsdk.calendar import _get_trading_calendar, TqContCalendar, _init_chinese_rest_days
5757
from tqsdk.data_extension import DataExtension
5858
from tqsdk.data_series import DataSeries
59-
from tqsdk.datetime import _get_trading_day_start_time, _get_trading_day_end_time, _get_trading_day_from_timestamp, \
60-
_datetime_to_timestamp_nano
59+
from tqsdk.datetime import _get_trading_day_from_timestamp, _datetime_to_timestamp_nano, _timestamp_nano_to_datetime, \
60+
_cst_now, _convert_user_input_to_nano
6161
from tqsdk.diff import _merge_diff, _get_obj, _is_key_exist, _register_update_chan
6262
from tqsdk.entity import Entity
6363
from tqsdk.exceptions import TqTimeoutError
@@ -305,7 +305,7 @@ def __init__(self, account: Optional[Union[TqMultiAccount, UnionTradeable]] = No
305305
def _print(self, msg: str = "", level: str = "INFO"):
306306
if self.disable_print:
307307
return
308-
dt = "" if self._backtest else datetime.now().strftime('%Y-%m-%d %H:%M:%S')
308+
dt = "" if self._backtest else _cst_now().strftime('%Y-%m-%d %H:%M:%S')
309309
level = level if isinstance(level, str) else logging.getLevelName(level)
310310
print(f"{(dt + ' - ') if dt else ''}{level:>8} - {msg}")
311311

@@ -836,9 +836,15 @@ def get_kline_data_series(self, symbol: Union[str, List[str]], duration_seconds:
836836
duration_seconds (int): K 线数据周期, 以秒为单位。例如: 1 分钟线为 60,1 小时线为 3600,日线为 86400。\
837837
注意: 周期在日线以内时此参数可以任意填写, 在日线以上时只能是日线(86400)的整数倍
838838
839-
start_dt (date/datetime): 起始时间, 如果类型为 date 则指的是交易日, 如果为 datetime 则指的是具体时间点
839+
start_dt (date/datetime): 起始时间
840+
* date: 指的是交易日
840841
841-
end_dt (date/datetime): 结束时间, 如果类型为 date 则指的是交易日, 如果为 datetime 则指的是具体时间点
842+
* datetime: 指的是具体时间点,如果没有指定时区信息,则默认为北京时间
843+
844+
end_dt (date/datetime): 结束时间
845+
* date: 指的是交易日
846+
847+
* datetime: 指的是具体时间点,如果没有指定时区信息,则默认为北京时间
842848
843849
adj_type (str/None): [可选]指定复权类型,默认为 None。adj_type 参数只对股票和基金类型合约有效。\
844850
"F" 表示前复权;"B" 表示后复权;None 表示不做处理。
@@ -896,9 +902,15 @@ def get_tick_data_series(self, symbol: Union[str, List[str]], start_dt: Union[da
896902
Args:
897903
symbol (str): 指定合约代码。当前只支持单个合约。
898904
899-
start_dt (date/datetime): 起始时间, 如果类型为 date 则指的是交易日, 如果为 datetime 则指的是具体时间点
905+
start_dt (date/datetime): 起始时间
906+
* date: 指的是交易日
907+
908+
* datetime: 指的是具体时间点,如果没有指定时区信息,则默认为北京时间
900909
901-
end_dt (date/datetime): 结束时间, 如果类型为 date 则指的是交易日, 如果为 datetime 则指的是具体时间点
910+
end_dt (date/datetime): 结束时间
911+
* date: 指的是交易日
912+
913+
* datetime: 指的是具体时间点,如果没有指定时区信息,则默认为北京时间
902914
903915
adj_type (str/None): [可选]指定复权类型,默认为 None。adj_type 参数只对股票和基金类型合约有效。\
904916
"F" 表示前复权;"B" 表示后复权;None 表示不做处理。
@@ -954,20 +966,7 @@ def _get_data_series(self, call_func: str, symbol_list: Union[str, List[str]], d
954966
if len(symbol_list) != 1:
955967
raise Exception(f"{call_func} 数据获取方式暂不支持多合约请求")
956968
self._ensure_symbol(symbol_list) # 检查合约代码是否存在
957-
if isinstance(start_dt, datetime):
958-
start_dt_nano = _datetime_to_timestamp_nano(start_dt)
959-
elif isinstance(start_dt, date):
960-
start_dt_nano = _get_trading_day_start_time(
961-
_datetime_to_timestamp_nano(datetime(start_dt.year, start_dt.month, start_dt.day)))
962-
else:
963-
raise Exception(f"start_dt 参数类型 {type(start_dt)} 错误, 只支持 datetime / date 类型,请检查是否正确")
964-
if isinstance(end_dt, datetime):
965-
end_dt_nano = _datetime_to_timestamp_nano(end_dt)
966-
elif isinstance(end_dt, date):
967-
end_dt_nano = _get_trading_day_end_time(
968-
_datetime_to_timestamp_nano(datetime(end_dt.year, end_dt.month, end_dt.day)))
969-
else:
970-
raise Exception(f"end_dt 参数类型 {type(end_dt)} 错误, 只支持 datetime / date 类型,请检查是否正确")
969+
start_dt_nano, end_dt_nano = _convert_user_input_to_nano(start_dt, end_dt)
971970
if adj_type not in [None, "F", "B"]:
972971
raise Exception("adj_type 参数只支持 None (不复权) | 'F' (前复权) | 'B' (后复权) ")
973972
ds = DataSeries(self, symbol_list, dur_nano, start_dt_nano, end_dt_nano, adj_type)
@@ -981,17 +980,23 @@ def _get_data_series(self, call_func: str, symbol_list: Union[str, List[str]], d
981980
# ----------------------------------------------------------------------
982981
def get_trading_calendar(self, start_dt: Union[date, datetime], end_dt: Union[date, datetime]) -> pd.DataFrame:
983982
"""
984-
获取一段时间内的交易日历信息,交易日历可以处理的范围为 2003-01-01 ~ 2022-12-31。
983+
获取一段时间内的交易日历信息,交易日历可以处理的范围为 2003-01-01 ~ 2024-12-31。
985984
986985
Args:
987-
start_dt (date/datetime): 起始时间,如果类型为 date 则指的是该日期;如果为 datetime 则指的是该时间点所在日期
986+
start_dt (date/datetime): 起始时间
987+
* date: 指的是交易日
988+
989+
* datetime: 指的指的是该时间点所在年月日日期
990+
991+
end_dt (date/datetime): 结束时间
992+
* date: 指的是交易日
988993
989-
end_dt (date/datetime): 结束时间,如果类型为 date 则指的是该日期;如果为 datetime 则指的是该时间点所在日期
994+
* datetime: 指的指的是该时间点所在年月日日期
990995
991996
Returns:
992997
pandas.DataFrame: 包含以下列:
993998
994-
* date: (datetime64[ns]) 日期
999+
* date: (datetime64[ns]) 日期,为北京时间的日期
9951000
* trading: (bool) 是否是交易日
9961001
9971002
Example::
@@ -1016,11 +1021,11 @@ def get_trading_calendar(self, start_dt: Union[date, datetime], end_dt: Union[da
10161021
api.close()
10171022
"""
10181023
if isinstance(start_dt, datetime):
1019-
start_dt = date(year=start_dt.year, month=start_dt.month, day=start_dt.day)
1024+
start_dt = start_dt.date()
10201025
elif not isinstance(start_dt, date):
10211026
raise Exception(f"start_dt 参数类型 {type(start_dt)} 错误, 只支持 datetime / date 类型,请检查是否正确")
10221027
if isinstance(end_dt, datetime):
1023-
end_dt = date(year=end_dt.year, month=end_dt.month, day=end_dt.day)
1028+
end_dt = end_dt.date()
10241029
elif not isinstance(end_dt, date):
10251030
raise Exception(f"end_dt 参数类型 {type(end_dt)} 错误, 只支持 datetime / date 类型,请检查是否正确")
10261031
first_date, latest_date = _init_chinese_rest_days()
@@ -1078,9 +1083,9 @@ def query_his_cont_quotes(self, symbol: Union[str, List[str]], n: int = 200) ->
10781083
raise Exception(f"参数错误,n={n} 应该是大于等于 1 的整数")
10791084
now_dt = self._get_current_datetime()
10801085
trading_day = _get_trading_day_from_timestamp(_datetime_to_timestamp_nano(now_dt))
1081-
end_dt = datetime.fromtimestamp(trading_day / 1000000000)
1082-
cont_calendar = TqContCalendar(start_dt=end_dt - timedelta(days=n * 2 + 30), end_dt=end_dt, symbols=symbols,
1083-
headers=self._base_headers)
1086+
end_dt = _timestamp_nano_to_datetime(trading_day)
1087+
cont_calendar = TqContCalendar(start_dt=(end_dt - timedelta(days=n * 2 + 30)).date(), end_dt=end_dt.date(),
1088+
symbols=symbols, headers=self._base_headers)
10841089
df = cont_calendar.df.loc[cont_calendar.df.date.le(end_dt), ['date'] + symbols]
10851090
df = df.iloc[-n:]
10861091
df.reset_index(inplace=True, drop=True)
@@ -2575,10 +2580,8 @@ def filter(query_result):
25752580
for edge in quote["derivatives"]["edges"]:
25762581
option = edge["node"]
25772582
if (option_class and option["call_or_put"] != option_class) \
2578-
or (exe_year and datetime.fromtimestamp(
2579-
option["last_exercise_datetime"] / 1e9).year != exe_year) \
2580-
or (exe_month and datetime.fromtimestamp(
2581-
option["last_exercise_datetime"] / 1e9).month != exe_month) \
2583+
or (exe_year and _timestamp_nano_to_datetime(option["last_exercise_datetime"]).year != exe_year) \
2584+
or (exe_month and _timestamp_nano_to_datetime(option["last_exercise_datetime"]).month != exe_month) \
25822585
or (strike_price and option["strike_price"] != strike_price) \
25832586
or (expired is not None and option["expired"] != expired) \
25842587
or (has_A is True and option["english_name"].count('A') == 0) \
@@ -2740,8 +2743,8 @@ def query_symbol_info(self, symbol: Union[str, List[str]]) -> TqSymbolDataFrame:
27402743
* pre_settlement: 昨结算
27412744
* pre_open_interest: 昨持仓
27422745
* pre_close: 昨收盘
2743-
* trading_time_day: 白盘交易时间段,list 类型
2744-
* trading_time_night: 夜盘交易时间段,list 类型
2746+
* trading_time_day: 白盘交易时间段,pandas.Series 类型
2747+
* trading_time_night: 夜盘交易时间段,pandas.Series 类型
27452748
27462749
注意:
27472750
@@ -2999,7 +3002,7 @@ def _convert_query_result_to_list(self, query_result):
29993002
for quote in query_result.get("result", {}).get("multi_symbol_info", []):
30003003
if quote.get("derivatives"):
30013004
for edge in quote["derivatives"]["edges"]:
3002-
last_exercise_datetime = datetime.fromtimestamp(edge["node"]["last_exercise_datetime"] / 1e9)
3005+
last_exercise_datetime = _timestamp_nano_to_datetime(edge["node"]["last_exercise_datetime"])
30033006
edge["node"]["exercise_year"] = last_exercise_datetime.year
30043007
edge["node"]["exercise_month"] = last_exercise_datetime.month
30053008
options.append(edge["node"])
@@ -3157,7 +3160,7 @@ def _setup_connection(self):
31573160
try:
31583161
self._md_url = self._auth._get_md_url(self._stock, backtest=isinstance(self._backtest, TqBacktest)) # 如果用户未指定行情地址,则使用名称服务获取行情地址
31593162
except Exception as e:
3160-
now = datetime.now()
3163+
now = _cst_now()
31613164
if now.hour == 19 and 0 <= now.minute <= 30:
31623165
raise Exception(f"{e}, 每日 19:00-19:30 为日常运维时间,请稍后再试")
31633166
else:
@@ -3178,8 +3181,8 @@ def _setup_connection(self):
31783181
# 期权增加了 exercise_year、exercise_month 在旧版合约服务中没有,需要添加,使用下市日期代替最后行权日
31793182
for quote in quotes.values():
31803183
if quote["ins_class"] == "FUTURE_OPTION":
3181-
quote["exercise_year"] = datetime.fromtimestamp(quote["expire_datetime"]).year
3182-
quote["exercise_month"] = datetime.fromtimestamp(quote["expire_datetime"]).month
3184+
quote["exercise_year"] = _timestamp_nano_to_datetime(int(quote["expire_datetime"] * 1000000) * 1000).year
3185+
quote["exercise_month"] = _timestamp_nano_to_datetime(int(quote["expire_datetime"] * 1000000) * 1000).month
31833186
ws_md_recv_chan.send_nowait({
31843187
"aid": "rtn_data",
31853188
"data": [{"quotes": quotes}]
@@ -3952,9 +3955,9 @@ def _symbols_to_quotes(self, symbols, keys=None):
39523955
def _get_current_datetime(self):
39533956
if isinstance(self._backtest, TqBacktest):
39543957
current_dt = self._data.get('_tqsdk_backtest', {}).get('current_dt', 0)
3955-
return datetime.fromtimestamp(current_dt / 1e9)
3958+
return _timestamp_nano_to_datetime(current_dt)
39563959
else:
3957-
return datetime.now()
3960+
return _cst_now()
39583961

39593962

39603963
print("在使用天勤量化之前,默认您已经知晓并同意以下免责条款,如果不同意请立即停止使用:https://www.shinnytech.com/blog/disclaimer/", file=sys.stderr)

0 commit comments

Comments
 (0)