Skip to content

Commit

Permalink
Update Version 3.4.11
Browse files Browse the repository at this point in the history
  • Loading branch information
shinny-pack authored and shinny-mayanqiong committed Jan 3, 2024
1 parent 596dada commit 138c302
Show file tree
Hide file tree
Showing 21 changed files with 176 additions and 134 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.4.10
Version: 3.4.11
Summary: TianQin SDK
Home-page: https://www.shinnytech.com/tqsdk
Author: TianQin
Expand Down
6 changes: 3 additions & 3 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,17 @@

# General information about the project.
project = u'TianQin Python SDK'
copyright = u'2018-2023, TianQin'
copyright = u'2018-2024, TianQin'
author = u'TianQin'

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = u'3.4.10'
version = u'3.4.11'
# The full version, including alpha/beta/rc tags.
release = u'3.4.10'
release = u'3.4.11'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
4 changes: 2 additions & 2 deletions doc/profession.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ TqSdk 中大部分功能是供用户免费使用的, 同时我们也提供了专

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

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

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

更稳定的行情服务器
-------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion doc/usage/backtest.rst
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ TqSdk回测框架使用一套复杂的规则来推进行情:
规则3: quote按照以下规则更新::

if 策略程序中使用了这个合约的tick序列:
每次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
每次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
elif 策略程序中使用了这个合约的K线序列:
每次K线序列推进时会更新quote. 使用 k线生成的 quote 的盘口由收盘价分别加/减一个最小变动单位, 并且 highest/lowest/average/amount 始终为 nan, volume 始终为0.
每次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
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.4.11 (2024/01/03)

