Skip to content

Commit

Permalink
Update Version 3.2.7
Browse files Browse the repository at this point in the history
  • Loading branch information
shinny-pack authored and shinny-mayanqiong committed Apr 25, 2022
1 parent 574f509 commit 43ccfa2
Show file tree
Hide file tree
Showing 14 changed files with 136 additions and 49 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.2.6
Version: 3.2.7
Summary: TianQin SDK
Home-page: https://www.shinnytech.com/tqsdk
Author: TianQin
Expand Down
12 changes: 6 additions & 6 deletions doc/advanced/tqsdk2ctptest.rst
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
.. _tqsdk2ctptest:

tqsdk 中调用 tqsdk2 查询保证金
TqSdk 中调用 TqSdk2 查询保证金
=================================================
tqSdk 没有直接提供查询保证金的接口,但是你可以通过使用tqsdk2 的直连功能来做到这个效果。tqsdk和tqsdk2可以在一个py文件中同时运行。
TqSdk 没有直接提供查询保证金的接口,但是你可以通过使用 TqSdk2 的直连功能来做到这个效果。tqsdk和tqsdk2可以在一个py文件中同时运行。

该方法仅支持直连 CTP 柜台时使用。受限制于 CTP 柜台的流控机制(每秒 1 笔), 短时间发送大量查询指令后, 后续查询指令将会排队等待。
该方法仅支持 TqSdk2 中直连CTP 柜台时使用。受限制于 CTP 柜台的流控机制(每秒 1 笔), 短时间发送大量查询指令后, 后续查询指令将会排队等待。
为了避免盘中的查询等待时间, 建议盘前启动程序, 对标的合约提前进行查询::

from tqsdk import TqApi, TqAuth, TqAccount
Expand All @@ -18,9 +18,9 @@ tqSdk 没有直接提供查询保证金的接口,但是你可以通过使用tq
quote = api.get_quote("SHFE.cu2201")
while True:
api.wait_update()
quote.datetime
print(quote.datetime)
# 正常和tqsdk一样执行策略


tqsdk2的直连功能需要企业版权限,有关企业版的具体费用和功能,请参考 `天勤官方网站 <https://www.shinnytech.com/tqsdk_professional/>`_
如果想了解更多关于tqsdk2的直连功能TqCtp,请参考 `tqsdk2官方文档 <https://doc.shinnytech.com/tqsdk2/latest/reference/tqsdk2.ctp.html?highlight=tqctp#tqsdk2.TqCtp/>`_
TqSdk2 的直连功能需要企业版权限,有关企业版的具体费用和功能,请参考 `天勤官方网站 <https://www.shinnytech.com/tqsdk_professional/>`_
如果想了解更多关于 TqSdk2 的直连功能TqCtp,请参考 `tqsdk2官方文档 <https://doc.shinnytech.com/tqsdk2/latest/reference/tqsdk2.ctp.html?highlight=tqctp#tqsdk2.TqCtp/>`_
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.2.6'
version = u'3.2.7'
# The full version, including alpha/beta/rc tags.
release = u'3.2.6'
release = u'3.2.7'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
6 changes: 6 additions & 0 deletions doc/demo/option_base.rst
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,10 @@ o73 - 查询标的对应期权按虚值平值实值分类方法二
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. literalinclude:: ../../tqsdk/demo/option_tutorial/o73.py
:language: python

o74 - 本地计算ETF期权卖方开仓保证金
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. literalinclude:: ../../tqsdk/demo/option_tutorial/o74.py
:language: python
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.2.7 (2022/04/22)

* 优化:对多线程用例,增加可能的错误提示
* 优化:TqApi 的 debug 默认值修改为 None,且 debug 为 None 情况下在磁盘剩余空间大于 3G 时才可能开启日志
* docs:增加 ETF 期权本地计算卖方保证金示例 o74,完善 targetpostask 的示例文档,完善 Position 下 orders 定义,统一修正文档大小写、变量命名等


3.2.6 (2022/03/09)

* 修复:修正深交所 ETF 期权的昨结算(pre_settlement)字段未正确显示的问题
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="3.2.6",
version="3.2.7",
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.2.6'
__version__ = '3.2.7'
31 changes: 18 additions & 13 deletions tqsdk/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
from tqsdk.diff import _merge_diff, _get_obj, _is_key_exist, _register_update_chan
from tqsdk.entity import Entity
from tqsdk.exceptions import TqTimeoutError
from tqsdk.log import _get_log_name, _clear_logs
from tqsdk.log import _clear_logs, _get_log_name, _get_disk_free
from tqsdk.objs import Quote, TradingStatus, Kline, Tick, Account, Position, Order, Trade, QuotesEntity, RiskManagementRule, RiskManagementData
from tqsdk.objs import SecurityAccount, SecurityOrder, SecurityTrade, SecurityPosition
from tqsdk.objs_not_entity import QuoteList, TqDataFrame, TqSymbolDataFrame, SymbolList, SymbolLevelList, \
Expand Down Expand Up @@ -88,7 +88,7 @@ class TqApi(TqBaseApi):

