Skip to content

Commit 41d8f69

Browse files
shinny-packshinny-mayanqiong
authored andcommitted
Update Version 2.8.2
1 parent 9afe7f3 commit 41d8f69

File tree

9 files changed

+235
-99
lines changed

9 files changed

+235
-99
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: 2.8.1
3+
Version: 2.8.2
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'2.8.1'
51+
version = u'2.8.2'
5252
# The full version, including alpha/beta/rc tags.
53-
release = u'2.8.1'
53+
release = u'2.8.2'
5454

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

doc/usage/mddatas.rst

Lines changed: 68 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -79,59 +79,78 @@ SZSE 深圳证券交易所
7979
----------------------------------------------------
8080
:py:meth:`~tqsdk.api.TqApi.get_quote` 函数提供实时行情和合约信息::
8181

82-
q = api.get_quote("SHFE.cu1901")
82+
q = api.get_quote("SHFE.cu2201")
8383

8484
返回值为一个dict, 结构如下::
8585

8686
{
87-
"datetime": "", # "2017-07-26 23:04:21.000001" (行情从交易所发出的时间(北京时间))
88-
"ask_price5": float("nan"), # 6122.0 (卖五价)
89-
"ask_volume5": 0, # 3 (卖五量)
90-
"ask_price4": float("nan"), # 6122.0 (卖四价)
91-
"ask_volume4": 0, # 3 (卖四量)
92-
"ask_price3": float("nan"), # 6122.0 (卖三价)
93-
"ask_volume3": 0, # 3 (卖三量)
94-
"ask_price2": float("nan"), # 6122.0 (卖二价)
95-
"ask_volume2": 0, # 3 (卖二量)
96-
"ask_price1": float("nan"), # 6122.0 (卖一价)
97-
"ask_volume1": 0, # 3 (卖一量)
98-
"bid_price1": float("nan"), # 6121.0 (买一价)
99-
"bid_volume1": 0, # 7 (买一量)
100-
"bid_price2": float("nan"), # 6121.0 (买二价)
101-
"bid_volume2": 0, # 7 (买二量)
102-
"bid_price3": float("nan"), # 6121.0 (买三价)
103-
"bid_volume3": 0, # 7 (买三量)
104-
"bid_price4": float("nan"), # 6121.0 (买四价)
105-
"bid_volume4": 0, # 7 (买四量)
106-
"bid_price5": float("nan"), # 6121.0 (买五价)
107-
"bid_volume5": 0, # 7 (买五量)
108-
"last_price": float("nan"), # 6122.0 (最新价)
109-
"highest": float("nan"), # 6129.0 (当日最高价)
110-
"lowest": float("nan"), # 6101.0 (当日最低价)
111-
"open": float("nan"), # 6102.0 (开盘价)
112-
"close": float("nan"), # nan (收盘价)
113-
"average": float("nan"), # 6119.0 (当日均价)
114-
"volume": 0, # 89252 (成交量)
115-
"amount": float("nan"), # 5461329880.0 (成交额)
116-
"open_interest": 0, # 616424 (持仓量)
117-
"settlement": float("nan"), # nan (结算价)
118-
"upper_limit": float("nan"), # 6388.0 (涨停价)
119-
"lower_limit": float("nan"), # 5896.0 (跌停价)
120-
"pre_open_interest": 0, # 616620 (昨持仓量)
121-
"pre_settlement": float("nan"), # 6142.0 (昨结算价)
122-
"pre_close": float("nan"), # 6106.0 (昨收盘价)
123-
"price_tick": float("nan"), # 10.0 (合约价格单位)
124-
"price_decs": 0, # 0 (合约价格小数位数)
125-
"volume_multiple": 0, # 10 (合约乘数)
126-
"max_limit_order_volume": 0, # 500 (最大限价单手数)
127-
"max_market_order_volume": 0, # 0 (最大市价单手数)
128-
"min_limit_order_volume": 0, # 1 (最小限价单手数)
129-
"min_market_order_volume": 0, # 0 (最小市价单手数)
130-
"underlying_symbol": "", # SHFE.rb1901 (标的合约)
131-
"strike_price": float("nan"), # nan (行权价)
132-
"change": float("nan"), # −20.0 (涨跌)
133-
"change_percent": float("nan"), # −0.00325 (涨跌幅)
134-
"expired": False, # False (合约是否已下市)
87+
"datetime": "2021-08-17 14:59:59.000001", # 行情从交易所发出的时间(北京时间)
88+
"ask_price1": 69750.0, # 卖一价
89+
"ask_volume1": 1, # 卖一量
90+
"bid_price1": 69600.0, # 买一价
91+
"bid_volume1": 2, # 买一量
92+
"ask_price2": 69920.0, # 卖二价
93+
"ask_volume2": 3, # 卖二量
94+
"bid_price2": 69500.0, # 买二价
95+
"bid_volume2": 3, # 买二量
96+
"ask_price3": 69940.0, # 卖三价
97+
"ask_volume3": 1, # 卖三量
98+
"bid_price3": 69450.0, # 买三价
99+
"bid_volume3": 1, # 买三量
100+
"ask_price4": 70010.0, # 卖四价
101+
"ask_volume4": 1, # 卖四量
102+
"bid_price4": 69400.0, # 买四价
103+
"bid_volume4": 1, # 买四量
104+
"ask_price5": 70050.0, # 卖五价
105+
"ask_volume5": 1, # 卖五量
106+
"bid_price5": 69380.0, # 买五价
107+
"bid_volume5": 1, # 买五量
108+
"last_price": 69710.0, # 最新价
109+
"highest": 70050.0, # 当日最高价
110+
"lowest": 69520.0, # 当日最低价
111+
"open": 69770.0, # 开盘价
112+
"close": 69710.0, # 收盘价
113+
"average": 69785.019711, # 当日均价
114+
"volume": 761, # 成交量
115+
"amount": 265532000.0, # 成交额
116+
"open_interest": 8850, # 持仓量
117+
"settlement": 69780.0, # 结算价
118+
"upper_limit": 75880.0, # 涨停价
119+
"lower_limit": 64630.0, # 跌停价
120+
"pre_open_interest": 8791, # 昨持仓量
121+
"pre_settlement": 70260.0, # 昨结算价
122+
"pre_close": 69680.0, # 昨收盘价
123+
"price_tick": 10.0, # 合约价格变动单位
124+
"price_decs": 0, # 合约价格小数位数
125+
"volume_multiple": 5.0, # 合约乘数
126+
"max_limit_order_volume": 500, # 最大限价单手数
127+
"max_market_order_volume": 0, # 最大市价单手数
128+
"min_limit_order_volume": 0, # 最小限价单手数
129+
"min_market_order_volume": 0, # 最小市价单手数
130+
"underlying_symbol": "", # 标的合约
131+
"strike_price": NaN, # 行权价
132+
"ins_class": "FUTURE", # 合约类型
133+
"instrument_id": "SHFE.cu2201", # 合约代码
134+
"instrument_name": "沪铜2201", # 合约中文名
135+
"exchange_id": "SHFE", # 交易所代码
136+
"expired": false, # 合约是否已下市
137+
"trading_time": "{'day': [['09:00:00', '10:15:00'], ['10:30:00', '11:30:00'], ['13:30:00', '15:00:00']], 'night': [['21:00:00', '25:00:00']]}", # 交易时间段
138+
"expire_datetime": 1642402800.0, # 到期具体日,以秒为单位的 timestamp 值
139+
"delivery_year": 2022, # 期货交割日年份,只对期货品种有效。期权推荐使用最后行权日年份
140+
"delivery_month": 1, # 期货交割日月份,只对期货品种有效。期权推荐使用最后行权日月份
141+
"last_exercise_datetime": NaN, # 期权最后行权日,以秒为单位的 timestamp 值
142+
"exercise_year": 0, # 期权最后行权日年份,只对期权品种有效。
143+
"exercise_month": 0, # 期权最后行权日月份,只对期权品种有效。
144+
"option_class": "", # 期权行权方式,看涨:'CALL',看跌:'PUT'
145+
"exercise_type": "", # 期权行权方式,美式:'A',欧式:'E'
146+
"product_id": "cu", # 品种代码
147+
"iopv": NaN, # ETF实时单位基金净值
148+
"public_float_share_quantity": 0, # 日流通股数,只对证券产品有效。
149+
"stock_dividend_ratio": [], # 除权表 ["20190601,0.15","20200107,0.2"…]
150+
"cash_dividend_ratio": [], # 除息表 ["20190601,0.15","20200107,0.2"…]
151+
"expire_rest_days": 153, # 距离到期日的剩余天数(自然日天数),正数表示距离到期日的剩余天数,0表示到期日当天,负数表示距离到期日已经过去的天数
152+
"commission": 17.565,
153+
"margin": 31617.0
135154
}
136155