* 优化:支持天勤在不同时区设置的操作系统上使用。tqsdk 内部时间表示全部使用北京时间。
对于以下接口,用户输入 datetime 类型参数时,如果未指定时区信息,tqsdk 会指定为北京时间;如果指定了时区信息,会转为北京时间,保证 Unix Timestamp 时间戳不变。
: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`。


3.4.10 (2023/09/22)

* 修复:pandas 2.1.0 版本 fillna 、NumericBlock 会报 deprecated warning 的问题
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.4.10",
version="3.4.11",
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.4.10'
__version__ = '3.4.11'
14 changes: 7 additions & 7 deletions tqsdk/algorithm/time_table_generater.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-
__author__ = 'mayanqiong'

from datetime import datetime, time, timedelta
from datetime import time, timedelta
from typing import Optional, Union

import numpy as np
Expand All @@ -12,7 +12,7 @@
from tqsdk.api import TqApi
from tqsdk import utils
from tqsdk.datetime import _get_trading_timestamp, _get_trade_timestamp, _get_trading_day_from_timestamp, \
_datetime_to_timestamp_nano
_datetime_to_timestamp_nano, _timestamp_nano_to_datetime
from tqsdk.rangeset import _rangeset_slice, _rangeset_head
from tqsdk.tradeable import TqAccount, TqKq, TqSim

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

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

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

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

start_datetime = datetime.fromtimestamp(start_datetime_nano // 1000000000)
end_datetime = datetime.fromtimestamp((r[-1][-1] - 1) // 1000000000)
start_datetime = _timestamp_nano_to_datetime(start_datetime_nano)
end_datetime = _timestamp_nano_to_datetime((r[-1][-1] - 1))
time_slot_start = time(start_datetime.hour, start_datetime.minute) # 计划交易时段起始时间点
time_slot_end = time(end_datetime.hour, end_datetime.minute) # 计划交易时段终点时间点
if time_slot_end > time_slot_start: # 判断是否类似 23:00:00 开始, 01:00:00 结束这样跨天的情况
Expand Down
87 changes: 45 additions & 42 deletions tqsdk/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@
from tqsdk.calendar import _get_trading_calendar, TqContCalendar, _init_chinese_rest_days
from tqsdk.data_extension import DataExtension
from tqsdk.data_series import DataSeries
from tqsdk.datetime import _get_trading_day_start_time, _get_trading_day_end_time, _get_trading_day_from_timestamp, \
_datetime_to_timestamp_nano
from tqsdk.datetime import _get_trading_day_from_timestamp, _datetime_to_timestamp_nano, _timestamp_nano_to_datetime, \
_cst_now, _convert_user_input_to_nano
from tqsdk.diff import _merge_diff, _get_obj, _is_key_exist, _register_update_chan
from tqsdk.entity import Entity
from tqsdk.exceptions import TqTimeoutError
Expand Down Expand Up @@ -305,7 +305,7 @@ def __init__(self, account: Optional[Union[TqMultiAccount, UnionTradeable]] = No
def _print(self, msg: str = "", level: str = "INFO"):
if self.disable_print:
return
dt = "" if self._backtest else datetime.now().strftime('%Y-%m-%d %H:%M:%S')
dt = "" if self._backtest else _cst_now().strftime('%Y-%m-%d %H:%M:%S')
level = level if isinstance(level, str) else logging.getLevelName(level)
print(f"{(dt + ' - ') if dt else ''}{level:>8} - {msg}")

Expand Down Expand Up @@ -836,9 +836,15 @@ def get_kline_data_series(self, symbol: Union[str, List[str]], duration_seconds:
duration_seconds (int): K 线数据周期, 以秒为单位。例如: 1 分钟线为 60,1 小时线为 3600,日线为 86400。\
注意: 周期在日线以内时此参数可以任意填写, 在日线以上时只能是日线(86400)的整数倍
start_dt (date/datetime): 起始时间, 如果类型为 date 则指的是交易日, 如果为 datetime 则指的是具体时间点
start_dt (date/datetime): 起始时间
* date: 指的是交易日
end_dt (date/datetime): 结束时间, 如果类型为 date 则指的是交易日, 如果为 datetime 则指的是具体时间点
* datetime: 指的是具体时间点,如果没有指定时区信息,则默认为北京时间
end_dt (date/datetime): 结束时间
* date: 指的是交易日
* datetime: 指的是具体时间点,如果没有指定时区信息,则默认为北京时间
adj_type (str/None): [可选]指定复权类型,默认为 None。adj_type 参数只对股票和基金类型合约有效。\
"F" 表示前复权;"B" 表示后复权;None 表示不做处理。
Expand Down Expand Up @@ -896,9 +902,15 @@ def get_tick_data_series(self, symbol: Union[str, List[str]], start_dt: Union[da
Args:
symbol (str): 指定合约代码。当前只支持单个合约。
start_dt (date/datetime): 起始时间, 如果类型为 date 则指的是交易日, 如果为 datetime 则指的是具体时间点
start_dt (date/datetime): 起始时间
* date: 指的是交易日
* datetime: 指的是具体时间点,如果没有指定时区信息,则默认为北京时间
end_dt (date/datetime): 结束时间, 如果类型为 date 则指的是交易日, 如果为 datetime 则指的是具体时间点
end_dt (date/datetime): 结束时间
* date: 指的是交易日
* datetime: 指的是具体时间点,如果没有指定时区信息,则默认为北京时间
adj_type (str/None): [可选]指定复权类型,默认为 None。adj_type 参数只对股票和基金类型合约有效。\
"F" 表示前复权;"B" 表示后复权;None 表示不做处理。
Expand Down Expand Up @@ -954,20 +966,7 @@ def _get_data_series(self, call_func: str, symbol_list: Union[str, List[str]], d
if len(symbol_list) != 1:
raise Exception(f"{call_func} 数据获取方式暂不支持多合约请求")
self._ensure_symbol(symbol_list) # 检查合约代码是否存在
if isinstance(start_dt, datetime):
start_dt_nano = _datetime_to_timestamp_nano(start_dt)
elif isinstance(start_dt, date):
start_dt_nano = _get_trading_day_start_time(
_datetime_to_timestamp_nano(datetime(start_dt.year, start_dt.month, start_dt.day)))
else:
raise Exception(f"start_dt 参数类型 {type(start_dt)} 错误, 只支持 datetime / date 类型,请检查是否正确")
if isinstance(end_dt, datetime):
end_dt_nano = _datetime_to_timestamp_nano(end_dt)
elif isinstance(end_dt, date):
end_dt_nano = _get_trading_day_end_time(
_datetime_to_timestamp_nano(datetime(end_dt.year, end_dt.month, end_dt.day)))
else:
raise Exception(f"end_dt 参数类型 {type(end_dt)} 错误, 只支持 datetime / date 类型,请检查是否正确")
start_dt_nano, end_dt_nano = _convert_user_input_to_nano(start_dt, end_dt)
if adj_type not in [None, "F", "B"]:
raise Exception("adj_type 参数只支持 None (不复权) | 'F' (前复权) | 'B' (后复权) ")
ds = DataSeries(self, symbol_list, dur_nano, start_dt_nano, end_dt_nano, adj_type)
Expand All @@ -981,17 +980,23 @@ 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 ~ 2022-12-31。
获取一段时间内的交易日历信息,交易日历可以处理的范围为 2003-01-01 ~ 2024-12-31。
Args:
start_dt (date/datetime): 起始时间,如果类型为 date 则指的是该日期;如果为 datetime 则指的是该时间点所在日期
start_dt (date/datetime): 起始时间
* date: 指的是交易日
* datetime: 指的指的是该时间点所在年月日日期
end_dt (date/datetime): 结束时间
* date: 指的是交易日
end_dt (date/datetime): 结束时间,如果类型为 date 则指的是该日期;如果为 datetime 则指的是该时间点所在日期
* datetime: 指的指的是该时间点所在年月日日期
Returns:
pandas.DataFrame: 包含以下列:
* date: (datetime64[ns]) 日期
* date: (datetime64[ns]) 日期,为北京时间的日期
* trading: (bool) 是否是交易日
Example::
Expand All @@ -1016,11 +1021,11 @@ def get_trading_calendar(self, start_dt: Union[date, datetime], end_dt: Union[da
api.close()
"""
if isinstance(start_dt, datetime):
start_dt = date(year=start_dt.year, month=start_dt.month, day=start_dt.day)
start_dt = start_dt.date()
elif not isinstance(start_dt, date):
raise Exception(f"start_dt 参数类型 {type(start_dt)} 错误, 只支持 datetime / date 类型,请检查是否正确")
if isinstance(end_dt, datetime):
end_dt = date(year=end_dt.year, month=end_dt.month, day=end_dt.day)
end_dt = end_dt.date()
elif not isinstance(end_dt, date):
raise Exception(f"end_dt 参数类型 {type(end_dt)} 错误, 只支持 datetime / date 类型,请检查是否正确")
first_date, latest_date = _init_chinese_rest_days()
Expand Down Expand Up @@ -1078,9 +1083,9 @@ def query_his_cont_quotes(self, symbol: Union[str, List[str]], n: int = 200) ->
raise Exception(f"参数错误,n={n} 应该是大于等于 1 的整数")
now_dt = self._get_current_datetime()
trading_day = _get_trading_day_from_timestamp(_datetime_to_timestamp_nano(now_dt))
end_dt = datetime.fromtimestamp(trading_day / 1000000000)
cont_calendar = TqContCalendar(start_dt=end_dt - timedelta(days=n * 2 + 30), end_dt=end_dt, symbols=symbols,
headers=self._base_headers)
end_dt = _timestamp_nano_to_datetime(trading_day)
cont_calendar = TqContCalendar(start_dt=(end_dt - timedelta(days=n * 2 + 30)).date(), end_dt=end_dt.date(),
symbols=symbols, headers=self._base_headers)
df = cont_calendar.df.loc[cont_calendar.df.date.le(end_dt), ['date'] + symbols]
df = df.iloc[-n:]
df.reset_index(inplace=True, drop=True)
Expand Down Expand Up @@ -2575,10 +2580,8 @@ def filter(query_result):
for edge in quote["derivatives"]["edges"]:
option = edge["node"]
if (option_class and option["call_or_put"] != option_class) \
or (exe_year and datetime.fromtimestamp(
option["last_exercise_datetime"] / 1e9).year != exe_year) \
or (exe_month and datetime.fromtimestamp(
option["last_exercise_datetime"] / 1e9).month != exe_month) \
or (exe_year and _timestamp_nano_to_datetime(option["last_exercise_datetime"]).year != exe_year) \
or (exe_month and _timestamp_nano_to_datetime(option["last_exercise_datetime"]).month != exe_month) \
or (strike_price and option["strike_price"] != strike_price) \
or (expired is not None and option["expired"] != expired) \
or (has_A is True and option["english_name"].count('A') == 0) \
Expand Down Expand Up @@ -2740,8 +2743,8 @@ def query_symbol_info(self, symbol: Union[str, List[str]]) -> TqSymbolDataFrame:
* pre_settlement: 昨结算
* pre_open_interest: 昨持仓
* pre_close: 昨收盘
* trading_time_day: 白盘交易时间段,list 类型
* trading_time_night: 夜盘交易时间段,list 类型
* trading_time_day: 白盘交易时间段,pandas.Series 类型
* trading_time_night: 夜盘交易时间段,pandas.Series 类型
注意:
Expand Down Expand Up @@ -2999,7 +3002,7 @@ def _convert_query_result_to_list(self, query_result):
for quote in query_result.get("result", {}).get("multi_symbol_info", []):
if quote.get("derivatives"):
for edge in quote["derivatives"]["edges"]:
last_exercise_datetime = datetime.fromtimestamp(edge["node"]["last_exercise_datetime"] / 1e9)
last_exercise_datetime = _timestamp_nano_to_datetime(edge["node"]["last_exercise_datetime"])
edge["node"]["exercise_year"] = last_exercise_datetime.year
edge["node"]["exercise_month"] = last_exercise_datetime.month
options.append(edge["node"])
Expand Down Expand Up @@ -3157,7 +3160,7 @@ def _setup_connection(self):
try:
self._md_url = self._auth._get_md_url(self._stock, backtest=isinstance(self._backtest, TqBacktest)) # 如果用户未指定行情地址,则使用名称服务获取行情地址
except Exception as e:
now = datetime.now()
now = _cst_now()
if now.hour == 19 and 0 <= now.minute <= 30:
raise Exception(f"{e}, 每日 19:00-19:30 为日常运维时间,请稍后再试")
else:
Expand All @@ -3178,8 +3181,8 @@ def _setup_connection(self):
# 期权增加了 exercise_year、exercise_month 在旧版合约服务中没有,需要添加,使用下市日期代替最后行权日
for quote in quotes.values():
if quote["ins_class"] == "FUTURE_OPTION":
quote["exercise_year"] = datetime.fromtimestamp(quote["expire_datetime"]).year
quote["exercise_month"] = datetime.fromtimestamp(quote["expire_datetime"]).month
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}]
Expand Down Expand Up @@ -3952,9 +3955,9 @@ def _symbols_to_quotes(self, symbols, keys=None):
def _get_current_datetime(self):
if isinstance(self._backtest, TqBacktest):
current_dt = self._data.get('_tqsdk_backtest', {}).get('current_dt', 0)
return datetime.fromtimestamp(current_dt / 1e9)
return _timestamp_nano_to_datetime(current_dt)
else:
return datetime.now()
return _cst_now()


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

0 comments on commit 138c302

Please sign in to comment.