Skip to content

Commit 2c461ea

Browse files
修改了一大波文件,发布支持 web_gui 版本
1 parent d920a00 commit 2c461ea

File tree

17 files changed

+212
-204
lines changed

17 files changed

+212
-204
lines changed

setup.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,7 @@ def get_tag(self):
5353
"License :: OSI Approved :: Apache Software License",
5454
"Operating System :: OS Independent",
5555
],
56+
package_data={
57+
"tqsdk": ["web"]
58+
}
5659
)

tqsdk/api.py

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ class TqApi(object):
5353
DEFAULT_INS_URL = "https://openmd.shinnytech.com/t/md/symbols/latest.json"
5454

5555
def __init__(self, account: Union['TqAccount', TqSim, 'TqApi', None] = None, url: Optional[str] = None,
56-
backtest: Optional[TqBacktest] = None, debug: Optional[str] = None,
57-
loop: Optional[asyncio.AbstractEventLoop] = None, _ins_url=None, _md_url=None, _td_url=None, web_gui=False, _http_server_port=None) -> None:
56+
backtest: Optional[TqBacktest] = None, web_gui: bool = False, debug: Optional[str] = None,
57+
loop: Optional[asyncio.AbstractEventLoop] = None, _ins_url=None, _md_url=None, _td_url=None) -> None:
5858
"""
5959
创建天勤接口实例
6060
@@ -87,25 +87,33 @@ def __init__(self, account: Union['TqAccount', TqSim, 'TqApi', None] = None, url
8787
* 为了图形化界面能够接收到程序传输的数据并且刷新,在程序中,需要循环调用 api.wait_update的形式去更新和获取数据
8888
* 推荐打开图形化界面的浏览器为Google Chrome 或 Firefox
8989
90-
Example::
90+
Example1::
9191
9292
# 使用实盘帐号直连行情和交易服务器
9393
from tqsdk import TqApi, TqAccount
9494
api = TqApi(TqAccount("H海通期货", "022631", "123456"))
9595
96+
Example2::
97+
9698
# 使用模拟帐号直连行情服务器
9799
from tqsdk import TqApi, TqSim
98100
api = TqApi(TqSim()) # 不填写参数则默认为 TqSim() 模拟账号
99101
102+
Example3::
103+
100104
# 进行策略回测
101105
from datetime import date
102106
from tqsdk import TqApi, TqSim, TqBacktest
103107
api = TqApi(TqSim(), backtest=TqBacktest(start_dt=date(2018, 5, 1), end_dt=date(2018, 10, 1)))
104108
109+
Example4::
110+
105111
# 开启 web_gui 功能
106112
from tqsdk import TqApi
107113
api = TqApi(web_gui=True)
114+
108115
"""
116+
109117
# 记录参数
110118
if account is None:
111119
account = TqSim()
@@ -126,10 +134,8 @@ def __init__(self, account: Union['TqAccount', TqSim, 'TqApi', None] = None, url
126134
if _td_url:
127135
self._td_url = _td_url
128136
self._loop = asyncio.SelectorEventLoop() if loop is None else loop # 创建一个新的 ioloop, 避免和其他框架/环境产生干扰
129-
self._tq_web_helper = TqWebHelper(_http_server_port=_http_server_port, enabled_web_gui=web_gui)
130137

131138
# 初始化 logger
132-
133139
self._logger = logging.getLogger("TqApi")
134140
self._logger.setLevel(logging.DEBUG)
135141
if not self._logger.handlers:
@@ -183,8 +189,10 @@ def __init__(self, account: Union['TqAccount', TqSim, 'TqApi', None] = None, url
183189
self._master._slaves.append(self)
184190
self._account = self._master._account
185191
self._to_tq = self._master._to_tq
192+
self._web_gui = False # 如果是slave, _web_gui 一定是 False
186193
return # 注: 如果是slave,则初始化到这里结束并返回,以下代码不执行
187194

195+
self._web_gui = web_gui
188196
# 初始化
189197
if sys.platform.startswith("win"):
190198
self.create_task(self._windows_patch()) # Windows系统下asyncio不支持KeyboardInterrupt的临时补丁
@@ -1129,11 +1137,10 @@ def _setup_connection(self):
11291137
self._account._run(self, self._send_chan, self._recv_chan, ws_md_send_chan, ws_md_recv_chan,
11301138
ws_td_send_chan, ws_td_recv_chan))
11311139

1132-
# 与web配合, 行情和交易都要 对接到 backtest 上
1133-
if self._tq_web_helper:
1134-
web_send_chan, web_recv_chan = TqChan(self), TqChan(self)
1135-
self.create_task(self._tq_web_helper._run(self, web_send_chan, web_recv_chan, self._send_chan, self._recv_chan))
1136-
self._send_chan, self._recv_chan = web_send_chan, web_recv_chan
1140+
# 与 web 配合, 在 tq_web_helper 内部中处理 web_gui 选项
1141+
web_send_chan, web_recv_chan = TqChan(self), TqChan(self)
1142+
self.create_task(TqWebHelper()._run(self, web_send_chan, web_recv_chan, self._send_chan, self._recv_chan))
1143+
self._send_chan, self._recv_chan = web_send_chan, web_recv_chan
11371144

11381145
# 抄送部分数据包到天勤
11391146
if tq_send_chan and tq_recv_chan:
@@ -1451,7 +1458,7 @@ def _process_chart_data_for_web(self, serial, symbol, duration, col, count, righ
14511458
"color": int(data.get(".color", [0xFFFF0000])[-1]),
14521459
"width": int(data.get(".width", [1])[-1]),
14531460
"board": data.get(".board", ["MAIN"])[-1]
1454-
})
1461+
}, aid="set_web_chart_data")
14551462
elif data_type == "KSERIAL":
14561463
send_data = {}
14571464
range_left = right - count
@@ -1468,11 +1475,11 @@ def _process_chart_data_for_web(self, serial, symbol, duration, col, count, righ
14681475
"range_left": right - count,
14691476
"range_right": right - 1,
14701477
"board": data.get(".board", ["MAIN"])[-1]
1471-
})
1478+
}, aid="set_web_chart_data")
14721479

