vnpy_scripttrader 1.0.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vnpy_scripttrader-1.0.3/LICENSE +21 -0
- vnpy_scripttrader-1.0.3/PKG-INFO +59 -0
- vnpy_scripttrader-1.0.3/README.md +33 -0
- vnpy_scripttrader-1.0.3/setup.cfg +43 -0
- vnpy_scripttrader-1.0.3/setup.py +4 -0
- vnpy_scripttrader-1.0.3/vnpy_scripttrader/__init__.py +47 -0
- vnpy_scripttrader-1.0.3/vnpy_scripttrader/cli.py +29 -0
- vnpy_scripttrader-1.0.3/vnpy_scripttrader/engine.py +334 -0
- vnpy_scripttrader-1.0.3/vnpy_scripttrader/ui/__init__.py +1 -0
- vnpy_scripttrader-1.0.3/vnpy_scripttrader/ui/script.ico +0 -0
- vnpy_scripttrader-1.0.3/vnpy_scripttrader/ui/widget.py +103 -0
- vnpy_scripttrader-1.0.3/vnpy_scripttrader.egg-info/PKG-INFO +59 -0
- vnpy_scripttrader-1.0.3/vnpy_scripttrader.egg-info/SOURCES.txt +16 -0
- vnpy_scripttrader-1.0.3/vnpy_scripttrader.egg-info/dependency_links.txt +1 -0
- vnpy_scripttrader-1.0.3/vnpy_scripttrader.egg-info/not-zip-safe +1 -0
- vnpy_scripttrader-1.0.3/vnpy_scripttrader.egg-info/requires.txt +2 -0
- vnpy_scripttrader-1.0.3/vnpy_scripttrader.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2015-present, Xiaoyou Chen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vnpy_scripttrader
|
|
3
|
+
Version: 1.0.3
|
|
4
|
+
Summary: Script trading application for VeighNa quant trading framework.
|
|
5
|
+
Home-page: https://www.vnpy.com
|
|
6
|
+
Author: Xiaoyou Chen
|
|
7
|
+
Author-email: xiaoyou.chen@mail.vnpy.com
|
|
8
|
+
License: MIT
|
|
9
|
+
Keywords: quant,quantitative,investment,trading,algotrading
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
18
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
19
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
20
|
+
Classifier: Natural Language :: Chinese (Simplified)
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: importlib_metadata
|
|
24
|
+
Requires-Dist: pandas
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
# VeighNa框架的脚本交易模块
|
|
28
|
+
|
|
29
|
+
<p align="center">
|
|
30
|
+
<img src ="https://vnpy.oss-cn-shanghai.aliyuncs.com/vnpy-logo.png"/>
|
|
31
|
+
</p>
|
|
32
|
+
|
|
33
|
+
<p align="center">
|
|
34
|
+
<img src ="https://img.shields.io/badge/version-1.0.3-blueviolet.svg"/>
|
|
35
|
+
<img src ="https://img.shields.io/badge/platform-windows|linux|macos-yellow.svg"/>
|
|
36
|
+
<img src ="https://img.shields.io/badge/python-3.10|3.11|3.12|3.13-blue.svg" />
|
|
37
|
+
<img src ="https://img.shields.io/github/license/vnpy/vnpy.svg?color=orange"/>
|
|
38
|
+
</p>
|
|
39
|
+
|
|
40
|
+
## 说明
|
|
41
|
+
|
|
42
|
+
ScriptTrader是用于交易脚本执行的功能模块,和其他策略模块最大的区别在于其基于【时间驱动】的【同步逻辑】,也支持在命令行(Jupyter Notebook)中以REPL指令形式的进行交易操作,该模块没有回测功能。
|
|
43
|
+
|
|
44
|
+
## 安装
|
|
45
|
+
|
|
46
|
+
安装环境推荐基于3.0.0版本以上的【[**VeighNa Studio**](https://www.vnpy.com)】。
|
|
47
|
+
|
|
48
|
+
直接使用pip命令:
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
pip install vnpy_scripttrader
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
或者下载源代码后,解压后在cmd中运行:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
pip install .
|
|
59
|
+
```
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# VeighNa框架的脚本交易模块
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src ="https://vnpy.oss-cn-shanghai.aliyuncs.com/vnpy-logo.png"/>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<img src ="https://img.shields.io/badge/version-1.0.3-blueviolet.svg"/>
|
|
9
|
+
<img src ="https://img.shields.io/badge/platform-windows|linux|macos-yellow.svg"/>
|
|
10
|
+
<img src ="https://img.shields.io/badge/python-3.10|3.11|3.12|3.13-blue.svg" />
|
|
11
|
+
<img src ="https://img.shields.io/github/license/vnpy/vnpy.svg?color=orange"/>
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
## 说明
|
|
15
|
+
|
|
16
|
+
ScriptTrader是用于交易脚本执行的功能模块,和其他策略模块最大的区别在于其基于【时间驱动】的【同步逻辑】,也支持在命令行(Jupyter Notebook)中以REPL指令形式的进行交易操作,该模块没有回测功能。
|
|
17
|
+
|
|
18
|
+
## 安装
|
|
19
|
+
|
|
20
|
+
安装环境推荐基于3.0.0版本以上的【[**VeighNa Studio**](https://www.vnpy.com)】。
|
|
21
|
+
|
|
22
|
+
直接使用pip命令:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
pip install vnpy_scripttrader
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
或者下载源代码后,解压后在cmd中运行:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
pip install .
|
|
33
|
+
```
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
[metadata]
|
|
2
|
+
name = vnpy_scripttrader
|
|
3
|
+
version = 1.0.3
|
|
4
|
+
url = https://www.vnpy.com
|
|
5
|
+
license = MIT
|
|
6
|
+
author = Xiaoyou Chen
|
|
7
|
+
author_email = xiaoyou.chen@mail.vnpy.com
|
|
8
|
+
description = Script trading application for VeighNa quant trading framework.
|
|
9
|
+
long_description = file: README.md
|
|
10
|
+
long_description_content_type = text/markdown
|
|
11
|
+
keywords =
|
|
12
|
+
quant
|
|
13
|
+
quantitative
|
|
14
|
+
investment
|
|
15
|
+
trading
|
|
16
|
+
algotrading
|
|
17
|
+
classifiers =
|
|
18
|
+
Development Status :: 5 - Production/Stable
|
|
19
|
+
Operating System :: OS Independent
|
|
20
|
+
Programming Language :: Python :: 3
|
|
21
|
+
Programming Language :: Python :: 3.10
|
|
22
|
+
Programming Language :: Python :: 3.11
|
|
23
|
+
Programming Language :: Python :: 3.12
|
|
24
|
+
Programming Language :: Python :: 3.13
|
|
25
|
+
Topic :: Office/Business :: Financial :: Investment
|
|
26
|
+
Programming Language :: Python :: Implementation :: CPython
|
|
27
|
+
License :: OSI Approved :: MIT License
|
|
28
|
+
Natural Language :: Chinese (Simplified)
|
|
29
|
+
|
|
30
|
+
[options]
|
|
31
|
+
packages = find:
|
|
32
|
+
zip_safe = False
|
|
33
|
+
install_requires =
|
|
34
|
+
importlib_metadata
|
|
35
|
+
pandas
|
|
36
|
+
|
|
37
|
+
[options.package_data]
|
|
38
|
+
* = *.ico
|
|
39
|
+
|
|
40
|
+
[egg_info]
|
|
41
|
+
tag_build =
|
|
42
|
+
tag_date = 0
|
|
43
|
+
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# The MIT License (MIT)
|
|
2
|
+
#
|
|
3
|
+
# Copyright (c) 2015-present, Xiaoyou Chen
|
|
4
|
+
#
|
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
# furnished to do so, subject to the following conditions:
|
|
11
|
+
#
|
|
12
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
# copies or substantial portions of the Software.
|
|
14
|
+
#
|
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
# SOFTWARE.
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
|
|
26
|
+
import importlib_metadata
|
|
27
|
+
from vnpy.trader.app import BaseApp
|
|
28
|
+
|
|
29
|
+
from .engine import ScriptEngine, APP_NAME
|
|
30
|
+
from .cli import init_cli_trading
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
__version__ = importlib_metadata.version("vnpy_scripttrader")
|
|
34
|
+
except importlib_metadata.PackageNotFoundError:
|
|
35
|
+
__version__ = "dev"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ScriptTraderApp(BaseApp):
|
|
39
|
+
""""""
|
|
40
|
+
|
|
41
|
+
app_name: str = APP_NAME
|
|
42
|
+
app_module: str = __module__
|
|
43
|
+
app_path: Path = Path(__file__).parent
|
|
44
|
+
display_name: str = "脚本策略"
|
|
45
|
+
engine_class: ScriptEngine = ScriptEngine
|
|
46
|
+
widget_name: str = "ScriptManager"
|
|
47
|
+
icon_name: str = str(app_path.joinpath("ui", "script.ico"))
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from typing import Sequence, Type
|
|
2
|
+
|
|
3
|
+
from vnpy.event import EventEngine, Event
|
|
4
|
+
from vnpy.trader.engine import MainEngine
|
|
5
|
+
from vnpy.trader.gateway import BaseGateway
|
|
6
|
+
from vnpy.trader.event import EVENT_LOG
|
|
7
|
+
from vnpy.trader.object import LogData
|
|
8
|
+
|
|
9
|
+
from .engine import ScriptEngine, BaseEngine
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def process_log_event(event: Event) -> None:
|
|
13
|
+
""""""
|
|
14
|
+
log: LogData = event.data
|
|
15
|
+
print(f"{log.time}\t{log.msg}")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def init_cli_trading(gateways: Sequence[Type[BaseGateway]]) -> BaseEngine:
|
|
19
|
+
""""""
|
|
20
|
+
event_engine: EventEngine = EventEngine()
|
|
21
|
+
event_engine.register(EVENT_LOG, process_log_event)
|
|
22
|
+
|
|
23
|
+
main_engine: MainEngine = MainEngine(event_engine)
|
|
24
|
+
for gateway in gateways:
|
|
25
|
+
main_engine.add_gateway(gateway)
|
|
26
|
+
|
|
27
|
+
script_engine: ScriptEngine = main_engine.add_engine(ScriptEngine)
|
|
28
|
+
|
|
29
|
+
return script_engine
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import importlib
|
|
3
|
+
import traceback
|
|
4
|
+
from types import ModuleType
|
|
5
|
+
from typing import Optional, Sequence, Any, List
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from threading import Thread
|
|
9
|
+
|
|
10
|
+
from pandas import DataFrame
|
|
11
|
+
|
|
12
|
+
from vnpy.event import Event, EventEngine
|
|
13
|
+
from vnpy.trader.engine import BaseEngine, MainEngine
|
|
14
|
+
from vnpy.trader.constant import Direction, Offset, OrderType, Interval
|
|
15
|
+
from vnpy.trader.object import (
|
|
16
|
+
BaseData,
|
|
17
|
+
OrderRequest,
|
|
18
|
+
HistoryRequest,
|
|
19
|
+
SubscribeRequest,
|
|
20
|
+
TickData,
|
|
21
|
+
OrderData,
|
|
22
|
+
TradeData,
|
|
23
|
+
PositionData,
|
|
24
|
+
AccountData,
|
|
25
|
+
ContractData,
|
|
26
|
+
LogData,
|
|
27
|
+
BarData,
|
|
28
|
+
CancelRequest
|
|
29
|
+
)
|
|
30
|
+
from vnpy.trader.datafeed import BaseDatafeed, get_datafeed
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
APP_NAME = "ScriptTrader"
|
|
34
|
+
|
|
35
|
+
EVENT_SCRIPT_LOG = "eScriptLog"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ScriptEngine(BaseEngine):
|
|
39
|
+
""""""
|
|
40
|
+
setting_filename: str = "script_trader_setting.json"
|
|
41
|
+
|
|
42
|
+
def __init__(self, main_engine: MainEngine, event_engine: EventEngine) -> None:
|
|
43
|
+
""""""
|
|
44
|
+
super().__init__(main_engine, event_engine, APP_NAME)
|
|
45
|
+
|
|
46
|
+
self.strategy_active: bool = False
|
|
47
|
+
self.strategy_thread: Thread = None
|
|
48
|
+
|
|
49
|
+
self.datafeed: BaseDatafeed = get_datafeed()
|
|
50
|
+
|
|
51
|
+
def init(self) -> None:
|
|
52
|
+
"""启动策略引擎"""
|
|
53
|
+
result: bool = self.datafeed.init()
|
|
54
|
+
if result:
|
|
55
|
+
self.write_log("数据服务初始化成功")
|
|
56
|
+
|
|
57
|
+
def start_strategy(self, script_path: str) -> None:
|
|
58
|
+
"""运行策略线程中的策略方法"""
|
|
59
|
+
if self.strategy_active:
|
|
60
|
+
return
|
|
61
|
+
self.strategy_active: bool = True
|
|
62
|
+
|
|
63
|
+
self.strategy_thread: Thread = Thread(
|
|
64
|
+
target=self.run_strategy, args=(script_path,))
|
|
65
|
+
self.strategy_thread.start()
|
|
66
|
+
|
|
67
|
+
self.write_log("策略交易脚本启动")
|
|
68
|
+
|
|
69
|
+
def run_strategy(self, script_path: str) -> None:
|
|
70
|
+
"""加载策略脚本并调用run函数"""
|
|
71
|
+
path: Path = Path(script_path)
|
|
72
|
+
sys.path.append(str(path.parent))
|
|
73
|
+
|
|
74
|
+
script_name: str = path.parts[-1]
|
|
75
|
+
module_name: str = script_name.replace(".py", "")
|
|
76
|
+
|
|
77
|
+
try:
|
|
78
|
+
module: ModuleType = importlib.import_module(module_name)
|
|
79
|
+
importlib.reload(module)
|
|
80
|
+
module.run(self)
|
|
81
|
+
except Exception:
|
|
82
|
+
msg: str = f"触发异常已停止\n{traceback.format_exc()}"
|
|
83
|
+
self.write_log(msg)
|
|
84
|
+
|
|
85
|
+
def stop_strategy(self) -> None:
|
|
86
|
+
"""停止运行中的策略"""
|
|
87
|
+
if not self.strategy_active:
|
|
88
|
+
return
|
|
89
|
+
self.strategy_active: bool = False
|
|
90
|
+
|
|
91
|
+
if self.strategy_thread:
|
|
92
|
+
self.strategy_thread.join()
|
|
93
|
+
self.strategy_thread: Thread = None
|
|
94
|
+
|
|
95
|
+
self.write_log("策略交易脚本停止")
|
|
96
|
+
|
|
97
|
+
def connect_gateway(self, setting: dict, gateway_name: str) -> None:
|
|
98
|
+
""""""
|
|
99
|
+
self.main_engine.connect(setting, gateway_name)
|
|
100
|
+
|
|
101
|
+
def send_order(
|
|
102
|
+
self,
|
|
103
|
+
vt_symbol: str,
|
|
104
|
+
price: float,
|
|
105
|
+
volume: float,
|
|
106
|
+
direction: Direction,
|
|
107
|
+
offset: Offset,
|
|
108
|
+
order_type: OrderType
|
|
109
|
+
) -> str:
|
|
110
|
+
""""""
|
|
111
|
+
contract: Optional[ContractData] = self.get_contract(vt_symbol)
|
|
112
|
+
if not contract:
|
|
113
|
+
return ""
|
|
114
|
+
|
|
115
|
+
req: OrderRequest = OrderRequest(
|
|
116
|
+
symbol=contract.symbol,
|
|
117
|
+
exchange=contract.exchange,
|
|
118
|
+
direction=direction,
|
|
119
|
+
type=order_type,
|
|
120
|
+
volume=volume,
|
|
121
|
+
price=price,
|
|
122
|
+
offset=offset,
|
|
123
|
+
reference=APP_NAME
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
vt_orderid: str = self.main_engine.send_order(req, contract.gateway_name)
|
|
127
|
+
return vt_orderid
|
|
128
|
+
|
|
129
|
+
def subscribe(self, vt_symbols) -> None:
|
|
130
|
+
""""""
|
|
131
|
+
for vt_symbol in vt_symbols:
|
|
132
|
+
contract: Optional[ContractData] = self.main_engine.get_contract(vt_symbol)
|
|
133
|
+
if contract:
|
|
134
|
+
req: SubscribeRequest = SubscribeRequest(
|
|
135
|
+
symbol=contract.symbol,
|
|
136
|
+
exchange=contract.exchange
|
|
137
|
+
)
|
|
138
|
+
self.main_engine.subscribe(req, contract.gateway_name)
|
|
139
|
+
|
|
140
|
+
def buy(
|
|
141
|
+
self,
|
|
142
|
+
vt_symbol: str,
|
|
143
|
+
price: float,
|
|
144
|
+
volume: float,
|
|
145
|
+
order_type: OrderType = OrderType.LIMIT
|
|
146
|
+
) -> str:
|
|
147
|
+
""""""
|
|
148
|
+
return self.send_order(vt_symbol, price, volume, Direction.LONG, Offset.OPEN, order_type)
|
|
149
|
+
|
|
150
|
+
def sell(
|
|
151
|
+
self,
|
|
152
|
+
vt_symbol: str,
|
|
153
|
+
price: float,
|
|
154
|
+
volume: float,
|
|
155
|
+
order_type: OrderType = OrderType.LIMIT
|
|
156
|
+
) -> str:
|
|
157
|
+
""""""
|
|
158
|
+
return self.send_order(vt_symbol, price, volume, Direction.SHORT, Offset.CLOSE, order_type)
|
|
159
|
+
|
|
160
|
+
def short(
|
|
161
|
+
self,
|
|
162
|
+
vt_symbol: str,
|
|
163
|
+
price: float,
|
|
164
|
+
volume: float,
|
|
165
|
+
order_type: OrderType = OrderType.LIMIT
|
|
166
|
+
) -> str:
|
|
167
|
+
""""""
|
|
168
|
+
return self.send_order(vt_symbol, price, volume, Direction.SHORT, Offset.OPEN, order_type)
|
|
169
|
+
|
|
170
|
+
def cover(
|
|
171
|
+
self,
|
|
172
|
+
vt_symbol: str,
|
|
173
|
+
price: float,
|
|
174
|
+
volume: float,
|
|
175
|
+
order_type: OrderType = OrderType.LIMIT
|
|
176
|
+
) -> str:
|
|
177
|
+
""""""
|
|
178
|
+
return self.send_order(vt_symbol, price, volume, Direction.LONG, Offset.CLOSE, order_type)
|
|
179
|
+
|
|
180
|
+
def cancel_order(self, vt_orderid: str) -> None:
|
|
181
|
+
""""""
|
|
182
|
+
order: Optional[OrderData] = self.get_order(vt_orderid)
|
|
183
|
+
if not order:
|
|
184
|
+
return
|
|
185
|
+
|
|
186
|
+
req: CancelRequest = order.create_cancel_request()
|
|
187
|
+
self.main_engine.cancel_order(req, order.gateway_name)
|
|
188
|
+
|
|
189
|
+
def get_tick(self, vt_symbol: str, use_df: bool = False) -> Optional[TickData]:
|
|
190
|
+
""""""
|
|
191
|
+
return get_data(self.main_engine.get_tick, arg=vt_symbol, use_df=use_df)
|
|
192
|
+
|
|
193
|
+
def get_ticks(self, vt_symbols: Sequence[str], use_df: bool = False) -> Sequence[TickData]:
|
|
194
|
+
""""""
|
|
195
|
+
ticks: list = []
|
|
196
|
+
for vt_symbol in vt_symbols:
|
|
197
|
+
tick: Optional[TickData] = self.main_engine.get_tick(vt_symbol)
|
|
198
|
+
ticks.append(tick)
|
|
199
|
+
|
|
200
|
+
if not use_df:
|
|
201
|
+
return ticks
|
|
202
|
+
else:
|
|
203
|
+
return to_df(ticks)
|
|
204
|
+
|
|
205
|
+
def get_order(self, vt_orderid: str, use_df: bool = False) -> Optional[OrderData]:
|
|
206
|
+
""""""
|
|
207
|
+
return get_data(self.main_engine.get_order, arg=vt_orderid, use_df=use_df)
|
|
208
|
+
|
|
209
|
+
def get_orders(self, vt_orderids: Sequence[str], use_df: bool = False) -> Sequence[OrderData]:
|
|
210
|
+
""""""
|
|
211
|
+
orders: list = []
|
|
212
|
+
for vt_orderid in vt_orderids:
|
|
213
|
+
order: Optional[OrderData] = self.main_engine.get_order(vt_orderid)
|
|
214
|
+
orders.append(order)
|
|
215
|
+
|
|
216
|
+
if not use_df:
|
|
217
|
+
return orders
|
|
218
|
+
else:
|
|
219
|
+
return to_df(orders)
|
|
220
|
+
|
|
221
|
+
def get_trades(self, vt_orderid: str, use_df: bool = False) -> Sequence[TradeData]:
|
|
222
|
+
""""""
|
|
223
|
+
trades: list = []
|
|
224
|
+
all_trades: List[TradeData] = self.main_engine.get_all_trades()
|
|
225
|
+
|
|
226
|
+
for trade in all_trades:
|
|
227
|
+
if trade.vt_orderid == vt_orderid:
|
|
228
|
+
trades.append(trade)
|
|
229
|
+
|
|
230
|
+
if not use_df:
|
|
231
|
+
return trades
|
|
232
|
+
else:
|
|
233
|
+
return to_df(trades)
|
|
234
|
+
|
|
235
|
+
def get_all_active_orders(self, use_df: bool = False) -> Sequence[OrderData]:
|
|
236
|
+
""""""
|
|
237
|
+
return get_data(self.main_engine.get_all_active_orders, use_df=use_df)
|
|
238
|
+
|
|
239
|
+
def get_contract(self, vt_symbol, use_df: bool = False) -> Optional[ContractData]:
|
|
240
|
+
""""""
|
|
241
|
+
return get_data(self.main_engine.get_contract, arg=vt_symbol, use_df=use_df)
|
|
242
|
+
|
|
243
|
+
def get_all_contracts(self, use_df: bool = False) -> Sequence[ContractData]:
|
|
244
|
+
""""""
|
|
245
|
+
return get_data(self.main_engine.get_all_contracts, use_df=use_df)
|
|
246
|
+
|
|
247
|
+
def get_account(self, vt_accountid: str, use_df: bool = False) -> Optional[AccountData]:
|
|
248
|
+
""""""
|
|
249
|
+
return get_data(self.main_engine.get_account, arg=vt_accountid, use_df=use_df)
|
|
250
|
+
|
|
251
|
+
def get_all_accounts(self, use_df: bool = False) -> Sequence[AccountData]:
|
|
252
|
+
""""""
|
|
253
|
+
return get_data(self.main_engine.get_all_accounts, use_df=use_df)
|
|
254
|
+
|
|
255
|
+
def get_position(self, vt_positionid: str, use_df: bool = False) -> Optional[PositionData]:
|
|
256
|
+
""""""
|
|
257
|
+
return get_data(self.main_engine.get_position, arg=vt_positionid, use_df=use_df)
|
|
258
|
+
|
|
259
|
+
def get_position_(self, vt_symbol: str, direction: Direction, use_df: bool = False) -> PositionData:
|
|
260
|
+
""""""
|
|
261
|
+
contract: ContractData = self.main_engine.get_contract(vt_symbol)
|
|
262
|
+
if not contract:
|
|
263
|
+
return None
|
|
264
|
+
|
|
265
|
+
vt_positionid: str = f"{contract.gateway_name}.{contract.vt_symbol}.{direction.value}"
|
|
266
|
+
return get_data(self.main_engine.get_position, arg=vt_positionid, use_df=use_df)
|
|
267
|
+
|
|
268
|
+
def get_all_positions(self, use_df: bool = False) -> Sequence[PositionData]:
|
|
269
|
+
""""""
|
|
270
|
+
return get_data(self.main_engine.get_all_positions, use_df=use_df)
|
|
271
|
+
|
|
272
|
+
def get_bars(
|
|
273
|
+
self,
|
|
274
|
+
vt_symbol: str,
|
|
275
|
+
start_date: str,
|
|
276
|
+
interval: Interval,
|
|
277
|
+
use_df: bool = False
|
|
278
|
+
) -> Sequence[BarData]:
|
|
279
|
+
""""""
|
|
280
|
+
contract: Optional[ContractData] = self.main_engine.get_contract(vt_symbol)
|
|
281
|
+
if not contract:
|
|
282
|
+
return []
|
|
283
|
+
|
|
284
|
+
start: datetime = datetime.strptime(start_date, "%Y%m%d")
|
|
285
|
+
end: datetime = datetime.now()
|
|
286
|
+
|
|
287
|
+
req: HistoryRequest = HistoryRequest(
|
|
288
|
+
symbol=contract.symbol,
|
|
289
|
+
exchange=contract.exchange,
|
|
290
|
+
start=start,
|
|
291
|
+
end=end,
|
|
292
|
+
interval=interval
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
return get_data(self.datafeed.query_bar_history, arg=req, use_df=use_df)
|
|
296
|
+
|
|
297
|
+
def write_log(self, msg: str) -> None:
|
|
298
|
+
""""""
|
|
299
|
+
log: LogData = LogData(msg=msg, gateway_name=APP_NAME)
|
|
300
|
+
print(f"{log.time}\t{log.msg}")
|
|
301
|
+
|
|
302
|
+
event: Event = Event(EVENT_SCRIPT_LOG, log)
|
|
303
|
+
self.event_engine.put(event)
|
|
304
|
+
|
|
305
|
+
def send_email(self, msg: str) -> None:
|
|
306
|
+
""""""
|
|
307
|
+
subject: str = "脚本策略引擎通知"
|
|
308
|
+
self.main_engine.send_email(subject, msg)
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def to_df(data_list: Sequence) -> Optional[DataFrame]:
|
|
312
|
+
""""""
|
|
313
|
+
if not data_list:
|
|
314
|
+
return None
|
|
315
|
+
|
|
316
|
+
dict_list: list = [data.__dict__ for data in data_list if data]
|
|
317
|
+
return DataFrame(dict_list)
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def get_data(func: callable, arg: Any = None, use_df: bool = False) -> Optional[BaseData]:
|
|
321
|
+
""""""
|
|
322
|
+
if not arg:
|
|
323
|
+
data = func()
|
|
324
|
+
else:
|
|
325
|
+
data = func(arg)
|
|
326
|
+
|
|
327
|
+
if not use_df:
|
|
328
|
+
return data
|
|
329
|
+
elif data is None:
|
|
330
|
+
return data
|
|
331
|
+
else:
|
|
332
|
+
if not isinstance(data, list):
|
|
333
|
+
data: list = [data]
|
|
334
|
+
return to_df(data)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .widget import ScriptManager
|
|
Binary file
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from vnpy.event import EventEngine, Event
|
|
4
|
+
from vnpy.trader.engine import MainEngine
|
|
5
|
+
from vnpy.trader.ui import QtWidgets, QtCore
|
|
6
|
+
from vnpy.trader.object import LogData
|
|
7
|
+
from ..engine import APP_NAME, EVENT_SCRIPT_LOG, BaseEngine
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ScriptManager(QtWidgets.QWidget):
|
|
11
|
+
""""""
|
|
12
|
+
signal_log: QtCore.Signal = QtCore.Signal(Event)
|
|
13
|
+
|
|
14
|
+
def __init__(self, main_engine: MainEngine, event_engine: EventEngine) -> None:
|
|
15
|
+
""""""
|
|
16
|
+
super().__init__()
|
|
17
|
+
|
|
18
|
+
self.main_engine: MainEngine = main_engine
|
|
19
|
+
self.event_engine: EventEngine = event_engine
|
|
20
|
+
|
|
21
|
+
self.script_engine: BaseEngine = main_engine.get_engine(APP_NAME)
|
|
22
|
+
|
|
23
|
+
self.script_path: str = ""
|
|
24
|
+
|
|
25
|
+
self.init_ui()
|
|
26
|
+
self.register_event()
|
|
27
|
+
|
|
28
|
+
self.script_engine.init()
|
|
29
|
+
|
|
30
|
+
def init_ui(self) -> None:
|
|
31
|
+
""""""
|
|
32
|
+
self.setWindowTitle("脚本策略")
|
|
33
|
+
|
|
34
|
+
start_button: QtWidgets.QPushButton = QtWidgets.QPushButton("启动")
|
|
35
|
+
start_button.clicked.connect(self.start_script)
|
|
36
|
+
|
|
37
|
+
stop_button: QtWidgets.QPushButton = QtWidgets.QPushButton("停止")
|
|
38
|
+
stop_button.clicked.connect(self.stop_script)
|
|
39
|
+
|
|
40
|
+
select_button: QtWidgets.QPushButton = QtWidgets.QPushButton("打开")
|
|
41
|
+
select_button.clicked.connect(self.select_script)
|
|
42
|
+
|
|
43
|
+
self.strategy_line: QtWidgets.QLineEdit = QtWidgets.QLineEdit()
|
|
44
|
+
|
|
45
|
+
self.log_monitor: QtWidgets.QTextEdit = QtWidgets.QTextEdit()
|
|
46
|
+
self.log_monitor.setReadOnly(True)
|
|
47
|
+
|
|
48
|
+
clear_button: QtWidgets.QPushButton = QtWidgets.QPushButton("清空")
|
|
49
|
+
clear_button.clicked.connect(self.log_monitor.clear)
|
|
50
|
+
|
|
51
|
+
hbox: QtWidgets.QHBoxLayout = QtWidgets.QHBoxLayout()
|
|
52
|
+
hbox.addWidget(self.strategy_line)
|
|
53
|
+
hbox.addWidget(select_button)
|
|
54
|
+
hbox.addWidget(start_button)
|
|
55
|
+
hbox.addWidget(stop_button)
|
|
56
|
+
hbox.addStretch()
|
|
57
|
+
hbox.addWidget(clear_button)
|
|
58
|
+
|
|
59
|
+
vbox: QtWidgets.QVBoxLayout = QtWidgets.QVBoxLayout()
|
|
60
|
+
vbox.addLayout(hbox)
|
|
61
|
+
vbox.addWidget(self.log_monitor)
|
|
62
|
+
|
|
63
|
+
self.setLayout(vbox)
|
|
64
|
+
|
|
65
|
+
def register_event(self) -> None:
|
|
66
|
+
""""""
|
|
67
|
+
self.signal_log.connect(self.process_log_event)
|
|
68
|
+
|
|
69
|
+
self.event_engine.register(EVENT_SCRIPT_LOG, self.signal_log.emit)
|
|
70
|
+
|
|
71
|
+
def show(self) -> None:
|
|
72
|
+
""""""
|
|
73
|
+
self.showMaximized()
|
|
74
|
+
|
|
75
|
+
def process_log_event(self, event: Event) -> None:
|
|
76
|
+
""""""
|
|
77
|
+
log: LogData = event.data
|
|
78
|
+
msg: str = f"{log.time}\t{log.msg}"
|
|
79
|
+
self.log_monitor.append(msg)
|
|
80
|
+
|
|
81
|
+
def start_script(self) -> None:
|
|
82
|
+
""""""
|
|
83
|
+
if self.script_path:
|
|
84
|
+
self.script_engine.start_strategy(self.script_path)
|
|
85
|
+
|
|
86
|
+
def stop_script(self) -> None:
|
|
87
|
+
""""""
|
|
88
|
+
self.script_engine.stop_strategy()
|
|
89
|
+
|
|
90
|
+
def select_script(self) -> None:
|
|
91
|
+
""""""
|
|
92
|
+
cwd: str = str(Path.cwd())
|
|
93
|
+
|
|
94
|
+
path, type_ = QtWidgets.QFileDialog.getOpenFileName(
|
|
95
|
+
self,
|
|
96
|
+
u"载入策略脚本",
|
|
97
|
+
cwd,
|
|
98
|
+
"Python File(*.py)"
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
if path:
|
|
102
|
+
self.script_path = path
|
|
103
|
+
self.strategy_line.setText(path)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vnpy_scripttrader
|
|
3
|
+
Version: 1.0.3
|
|
4
|
+
Summary: Script trading application for VeighNa quant trading framework.
|
|
5
|
+
Home-page: https://www.vnpy.com
|
|
6
|
+
Author: Xiaoyou Chen
|
|
7
|
+
Author-email: xiaoyou.chen@mail.vnpy.com
|
|
8
|
+
License: MIT
|
|
9
|
+
Keywords: quant,quantitative,investment,trading,algotrading
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
18
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
19
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
20
|
+
Classifier: Natural Language :: Chinese (Simplified)
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: importlib_metadata
|
|
24
|
+
Requires-Dist: pandas
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
# VeighNa框架的脚本交易模块
|
|
28
|
+
|
|
29
|
+
<p align="center">
|
|
30
|
+
<img src ="https://vnpy.oss-cn-shanghai.aliyuncs.com/vnpy-logo.png"/>
|
|
31
|
+
</p>
|
|
32
|
+
|
|
33
|
+
<p align="center">
|
|
34
|
+
<img src ="https://img.shields.io/badge/version-1.0.3-blueviolet.svg"/>
|
|
35
|
+
<img src ="https://img.shields.io/badge/platform-windows|linux|macos-yellow.svg"/>
|
|
36
|
+
<img src ="https://img.shields.io/badge/python-3.10|3.11|3.12|3.13-blue.svg" />
|
|
37
|
+
<img src ="https://img.shields.io/github/license/vnpy/vnpy.svg?color=orange"/>
|
|
38
|
+
</p>
|
|
39
|
+
|
|
40
|
+
## 说明
|
|
41
|
+
|
|
42
|
+
ScriptTrader是用于交易脚本执行的功能模块,和其他策略模块最大的区别在于其基于【时间驱动】的【同步逻辑】,也支持在命令行(Jupyter Notebook)中以REPL指令形式的进行交易操作,该模块没有回测功能。
|
|
43
|
+
|
|
44
|
+
## 安装
|
|
45
|
+
|
|
46
|
+
安装环境推荐基于3.0.0版本以上的【[**VeighNa Studio**](https://www.vnpy.com)】。
|
|
47
|
+
|
|
48
|
+
直接使用pip命令:
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
pip install vnpy_scripttrader
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
或者下载源代码后,解压后在cmd中运行:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
pip install .
|
|
59
|
+
```
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
setup.cfg
|
|
4
|
+
setup.py
|
|
5
|
+
vnpy_scripttrader/__init__.py
|
|
6
|
+
vnpy_scripttrader/cli.py
|
|
7
|
+
vnpy_scripttrader/engine.py
|
|
8
|
+
vnpy_scripttrader.egg-info/PKG-INFO
|
|
9
|
+
vnpy_scripttrader.egg-info/SOURCES.txt
|
|
10
|
+
vnpy_scripttrader.egg-info/dependency_links.txt
|
|
11
|
+
vnpy_scripttrader.egg-info/not-zip-safe
|
|
12
|
+
vnpy_scripttrader.egg-info/requires.txt
|
|
13
|
+
vnpy_scripttrader.egg-info/top_level.txt
|
|
14
|
+
vnpy_scripttrader/ui/__init__.py
|
|
15
|
+
vnpy_scripttrader/ui/script.ico
|
|
16
|
+
vnpy_scripttrader/ui/widget.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
vnpy_scripttrader
|