137156
对于每个合约, 只需要调用一次 get_quote 函数. 如果需要监控数据更新, 可以使用 :py:meth:`~tqsdk.api.TqApi.wait_update`::

doc/version.rst

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

33
版本变更
44
=============================
5+
2.8.2 (2021/08/17)
6+
7+
* 增加:is_changing 接口增加对于合约 :py:meth:`~tqsdk.objs.Quote.expire_rest_days`,持仓 :py:meth:`~tqsdk.objs.Position.pos_long`、
8+
:py:meth:`~tqsdk.objs.Position.pos_short`、:py:meth:`~tqsdk.objs.Position.pos` 字段支持判断是否更新
9+
* 修复:2.8.1 版本重构后,不支持多线程运行的问题
10+
* docs: 更新合约字段示例说明
11+
12+
513
2.8.1 (2021/08/12)
614

715
* 增加:增强在协程中的支持,以下接口 :py:meth:`~tqsdk.api.TqApi.query_quotes`,:py:meth:`~tqsdk.api.TqApi.query_cont_quotes`,

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def get_tag(self):
3636

3737
setuptools.setup(
3838
name='tqsdk',
39-
version="2.8.1",
39+
version="2.8.2",
4040
description='TianQin SDK',
4141
author='TianQin',
4242
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__ = '2.8.1'
1+
__version__ = '2.8.2'

tqsdk/api.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
from tqsdk.backtest import TqBacktest, TqReplay
6161
from tqsdk.channel import TqChan
6262
from tqsdk.connect import TqConnect, MdReconnectHandler, TdReconnectHandler, ReconnectTimer
63+
from tqsdk.data_extension import DataExtension
6364
from tqsdk.data_series import DataSeries
6465
from tqsdk.datetime import _get_trading_day_start_time, _get_trading_day_end_time, _get_trading_calendar
6566
from tqsdk.diff import _merge_diff, _get_obj, _is_key_exist, _register_update_chan
@@ -258,6 +259,7 @@ def __init__(self, account: Union[TqMultiAccount, TqAccount, TqSim, None] = None
258259
self._prototype = self._gen_prototype() # 各业务数据的原型, 用于决定默认值及将收到的数据转为特定的类型
259260
self._security_prototype = self._gen_security_prototype() # 股票业务数据原型
260261
self._dividend_cache = {} # 缓存合约对应的复权系数矩阵,每个合约只计算一次
262+
self._send_chan, self._recv_chan = TqChan(self), TqChan(self) # 消息收发队列
261263

262264
# slave模式的api不需要完整初始化流程
263265
self._is_slave = isinstance(account, TqApi)
@@ -273,7 +275,6 @@ def __init__(self, account: Union[TqMultiAccount, TqAccount, TqSim, None] = None
273275

274276
self._web_gui = web_gui
275277
# 初始化
276-
self._send_chan, self._recv_chan = TqChan(self), TqChan(self) # 消息收发队列
277278
self.create_task(self._notify_watcher()) # 监控服务器发送的通知
278279
self._reconnect_timer = ReconnectTimer() # 管理 ws 连接重连时间
279280
self._setup_connection() # 初始化通讯连接
@@ -2867,16 +2868,21 @@ def _setup_connection(self):
28672868
self.create_task(tq_web_helper._run(web_send_chan, web_recv_chan, self._send_chan, self._recv_chan))
28682869
self._send_chan, self._recv_chan = web_send_chan, web_recv_chan
28692870

2870-
self._send_chan._logger_bind(chan_from="api")
2871-
self._recv_chan._logger_bind(chan_to="api")
2872-
28732871
# 股票盈亏计算
28742872
if self._account._has_stock_account:
28752873
stock_profit_helper = TqStockProfit(self)
28762874
stock_send_chan, stock_recv_chan = TqChan(self), TqChan(self)
28772875
self.create_task(stock_profit_helper._run(stock_send_chan, stock_recv_chan, self._send_chan, self._recv_chan))
28782876
self._send_chan, self._recv_chan = stock_send_chan, stock_recv_chan
28792877

2878+
data_extension = DataExtension(self)
2879+
data_extension_send_chan = TqChan(self, chan_name="send to data_extension")
2880+
data_extension_recv_chan = TqChan(self, chan_name="recv from data_extension")
2881+
self.create_task(data_extension._run(data_extension_send_chan, data_extension_recv_chan, self._send_chan, self._recv_chan))
2882+
self._send_chan, self._recv_chan = data_extension_send_chan, data_extension_recv_chan
2883+
self._send_chan._logger_bind(chan_from="api")
2884+
self._recv_chan._logger_bind(chan_to="api")
2885+
28802886
def _fetch_symbol_info(self, url):
28812887
"""获取合约信息"""
28822888
rsp = requests.get(url, headers=self._base_headers, timeout=30)

tqsdk/data_extension.py

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
__author__ = 'mayanqiong'
4+
5+
6+
from tqsdk.datetime import _get_expire_rest_days
7+
from tqsdk.datetime_state import TqDatetimeState
8+
from tqsdk.diff import _simple_merge_diff
9+
10+
11+
class DataExtension(object):
12+
"""
13+
为数据截面添加以下字段:
14+
{
15+
quotes: {
16+
*: {
17+
expire_rest_days: int
18+
}
19+
},
20+
trade: {
21+
*: {
22+
positions: {
23+
*: {
24+
'pos_long': int,
25+
'pos_short': int,
26+
'pos': int
27+
}
28+
}
29+
}
30+
}
31+
}
32+
"""
33+
34+
def __init__(self, api):
35+
self._api = api
36+
self._data = {'trade': {}} # 数据截面, 现在的功能只需要记录 trade
37+
self._diffs = []
38+
39+
async def _run(self, api_send_chan, api_recv_chan, md_send_chan, md_recv_chan):
40+
self._logger = self._api._logger.getChild("DataExtension")
41+
self._api_send_chan = api_send_chan
42+
self._api_recv_chan = api_recv_chan
43+
self._md_send_chan = md_send_chan
44+
self._md_recv_chan = md_recv_chan
45+
self._datetime_state = TqDatetimeState()
46+
md_task = self._api.create_task(self._md_handler())
47+
self._pending_peek = False # True 表示收到下游的 peek_message ,并且没有发给过下游回复;False 表示发给过下游回复,没有 pending_peek_message
48+
self._pending_peek_md = False # True 表示发给过上游 peek_message;False 表示对上游没有 pending_peek_message
49+
try:
50+
async for pack in api_send_chan:
51+
if "_md_recv" in pack:
52+
self._pending_peek_md = False
53+
await self._md_recv(pack)
54+
await self._send_diff()
55+
if self._pending_peek and self._pending_peek_md is False:
56+
self._pending_peek_md = True
57+
await self._md_send_chan.send({"aid": "peek_message"})
58+
elif pack["aid"] == "peek_message":
59+
self._pending_peek = True
60+
await self._send_diff()
61+
if self._pending_peek and self._pending_peek_md is False:
62+
self._pending_peek_md = True
63+
await self._md_send_chan.send(pack)
64+
else:
65+
await self._md_send_chan.send(pack)
66+
finally:
67+
md_task.cancel()
68+
69+
async def _md_handler(self):
70+
"""0 接收上游数据包 """
71+
async for pack in self._md_recv_chan:
72+
pack["_md_recv"] = True
73+
await self._api_send_chan.send(pack)
74+
75+
async def _md_recv(self, pack):
76+
"""将行情数据和交易数据合并至 self._data """
77+
for d in pack.get("data", {}):
78+
self._datetime_state.update_state(d)
79+
if d.get('trade', None):
80+
_simple_merge_diff(self._data['trade'], d['trade'], reduce_diff=False)
81+
self._diffs.append(d)
82+
83+
def _generate_ext_diff(self):
84+
""""
85+
补充 quote, position 额外字段
86+
此函数在 send_diff() 才会调用, self._datetime_state.data_ready 一定为 True,
87+
调用 self._datetime_state.get_current_dt() 一定有正确的当前时间
88+
"""
89+
pend_diff = {}
90+
for d in self._diffs:
91+
if d.get('quotes', None):
92+
_simple_merge_diff(pend_diff, self._update_quotes(d), reduce_diff=False)
93+
if d.get('trade', None):
94+
_simple_merge_diff(pend_diff, self._update_positions(d), reduce_diff=False)
95+
return pend_diff
96+
97+
def _update_quotes(self, diff):
98+
pend_diff = {}
99+
for symbol in diff['quotes']:
100+
expire_datetime = diff['quotes'].get(symbol, {}).get('expire_datetime', float('nan'))
101+
if expire_datetime == expire_datetime:
102+
# expire_rest_days 距离到期日的剩余天数(自然日天数)
103+
# 正数表示距离到期日的剩余天数,0表示到期日当天,负数表示距离到期日已经过去的天数
104+
expire_rest_days = _get_expire_rest_days(expire_datetime, self._datetime_state.get_current_dt() / 1e9)
105+
pend_diff[symbol] = {'expire_rest_days': expire_rest_days}
106+
return {'quotes': pend_diff} if pend_diff else {}
107+
108+
def _update_positions(self, diff):
109+
pend_diff = {}
110+
for account_key in diff['trade']:
111+
for symbol in diff['trade'].get(account_key, {}).get('positions', {}):
112+
pos = diff['trade'][account_key]['positions'][symbol]
113+
if 'pos_long_his' in pos or 'pos_long_today' in pos or 'pos_short_his' in pos or 'pos_short_today' in pos:
114+
data_pos = self._data['trade'][account_key]['positions'][symbol]
115+
pos_long = data_pos['pos_long_his'] + data_pos['pos_long_today']
116+
pos_short = data_pos['pos_short_his'] + data_pos['pos_short_today']
117+
pend_diff.setdefault(account_key, {})
118+
pend_diff[account_key].setdefault('positions', {})
119+
pend_diff[account_key]['positions'][symbol] = {
120+
'pos_long': pos_long,
121+
'pos_short': pos_short,
122+
'pos': pos_long - pos_short
123+
}
124+
return {'trade': pend_diff} if pend_diff else {}
125+
126+
async def _send_diff(self):
127+
if self._datetime_state.data_ready and self._pending_peek and self._diffs:
128+
# 生成增量业务截面, 该截面包含补充的字段,只在真正需要给下游发送数据时,才将需要发送的数据放在 _diffs 中
129+
ext_diff = self._generate_ext_diff()
130+
rtn_data = {
131+
"aid": "rtn_data",
132+
"data": self._diffs + [ext_diff],
133+
}
134+
self._diffs = []
135+
self._pending_peek = False
136+
await self._api_recv_chan.send(rtn_data)

0 commit comments

Comments
 (0)