1473-
def _send_series_data(self, symbol, duration, serial_id, serial_data):
1480+
def _send_series_data(self, symbol, duration, serial_id, serial_data, aid="set_chart_data"):
14741481
pack = {
1475-
"aid": "set_chart_data",
1482+
"aid": aid,
14761483
"symbol": symbol,
14771484
"dur_nano": duration,
14781485
"datas": {
@@ -1551,8 +1558,7 @@ async def _connect(self, url, send_chan, recv_chan):
15511558
}) as client:
15521559
# 发送网络连接建立的通知,code = 2019112901
15531560
notify_id = uuid.UUID(int=TqApi.RD.getrandbits(128)).hex
1554-
notify = {}
1555-
notify[notify_id] = {
1561+
notify = {
15561562
"type": "MESSAGE",
15571563
"level": "INFO",
15581564
"code": 2019112901,
@@ -1561,6 +1567,8 @@ async def _connect(self, url, send_chan, recv_chan):
15611567
}
15621568

15631569
if not first_connect: # 如果不是第一次连接, 即为重连
1570+
# 发送网络连接重新建立的通知,code = 2019112902
1571+
notify["code"] = 2019112902
15641572
notify["level"] = "WARNING"
15651573
notify["content"] = "与 %s 的网络连接已恢复" % url
15661574
un_processed = True # 重连后数据未处理完
@@ -1570,10 +1578,10 @@ async def _connect(self, url, send_chan, recv_chan):
15701578
if url == self._md_url: # 获取重连时需发送的所有 set_chart 指令包
15711579
set_chart_packs = {k: v for k, v in resend_request.items() if v.get("aid") == "set_chart"}
15721580

1573-
# 发送网络连接建立的通知,code = 2019112901,这里区分了第一次连接和重连
1581+
# 发送网络连接建立的通知,code = 2019112901 or 2019112902,这里区分了第一次连接和重连
15741582
await recv_chan.send({
15751583
"aid": "rtn_data",
1576-
"data": [{"notify": notify}]
1584+
"data": [{"notify": {notify_id: notify}}]
15771585
})
15781586
send_task = self.create_task(
15791587
self._send_handler(client, url, resend_request, send_chan, first_connect))
@@ -1682,19 +1690,18 @@ async def _connect(self, url, send_chan, recv_chan):
16821690
# 希望做到的效果是遇到网络问题可以断线重连, 但是可能抛出的例外太多了(TimeoutError,socket.gaierror等), 又没有文档或工具可以理出 try 代码中所有可能遇到的例外
16831691
# 而这里的 except 又需要处理所有子函数及子函数的子函数等等可能抛出的例外, 因此这里只能遇到问题之后再补, 并且无法避免 false positive 和 false negative
16841692
except (websockets.exceptions.ConnectionClosed, OSError):
1685-
# 发送网络连接断开的通知,code = 2019112902
1693+
# 发送网络连接断开的通知,code = 2019112911
16861694
notify_id = uuid.UUID(int=TqApi.RD.getrandbits(128)).hex
1687-
notify = {}
1688-
notify[notify_id] = {
1695+
notify = {
16891696
"type": "MESSAGE",
16901697
"level": "WARNING",
1691-
"code": 2019112902,
1698+
"code": 2019112911,
16921699
"content": "与 %s 的网络连接断开,请检查客户端及网络是否正常" % url,
16931700
"url": url
16941701
}
16951702
await recv_chan.send({
16961703
"aid": "rtn_data",
1697-
"data": [{"notify": notify}]
1704+
"data": [{"notify": {notify_id: notify}}]
16981705
})
16991706
finally:
17001707
if first_connect:

tqsdk/backtest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ async def _send_diff(self):
200200
if not self._serials: # 当无可发送数据时则抛出BacktestFinished例外,包括未订阅任何行情 或 所有已订阅行情的最后一笔行情获取完成
201201
self._logger.warning("回测结束")
202202
if self._current_dt < self._end_dt:
203-
self._current_dt = 150000000000000000000 # 一个远大于 end_dt 的日期
203+
self._current_dt = 2145888000000000000 # 一个远大于 end_dt 的日期 20380101
204204
await self._sim_recv_chan.send({
205205
"aid": "rtn_data",
206206
"data": [{

tqsdk/demo/tutorial/backtest.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,15 @@
33
__author__ = 'chengzhi'
44

55
from datetime import date
6-
from contextlib import closing
7-
from tqsdk import TqApi, TqSim, TqBacktest, TargetPosTask
6+
from tqsdk import TqApi, TqBacktest, TargetPosTask
87

98
'''
109
如果当前价格大于5分钟K线的MA15则开多仓
1110
如果小于则平仓
1211
回测从 2018-05-01 到 2018-10-01
1312
'''
1413
# 在创建 api 实例时传入 TqBacktest 就会进入回测模式
15-
api = TqApi(TqSim(init_balance=100000), backtest=TqBacktest(start_dt=date(2018, 5, 2), end_dt=date(2018, 5, 5)))
14+
api = TqApi(backtest=TqBacktest(start_dt=date(2018, 5, 1), end_dt=date(2018, 10, 1)))
1615
# 获得 m1901 5分钟K线的引用
1716
klines = api.get_kline_serial("DCE.m1901", 5 * 60, data_length=15)
1817
# 创建 m1901 的目标持仓 task,该 task 负责调整 m1901 的仓位到指定的目标仓位

tqsdk/demo/tutorial/t10.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#!/usr/bin/env python
22
# -*- coding: utf-8 -*-
3+
__author__ = 'chengzhi'
4+
35
from tqsdk import TqApi
46

57
# 创建API实例.

tqsdk/demo/tutorial/t90.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,15 @@
77

88
'''
99
画图示例: 在主图中画指标线
10-
注意:1 画图功能仅在天勤终端/天勤Vscode插件中生效,请在这两个平台中运行画图相关的代码
11-
2 画图示例中用到的数据不含有实际意义,请根据自己的实际策略情况进行修改
10+
注意: 画图示例中用到的数据不含有实际意义,请根据自己的实际策略情况进行修改
1211
'''
1312

14-
api = TqApi(web_gui=True)
13+
api = TqApi(web_gui=True) # web_gui=True, 开启使用 web 界面查看绘图结果的功能
1514
klines = api.get_kline_serial("SHFE.au2006", 5)
1615

17-
ma = MA(klines, 30) # 使用tqsdk自带指标函数计算均线
18-
klines["ma_MAIN"] = ma.ma
19-
# 在主图中画一根默认颜色(红色)的ma指标线
16+
ma = MA(klines, 30) # 使用 tqsdk 自带指标函数计算均线
17+
klines["ma_MAIN"] = ma.ma # 在主图中画一根默认颜色(红色)的 ma 指标线
2018

19+
# 由于需要在浏览器中查看绘图结果,因此程序不能退出
2120
while True:
2221
api.wait_update()
23-
if api.is_changing(klines):
24-
ma = MA(klines, 30) # 使用tqsdk自带指标函数计算均线
25-
klines["ma_MAIN"] = ma.ma

tqsdk/demo/tutorial/t91.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,24 @@
77

88
'''
99
画图示例: 在附图中画指标线
10-
注意:1 画图功能仅在天勤终端/天勤Vscode插件中生效,请在这两个平台中运行画图相关的代码
11-
2 画图示例中用到的数据不含有实际意义,请根据自己的实际策略情况进行修改
10+
注意: 画图示例中用到的数据不含有实际意义,请根据自己的实际策略情况进行修改
1211
'''
1312

14-
api = TqApi(web_gui=True)
13+
api = TqApi(web_gui=True) # web_gui=True, 开启使用 web 界面查看绘图结果的功能
1514
klines = api.get_kline_serial("SHFE.au1910", 24 * 60 * 60)
1615
ma = MA(klines, 30) # 使用tqsdk自带指标函数计算均线
1716

18-
while True:
19-
# 示例: 在附图中画一根绿色的ma指标线
20-
klines["ma_B2"] = ma.ma
21-
klines["ma_B2.board"] = "B2" # 设置附图: 可以设置任意字符串,同一字符串表示同一副图
22-
klines["ma_B2.color"] = 0xFF00FF00 # 设置为绿色
17+
# 示例: 在附图中画一根绿色的ma指标线
18+
klines["ma_B2"] = ma.ma
19+
klines["ma_B2.board"] = "B2" # 设置附图: 可以设置任意字符串,同一字符串表示同一副图
20+
klines["ma_B2.color"] = 0xFF00FF00 # 设置为绿色
2321

24-
# 示例: 在另一个附图画一根比ma小4的宽度为4的紫色指标线
25-
klines["ma_4"] = ma.ma - 4
26-
klines["ma_4.board"] = "MA4" # 设置为另一个附图
27-
klines["ma_4.color"] = 0xFF9933CC # 设置为紫色
28-
klines["ma_4.width"] = 4 # 设置宽度为4,默认为1
22+
# 示例: 在另一个附图画一根比ma小4的宽度为4的紫色指标线
23+
klines["ma_4"] = ma.ma - 4
24+
klines["ma_4.board"] = "MA4" # 设置为另一个附图
25+
klines["ma_4.color"] = 0xFF9933CC # 设置为紫色
26+
klines["ma_4.width"] = 4 # 设置宽度为4,默认为1
2927

28+
# 由于需要在浏览器中查看绘图结果,因此程序不能退出
29+
while True:
3030
api.wait_update()

tqsdk/demo/tutorial/t92.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,20 @@
66

77
'''
88
画图示例: 在主图中画信号线及文字标注
9-
注意:1 画图功能仅在天勤终端/天勤Vscode插件中生效,请在这两个平台中运行画图相关的代码
10-
2 画图示例中用到的数据不含有实际意义,请根据自己的实际策略情况进行修改
9+
注意: 画图示例中用到的数据不含有实际意义,请根据自己的实际策略情况进行修改
1110
'''
1211

13-
api = TqApi(web_gui=True)
14-
klines = api.get_kline_serial("SHFE.au2002", 60)
12+
api = TqApi(web_gui=True) # web_gui=True, 开启使用 web 界面查看绘图结果的功能
13+
klines = api.get_kline_serial("SHFE.au2002", 300)
1514

15+
# 在主图中最后一根K线上画射线以标注需要的信号
16+
api.draw_line(klines, -1, klines.iloc[-1].close, -1, klines.iloc[-1].high, line_type="RAY", color=0xFFFF9900, width=3)
17+
# 绘制字符串
18+
api.draw_text(klines, "信号1", x=-1, y=klines.iloc[-1].high + 5, color=0xFFFF3333)
19+
# 给主图最后5根K线加一个方框
20+
api.draw_box(klines, x1=-5, y1=klines.iloc[-5]["high"], x2=-1, y2=klines.iloc[-1]["low"], width=1, color=0xFF0000FF,
21+
bg_color=0x7000FF00)
22+
23+
# 由于需要在浏览器中查看绘图结果,因此程序不能退出
1624
while True:
17-
# 在主图中最后一根K线上画射线以标注需要的信号
18-
api.draw_line(klines, -1, klines.iloc[-1].close, -1, klines.iloc[-1].high, id="my_line", line_type="RAY", color=0xFFFF9900, width=3)
19-
# 绘制字符串
20-
api.draw_text(klines, "信号1", x=-1, y=klines.iloc[-1].high + 5, id="my_text", color=0xFFFF3333)
2125
api.wait_update()

tqsdk/demo/tutorial/t93.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,21 @@
66

77
'''
88
画图示例: 在主图中画线和方框
9-
注意:1 画图功能仅在天勤终端/天勤Vscode插件中生效,请在这两个平台中运行画图相关的代码
10-
2 画图示例中用到的数据不含有实际意义,请根据自己的实际策略情况进行修改
9+
注意: 画图示例中用到的数据不含有实际意义,请根据自己的实际策略情况进行修改
1110
'''
1211

13-
api = TqApi(web_gui=True)
14-
klines = api.get_kline_serial("SHFE.cu1910", 86400)
12+
api = TqApi(web_gui=True) # web_gui=True, 开启使用 web 界面查看绘图结果的功能
13+
klines = api.get_kline_serial("SHFE.au2002", 60)
1514

15+
# 由于需要在浏览器中查看绘图结果,因此程序不能退出
1616
while True:
17-
# 在主图中画直线
18-
api.draw_line(klines, -4, klines.iloc[-4].low, -3, klines.iloc[-3].high, line_type="LINE", color=0xFF0000FF)
19-
# 给主图最后5根K线加一个方框
20-
api.draw_box(klines, x1=-5, y1=klines.iloc[-5]["high"], x2=-1, y2=klines.iloc[-1]["low"], width=1, color=0xFF0000FF,
21-
bg_color=0x7000FF00)
22-
api.wait_update()
17+
api.wait_update() # 当有业务信息发生变化时执行
18+
# 当最后 1 根柱子最大最小值价差大于 0.05 时,在主图绘制信号
19+
high = klines.iloc[-1].high
20+
low = klines.iloc[-1].low
21+
if high - low > 0.05:
22+
# 绘制直线, 每一个 id 对应同一条直线
23+
api.draw_line(klines, -1, high, -1, low, id="box%.0f" % (klines.iloc[-1].id), color=0xaa662244, width=4)
24+
# 绘制字符串
25+
api.draw_text(klines, "信号1", x=-1, y=low, id="text%.0f" % (klines.iloc[-1].id), color=0xFFFF3333)
26+

tqsdk/demo/tutorial/t94.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,21 @@
66

77
'''
88
画图示例: 在附图中画K线
9-
注意:1 画图功能仅在天勤终端/天勤Vscode插件中生效,请在这两个平台中运行画图相关的代码
10-
2 画图示例中用到的数据不含有实际意义,请根据自己的实际策略情况进行修改
9+
注意: 画图示例中用到的数据不含有实际意义,请根据自己的实际策略情况进行修改
1110
'''
1211

1312
api = TqApi(web_gui=True)
1413

1514
klines = api.get_kline_serial("SHFE.cu1910", 86400)
1615
klines2 = api.get_kline_serial("SHFE.cu1911", 86400)
1716

17+
# 在附图画出 cu1911 的K线: 需要将open、high、log、close的数据都设置正确
18+
klines["cu1911.open"] = klines2["open"]
19+
klines["cu1911.high"] = klines2["high"]
20+
klines["cu1911.low"] = klines2["low"]
21+
klines["cu1911.close"] = klines2["close"]
22+
klines["cu1911.board"] = "B2"
23+
24+
# 由于需要在浏览器中查看绘图结果,因此程序不能退出
1825
while True:
19-
# 在附图画出 cu1911 的K线: 需要将open、high、log、close的数据都设置正确
20-
klines["cu1911.open"] = klines2["open"]
21-
klines["cu1911.high"] = klines2["high"]
22-
klines["cu1911.low"] = klines2["low"]
23-
klines["cu1911.close"] = klines2["close"]
24-
klines["cu1911.board"] = "B2"
2526
api.wait_update()

0 commit comments

Comments
 (0)