Skip to content

Commit

Permalink
Update Version 2.8.2
Browse files Browse the repository at this point in the history
  • Loading branch information
shinny-pack authored and shinny-mayanqiong committed Aug 17, 2021
1 parent 9afe7f3 commit 41d8f69
Show file tree
Hide file tree
Showing 9 changed files with 235 additions and 99 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: 2.8.1
Version: 2.8.2
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'2.8.1'
version = u'2.8.2'
# The full version, including alpha/beta/rc tags.
release = u'2.8.1'
release = u'2.8.2'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
117 changes: 68 additions & 49 deletions doc/usage/mddatas.rst
Original file line number Diff line number Diff line change
Expand Up @@ -79,59 +79,78 @@ SZSE 深圳证券交易所
----------------------------------------------------
:py:meth:`~tqsdk.api.TqApi.get_quote` 函数提供实时行情和合约信息::

q = api.get_quote("SHFE.cu1901")
q = api.get_quote("SHFE.cu2201")

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

{
"datetime": "", # "2017-07-26 23:04:21.000001" (行情从交易所发出的时间(北京时间))
"ask_price5": float("nan"), # 6122.0 (卖五价)
"ask_volume5": 0, # 3 (卖五量)
"ask_price4": float("nan"), # 6122.0 (卖四价)
"ask_volume4": 0, # 3 (卖四量)
"ask_price3": float("nan"), # 6122.0 (卖三价)
"ask_volume3": 0, # 3 (卖三量)
"ask_price2": float("nan"), # 6122.0 (卖二价)
"ask_volume2": 0, # 3 (卖二量)
"ask_price1": float("nan"), # 6122.0 (卖一价)
"ask_volume1": 0, # 3 (卖一量)
"bid_price1": float("nan"), # 6121.0 (买一价)
"bid_volume1": 0, # 7 (买一量)
"bid_price2": float("nan"), # 6121.0 (买二价)
"bid_volume2": 0, # 7 (买二量)
"bid_price3": float("nan"), # 6121.0 (买三价)
"bid_volume3": 0, # 7 (买三量)
"bid_price4": float("nan"), # 6121.0 (买四价)
"bid_volume4": 0, # 7 (买四量)
"bid_price5": float("nan"), # 6121.0 (买五价)
"bid_volume5": 0, # 7 (买五量)
"last_price": float("nan"), # 6122.0 (最新价)
"highest": float("nan"), # 6129.0 (当日最高价)
"lowest": float("nan"), # 6101.0 (当日最低价)
"open": float("nan"), # 6102.0 (开盘价)
"close": float("nan"), # nan (收盘价)
"average": float("nan"), # 6119.0 (当日均价)
"volume": 0, # 89252 (成交量)
"amount": float("nan"), # 5461329880.0 (成交额)
"open_interest": 0, # 616424 (持仓量)
"settlement": float("nan"), # nan (结算价)
"upper_limit": float("nan"), # 6388.0 (涨停价)
"lower_limit": float("nan"), # 5896.0 (跌停价)
"pre_open_interest": 0, # 616620 (昨持仓量)
"pre_settlement": float("nan"), # 6142.0 (昨结算价)
"pre_close": float("nan"), # 6106.0 (昨收盘价)
"price_tick": float("nan"), # 10.0 (合约价格单位)
"price_decs": 0, # 0 (合约价格小数位数)
"volume_multiple": 0, # 10 (合约乘数)
"max_limit_order_volume": 0, # 500 (最大限价单手数)
"max_market_order_volume": 0, # 0 (最大市价单手数)
"min_limit_order_volume": 0, # 1 (最小限价单手数)
"min_market_order_volume": 0, # 0 (最小市价单手数)
"underlying_symbol": "", # SHFE.rb1901 (标的合约)
"strike_price": float("nan"), # nan (行权价)
"change": float("nan"), # −20.0 (涨跌)
"change_percent": float("nan"), # −0.00325 (涨跌幅)
"expired": False, # False (合约是否已下市)
"datetime": "2021-08-17 14:59:59.000001", # 行情从交易所发出的时间(北京时间)
"ask_price1": 69750.0, # 卖一价
"ask_volume1": 1, # 卖一量
"bid_price1": 69600.0, # 买一价
"bid_volume1": 2, # 买一量
"ask_price2": 69920.0, # 卖二价
"ask_volume2": 3, # 卖二量
"bid_price2": 69500.0, # 买二价
"bid_volume2": 3, # 买二量
"ask_price3": 69940.0, # 卖三价
"ask_volume3": 1, # 卖三量
"bid_price3": 69450.0, # 买三价
"bid_volume3": 1, # 买三量
"ask_price4": 70010.0, # 卖四价
"ask_volume4": 1, # 卖四量
"bid_price4": 69400.0, # 买四价
"bid_volume4": 1, # 买四量
"ask_price5": 70050.0, # 卖五价
"ask_volume5": 1, # 卖五量
"bid_price5": 69380.0, # 买五价
"bid_volume5": 1, # 买五量
"last_price": 69710.0, # 最新价
"highest": 70050.0, # 当日最高价
"lowest": 69520.0, # 当日最低价
"open": 69770.0, # 开盘价
"close": 69710.0, # 收盘价
"average": 69785.019711, # 当日均价
"volume": 761, # 成交量
"amount": 265532000.0, # 成交额
"open_interest": 8850, # 持仓量
"settlement": 69780.0, # 结算价
"upper_limit": 75880.0, # 涨停价
"lower_limit": 64630.0, # 跌停价
"pre_open_interest": 8791, # 昨持仓量
"pre_settlement": 70260.0, # 昨结算价
"pre_close": 69680.0, # 昨收盘价
"price_tick": 10.0, # 合约价格变动单位
"price_decs": 0, # 合约价格小数位数
"volume_multiple": 5.0, # 合约乘数
"max_limit_order_volume": 500, # 最大限价单手数
"max_market_order_volume": 0, # 最大市价单手数
"min_limit_order_volume": 0, # 最小限价单手数
"min_market_order_volume": 0, # 最小市价单手数
"underlying_symbol": "", # 标的合约
"strike_price": NaN, # 行权价
"ins_class": "FUTURE", # 合约类型
"instrument_id": "SHFE.cu2201", # 合约代码
"instrument_name": "沪铜2201", # 合约中文名
"exchange_id": "SHFE", # 交易所代码
"expired": false, # 合约是否已下市
"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']]}", # 交易时间段
"expire_datetime": 1642402800.0, # 到期具体日,以秒为单位的 timestamp 值
"delivery_year": 2022, # 期货交割日年份,只对期货品种有效。期权推荐使用最后行权日年份
"delivery_month": 1, # 期货交割日月份,只对期货品种有效。期权推荐使用最后行权日月份
"last_exercise_datetime": NaN, # 期权最后行权日,以秒为单位的 timestamp 值
"exercise_year": 0, # 期权最后行权日年份,只对期权品种有效。
"exercise_month": 0, # 期权最后行权日月份,只对期权品种有效。
"option_class": "", # 期权行权方式,看涨:'CALL',看跌:'PUT'
"exercise_type": "", # 期权行权方式,美式:'A',欧式:'E'
"product_id": "cu", # 品种代码
"iopv": NaN, # ETF实时单位基金净值
"public_float_share_quantity": 0, # 日流通股数,只对证券产品有效。
"stock_dividend_ratio": [], # 除权表 ["20190601,0.15","20200107,0.2"…]
"cash_dividend_ratio": [], # 除息表 ["20190601,0.15","20200107,0.2"…]
"expire_rest_days": 153, # 距离到期日的剩余天数(自然日天数),正数表示距离到期日的剩余天数,0表示到期日当天,负数表示距离到期日已经过去的天数
"commission": 17.565,
"margin": 31617.0
}