def __init__(self, account: Optional[Union[TqMultiAccount, UnionTradeable]] = None, auth: Union[TqAuth, str, None] = None,
url: Optional[str] = None, backtest: Union[TqBacktest, TqReplay, None] = None,
web_gui: Union[bool, str] = False, debug: Union[bool, str, None] = False,
web_gui: Union[bool, str] = False, debug: Union[bool, str, None] = None,
loop: Optional[asyncio.AbstractEventLoop] = None, disable_print: bool = False, _stock: bool = True,
_ins_url=None, _md_url=None, _td_url=None) -> None:
"""
Expand Down Expand Up @@ -128,10 +128,11 @@ def __init__(self, account: Optional[Union[TqMultiAccount, UnionTradeable]] = No
在回测模式下, TqBacktest 连接 wss://backtest.shinnytech.com/t/md/front/mobile 接收行情数据, \
由 TqBacktest 内部完成回测时间段内的行情推进和 K 线、Tick 更新.
debug(bool/str): [可选] 是否将调试信息输出到指定文件,默认值为 False
debug(bool/str): [可选] 是否将调试信息输出到指定文件,默认值为 None
* None [默认]: 根据账户情况不同,默认值的行为不同。
+ 当有以下账户之一时,:py:class:`~tqsdk.TqAccount`、:py:class:`~tqsdk.TqKq`、:py:class:`~tqsdk.TqKqStock` 账户时,调试信息输出到指定文件夹 `~/.tqsdk/logs`。
+ 当有以下账户之一时,:py:class:`~tqsdk.TqAccount`、:py:class:`~tqsdk.TqKq`、:py:class:`~tqsdk.TqKqStock` 账户时,\
调试信息输出到指定文件夹 `~/.tqsdk/logs`(如果磁盘剩余空间不足 3G 则不会输出调试信息)。
+ 其他情况,即仅有本地模拟账户 :py:class:`~tqsdk.TqSim`、:py:class:`~tqsdk.TqSimStock` 时,调试信息不输出。
Expand Down Expand Up @@ -271,7 +272,9 @@ def __init__(self, account: Optional[Union[TqMultiAccount, UnionTradeable]] = No
raise Exception("不可以为slave再创建slave")
self._master._slaves.append(self)
self._account = self._master._account
self._web_gui = False # 如果是slave, _web_gui 一定是 False
if isinstance(self._account, TqMultiAccount):
warnings.warn("TqSdk 多账户功能暂不支持在多线程下使用")
self._web_gui = False # 如果是slave, _web_gui 一定是 False
return # 注: 如果是slave,则初始化到这里结束并返回,以下代码不执行

self._web_gui = web_gui
Expand Down Expand Up @@ -349,7 +352,6 @@ def close(self) -> None:
super(TqApi, self)._close()
mem = psutil.virtual_memory()
self._logger.debug("process end", mem_total=mem.total, mem_free=mem.free)
_clear_logs() # 清除过期日志文件

def __enter__(self):
return self
Expand Down Expand Up @@ -591,7 +593,7 @@ def get_kline_serial(self, symbol: Union[str, List[str]], duration_seconds: int,
注意: 周期在日线以内时此参数可以任意填写, 在日线以上时只能是日线(86400)的整数倍, 最大为28天 (86400*28)。
data_length (int): 需要获取的序列长度。默认200根, 返回的K线序列数据是从当前最新一根K线开始往回取data_length根。\
每个序列最大支持请求 8964 个数据
每个序列最大支持请求 8000 个数据
chart_id (str): [可选]指定序列id, 默认由 api 自动生成
Expand All @@ -607,7 +609,7 @@ def get_kline_serial(self, symbol: Union[str, List[str]], duration_seconds: int,
3. 若设置了较大的序列长度参数,而所有可对齐的数据并没有这么多,则序列前面部分数据为NaN(这与获取单合约K线且数据不足序列长度时情况相似)。
4. 若主合约与副合约的交易时间在所有合约数据中最晚一根K线时间开始往回的 8964*周期 时间段内完全不重合,则无法生成多合约K线,程序会报出获取数据超时异常。
4. 若主合约与副合约的交易时间在所有合约数据中最晚一根K线时间开始往回的 8000*周期 时间段内完全不重合,则无法生成多合约K线,程序会报出获取数据超时异常。
5. datetime、duration是所有合约公用的字段,则未单独为每个副合约增加一份副本,这两个字段使用原始字段名(即没有数字后缀)。
Expand Down Expand Up @@ -737,7 +739,7 @@ def get_tick_serial(self, symbol: str, data_length: int = 200, chart_id: Optiona
Args:
symbol (str): 指定合约代码.
data_length (int): 需要获取的序列长度。每个序列最大支持请求 8964 个数据
data_length (int): 需要获取的序列长度。每个序列最大支持请求 8000 个数据
chart_id (str): [可选]指定序列id, 默认由 api 自动生成
Expand Down Expand Up @@ -3094,11 +3096,14 @@ def _setup_connection(self):
# TqWebHelper 初始化可能会修改 self._account、self._backtest,所以在这里才初始化 logger
# 在此之前使用 self._logger 不会打印日志
if not self._logger.handlers and (self._debug or (self._account._has_tq_account and self._debug is not False)):
_clear_logs() # 先清空日志
log_name = self._debug if isinstance(self._debug, str) else _get_log_name()
fh = logging.FileHandler(filename=log_name)
fh.setFormatter(JSONFormatter())
fh.setLevel(logging.DEBUG)
self._logger.addHandler(fh)
if self._debug is not None or _get_disk_free() >= 3:
# self._debug is None 并且磁盘剩余空间小于 3G 则不写入日志
fh = logging.FileHandler(filename=log_name)
fh.setFormatter(JSONFormatter())
fh.setLevel(logging.DEBUG)
self._logger.addHandler(fh)
mem = psutil.virtual_memory()
self._logger.debug("process start", product="tqsdk-python", version=__version__, os=platform.platform(),
py_version=platform.python_version(), py_arch=platform.architecture()[0],
Expand Down
2 changes: 1 addition & 1 deletion tqsdk/demo/option_tutorial/o20.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
ls = api.query_options("SHFE.au2012", strike_price=340)
print(ls) # 标的为 "SHFE.au2012" 、行权价为 340 的期权

ls = api.query_options("SSE.510300", exchange_id="CFFEX")
ls = api.query_options("SSE.000300", exchange_id="CFFEX")
print(ls) # 中金所沪深300股指期权

ls = api.query_options("SSE.510300", exchange_id="SSE")
Expand Down
39 changes: 39 additions & 0 deletions tqsdk/demo/option_tutorial/o74.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from tqsdk import TqApi, TqAuth

'''
根据输入的ETF期权来查询该期权的交易所规则下的理论卖方保证金,实际情况请以期货公司收取的一手保证金为准
'''

def etf_margin_cal(symbol):
quote_etf = api.get_quote(symbol)
# 判断期权标的是不是ETF
if quote_etf.underlying_symbol in ["SSE.510050", "SSE.510300", "SZSE.159919"]:
if quote_etf.option_class == "CALL":
# 认购期权虚值=Max(行权价-合约标的前收盘价,0)
call_out_value = max(quote_etf.strike_price - quote_etf.underlying_quote.pre_close, 0)
# 认购期权义务仓开仓保证金=[合约前结算价+Max(12%×合约标的前收盘价-认购期权虚值,7%×合约标的前收盘价)]×合约单位
call_margin = (quote_etf.pre_settlement + max(0.12 * quote_etf.underlying_quote.pre_close - call_out_value,
0.07 * quote_etf.underlying_quote.pre_close)) * quote_etf.volume_multiple
return round(call_margin, 2)
elif quote_etf.option_class == "PUT":
# 认沽期权虚值=Max(合约标的前收盘价-行权价,0)
put_out_value = max(quote_etf.underlying_quote.pre_close - quote_etf.strike_price, 0)
# 认沽期权义务仓开仓保证金=Min[合约前结算价+Max(12%×合约标的前收盘价-认沽期权虚值,7%×行权价),行权价]×合约单位。
put_margin = min(quote_etf.pre_settlement + max(0.12 * quote_etf.underlying_quote.pre_close - put_out_value,
0.07 * quote_etf.strike_price),
quote_etf.strike_price) * quote_etf.volume_multiple
return round(put_margin, 2)
else:
print("输入的不是ETF期权合约")
return None


# 创建api
api = TqApi(auth=TqAuth("信易账户", "账户密码"))

# 深交所300etf期权
symbol = "SZSE.90000833"

print(etf_margin_cal(symbol))

api.close()
30 changes: 16 additions & 14 deletions tqsdk/lib/target_pos_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,22 +147,24 @@ def get_price(direction):
Example2::
# ... 用户代码 ...
quote1 = api.get_quote("SHFE.cu2012")
quote2 = api.get_quote("SHFE.au2012")
quote_list = api.get_quote_list(["SHFE.cu2012","SHFE.au2012"])
def get_price(direction, quote):
# 在 BUY 时使用买一价加一档价格,SELL 时使用卖一价减一档价格
if direction == "BUY":
price = quote.bid_price1 + quote.price_tick
else:
price = quote.ask_price1 - quote.price_tick
# 如果 price 价格是 nan,使用最新价报单
if price != price:
price = quote.last_price
return price
def get_price_by_quote(quote):
def get_price(direction):
# 在 BUY 时使用买一价加一档价格,SELL 时使用卖一价减一档价格
if direction == "BUY":
price = quote["upper_limit"]
else:
price = quote.lower_limit
# 如果 price 价格是 nan,使用最新价报单
if price != price:
price = quote.last_price
return price
return get_price
for quote in quote_list:
target_pos_active_dict[quote.instrument_id] = TargetPosTask(api, quote.instrument_id, price=get_price_by_quote(quote))
target_pos1 = TargetPosTask(api, "SHFE.cu2012", price=lambda direction: get_price(direction, quote1))
target_pos2 = TargetPosTask(api, "SHFE.au2012", price=lambda direction: get_price(direction, quote2))
# ... 用户代码 ...
Example3::
Expand Down
44 changes: 36 additions & 8 deletions tqsdk/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import datetime
import os

import psutil

DEBUG_DIR = os.path.join(os.path.expanduser('~'), ".tqsdk/logs")

Expand All @@ -16,16 +17,43 @@ def _get_log_name():
return os.path.join(DEBUG_DIR, f"{datetime.datetime.now().strftime('%Y%m%d%H%M%S%f')}-{os.getpid()}.log")


def _get_disk_free():
free = psutil.disk_usage(DEBUG_DIR).free
return free / 1e9


def _log_path_list():
# 获取所有日志文件路径,并按照修改时间递减排序
path_list = [os.path.join(DEBUG_DIR, log) for log in os.listdir(DEBUG_DIR)]
path_list.sort(key=lambda x: _stat_dt(x))
return path_list


def _stat_dt(path):
try:
return datetime.datetime.fromtimestamp(os.stat(path).st_mtime)
except:
return datetime.datetime.now()


def _remove_log(path):
try:
os.remove(path)
except:
pass # 忽略抛错


def _clear_logs():
"""清除最后修改时间是 n 天前的日志"""
if not os.path.exists(DEBUG_DIR):
return
n = os.getenv("TQ_SAVE_LOG_DAYS", 30)
dt = datetime.datetime.now() - datetime.timedelta(days=int(n))
for log in os.listdir(DEBUG_DIR):
path = os.path.join(DEBUG_DIR, log)
try:
if datetime.datetime.fromtimestamp(os.stat(path).st_mtime) < dt:
os.remove(path)
except:
pass # 忽略抛错
# 清除最后修改时间是 n 天前的日志
# 清空日志保证剩余空间大于 3G,但是最近 3 个自然日的一定不会清除,保证最近的一个交易日不会被清除日志
dt30 = datetime.datetime.now() - datetime.timedelta(days=int(n))
dt3 = datetime.datetime.now() - datetime.timedelta(days=int(3))
for path in _log_path_list():
if _stat_dt(path) < dt30 or (_get_disk_free() < 3 and _stat_dt(path) < dt3):
_remove_log(path)
else:
break
2 changes: 1 addition & 1 deletion tqsdk/objs.py
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@ def __init__(self, api):
@property
def orders(self):
"""
与此持仓相关的开仓/平仓挂单
与此持仓相关的且目前委托单状态为ALIVE的开仓/平仓挂单
:return: dict, 其中每个元素的key为委托单ID, value为 :py:class:`~tqsdk.objs.Order`
"""
Expand Down
2 changes: 1 addition & 1 deletion tqsdk/tradeable/otg/tqkq.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def __init__(self, td_url: Optional[str] = None):
from tqsdk import TqApi, TqAuth, TqKq
tq_kq_stock = TqKq()
tq_kq = TqKq()
api = TqApi(account=tq_kq, auth=TqAuth("信易账户", "账户密码"))
quote = api.get_quote("SHFE.cu2206")
print(quote)
Expand Down

0 comments on commit 43ccfa2

Please sign in to comment.