对于每个合约, 只需要调用一次 get_quote 函数. 如果需要监控数据更新, 可以使用 :py:meth:`~tqsdk.api.TqApi.wait_update`::
Expand Down
8 changes: 8 additions & 0 deletions doc/version.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

版本变更
=============================
2.8.2 (2021/08/17)

* 增加:is_changing 接口增加对于合约 :py:meth:`~tqsdk.objs.Quote.expire_rest_days`,持仓 :py:meth:`~tqsdk.objs.Position.pos_long`、
:py:meth:`~tqsdk.objs.Position.pos_short`、:py:meth:`~tqsdk.objs.Position.pos` 字段支持判断是否更新
* 修复:2.8.1 版本重构后,不支持多线程运行的问题
* docs: 更新合约字段示例说明


2.8.1 (2021/08/12)

* 增加:增强在协程中的支持,以下接口 :py:meth:`~tqsdk.api.TqApi.query_quotes`,:py:meth:`~tqsdk.api.TqApi.query_cont_quotes`,
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def get_tag(self):

setuptools.setup(
name='tqsdk',
version="2.8.1",
version="2.8.2",
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__ = '2.8.1'
__version__ = '2.8.2'
14 changes: 10 additions & 4 deletions tqsdk/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
from tqsdk.backtest import TqBacktest, TqReplay
from tqsdk.channel import TqChan
from tqsdk.connect import TqConnect, MdReconnectHandler, TdReconnectHandler, ReconnectTimer
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_calendar
from tqsdk.diff import _merge_diff, _get_obj, _is_key_exist, _register_update_chan
Expand Down Expand Up @@ -258,6 +259,7 @@ def __init__(self, account: Union[TqMultiAccount, TqAccount, TqSim, None] = None
self._prototype = self._gen_prototype() # 各业务数据的原型, 用于决定默认值及将收到的数据转为特定的类型
self._security_prototype = self._gen_security_prototype() # 股票业务数据原型
self._dividend_cache = {} # 缓存合约对应的复权系数矩阵,每个合约只计算一次
self._send_chan, self._recv_chan = TqChan(self), TqChan(self) # 消息收发队列

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

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

self._send_chan._logger_bind(chan_from="api")
self._recv_chan._logger_bind(chan_to="api")

# 股票盈亏计算
if self._account._has_stock_account:
stock_profit_helper = TqStockProfit(self)
stock_send_chan, stock_recv_chan = TqChan(self), TqChan(self)
self.create_task(stock_profit_helper._run(stock_send_chan, stock_recv_chan, self._send_chan, self._recv_chan))
self._send_chan, self._recv_chan = stock_send_chan, stock_recv_chan

data_extension = DataExtension(self)
data_extension_send_chan = TqChan(self, chan_name="send to data_extension")
data_extension_recv_chan = TqChan(self, chan_name="recv from data_extension")
self.create_task(data_extension._run(data_extension_send_chan, data_extension_recv_chan, self._send_chan, self._recv_chan))
self._send_chan, self._recv_chan = data_extension_send_chan, data_extension_recv_chan
self._send_chan._logger_bind(chan_from="api")
self._recv_chan._logger_bind(chan_to="api")

def _fetch_symbol_info(self, url):
"""获取合约信息"""
rsp = requests.get(url, headers=self._base_headers, timeout=30)
Expand Down
136 changes: 136 additions & 0 deletions tqsdk/data_extension.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = 'mayanqiong'


from tqsdk.datetime import _get_expire_rest_days
from tqsdk.datetime_state import TqDatetimeState
from tqsdk.diff import _simple_merge_diff


class DataExtension(object):
"""
为数据截面添加以下字段:
{
quotes: {
*: {
expire_rest_days: int
}
},
trade: {
*: {
positions: {
*: {
'pos_long': int,
'pos_short': int,
'pos': int
}
}
}
}
}
"""

def __init__(self, api):
self._api = api
self._data = {'trade': {}} # 数据截面, 现在的功能只需要记录 trade
self._diffs = []

async def _run(self, api_send_chan, api_recv_chan, md_send_chan, md_recv_chan):
self._logger = self._api._logger.getChild("DataExtension")
self._api_send_chan = api_send_chan
self._api_recv_chan = api_recv_chan
self._md_send_chan = md_send_chan
self._md_recv_chan = md_recv_chan
self._datetime_state = TqDatetimeState()
md_task = self._api.create_task(self._md_handler())
self._pending_peek = False # True 表示收到下游的 peek_message ,并且没有发给过下游回复;False 表示发给过下游回复,没有 pending_peek_message
self._pending_peek_md = False # True 表示发给过上游 peek_message;False 表示对上游没有 pending_peek_message
try:
async for pack in api_send_chan:
if "_md_recv" in pack:
self._pending_peek_md = False
await self._md_recv(pack)
await self._send_diff()
if self._pending_peek and self._pending_peek_md is False:
self._pending_peek_md = True
await self._md_send_chan.send({"aid": "peek_message"})
elif pack["aid"] == "peek_message":
self._pending_peek = True
await self._send_diff()
if self._pending_peek and self._pending_peek_md is False:
self._pending_peek_md = True
await self._md_send_chan.send(pack)
else:
await self._md_send_chan.send(pack)
finally:
md_task.cancel()

async def _md_handler(self):
"""0 接收上游数据包 """
async for pack in self._md_recv_chan:
pack["_md_recv"] = True
await self._api_send_chan.send(pack)

async def _md_recv(self, pack):
"""将行情数据和交易数据合并至 self._data """
for d in pack.get("data", {}):
self._datetime_state.update_state(d)
if d.get('trade', None):
_simple_merge_diff(self._data['trade'], d['trade'], reduce_diff=False)
self._diffs.append(d)

def _generate_ext_diff(self):
""""
补充 quote, position 额外字段
此函数在 send_diff() 才会调用, self._datetime_state.data_ready 一定为 True,
调用 self._datetime_state.get_current_dt() 一定有正确的当前时间
"""
pend_diff = {}
for d in self._diffs:
if d.get('quotes', None):
_simple_merge_diff(pend_diff, self._update_quotes(d), reduce_diff=False)
if d.get('trade', None):
_simple_merge_diff(pend_diff, self._update_positions(d), reduce_diff=False)
return pend_diff

def _update_quotes(self, diff):
pend_diff = {}
for symbol in diff['quotes']:
expire_datetime = diff['quotes'].get(symbol, {}).get('expire_datetime', float('nan'))
if expire_datetime == expire_datetime:
# expire_rest_days 距离到期日的剩余天数(自然日天数)
# 正数表示距离到期日的剩余天数,0表示到期日当天,负数表示距离到期日已经过去的天数
expire_rest_days = _get_expire_rest_days(expire_datetime, self._datetime_state.get_current_dt() / 1e9)
pend_diff[symbol] = {'expire_rest_days': expire_rest_days}
return {'quotes': pend_diff} if pend_diff else {}

def _update_positions(self, diff):
pend_diff = {}
for account_key in diff['trade']:
for symbol in diff['trade'].get(account_key, {}).get('positions', {}):
pos = diff['trade'][account_key]['positions'][symbol]
if 'pos_long_his' in pos or 'pos_long_today' in pos or 'pos_short_his' in pos or 'pos_short_today' in pos:
data_pos = self._data['trade'][account_key]['positions'][symbol]
pos_long = data_pos['pos_long_his'] + data_pos['pos_long_today']
pos_short = data_pos['pos_short_his'] + data_pos['pos_short_today']
pend_diff.setdefault(account_key, {})
pend_diff[account_key].setdefault('positions', {})
pend_diff[account_key]['positions'][symbol] = {
'pos_long': pos_long,
'pos_short': pos_short,
'pos': pos_long - pos_short
}
return {'trade': pend_diff} if pend_diff else {}

async def _send_diff(self):
if self._datetime_state.data_ready and self._pending_peek and self._diffs:
# 生成增量业务截面, 该截面包含补充的字段,只在真正需要给下游发送数据时,才将需要发送的数据放在 _diffs 中
ext_diff = self._generate_ext_diff()
rtn_data = {
"aid": "rtn_data",
"data": self._diffs + [ext_diff],
}
self._diffs = []
self._pending_peek = False
await self._api_recv_chan.send(rtn_data)
Loading

0 comments on commit 41d8f69

Please sign in to comment.