Skip to content

Commit 7fbbb29

Browse files
authored
Merge pull request vnpy#1 from youyuanrsq/main
剥离excel_rtd模块
2 parents 4fc8921 + 479e473 commit 7fbbb29

File tree

10 files changed

+364
-0
lines changed

10 files changed

+364
-0
lines changed

.flake8

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[flake8]
2+
exclude = build,__pycache__,__init__.py
3+
ignore =
4+
E501 line too long, fixed by black
5+
W503 line break before binary operator

README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,33 @@
11
# vnpy_excelrtd
22
vn.py框架的Excel RTD应用模块
3+
4+
<p align="center">
5+
<img src ="https://vnpy.oss-cn-shanghai.aliyuncs.com/vnpy-logo.png"/>
6+
</p>
7+
8+
<p align="center">
9+
<img src ="https://img.shields.io/badge/version-1.0.0-blueviolet.svg"/>
10+
<img src ="https://img.shields.io/badge/platform-windows|linux|macos-yellow.svg"/>
11+
<img src ="https://img.shields.io/badge/python-3.7-blue.svg" />
12+
<img src ="https://img.shields.io/github/license/vnpy/vnpy.svg?color=orange"/>
13+
</p>
14+
15+
## 说明
16+
17+
RTD全称是RealTimeData,是微软提供的主要面向金融行业中实时数据需求设计的Excel数据对接方案,而ExcelRtd模块则是vn.py官方提供的用于实现在Excel中访问vn.py程序内任意数据信息的功能模块。
18+
19+
## 安装
20+
21+
安装需要基于2.7.0版本以上的[VN Studio](https://www.vnpy.com)
22+
23+
直接使用pip命令:
24+
25+
```
26+
pip install vnpy_excelrtd
27+
```
28+
29+
下载解压后在cmd中运行
30+
31+
```
32+
python setup.py install
33+
```

setup.cfg

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
[metadata]
2+
name = vnpy_excelrtd
3+
version = 1.0.0
4+
url = https://www.vnpy.com
5+
license = MIT
6+
author = Xiaoyou Chen
7+
author_email = [email protected]
8+
description = excel RTD application for vn.py 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.7
22+
Programming Language :: Python :: 3.8
23+
Programming Language :: Python :: 3.9
24+
Programming Language :: Python :: 3.10
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+
36+
[options.package_data]
37+
* = *.ico

setup.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from setuptools import setup
2+
3+
setup()

vnpy_excelrtd/__init__.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from pathlib import Path
2+
from vnpy.trader.app import BaseApp
3+
from .engine import RtdEngine, APP_NAME
4+
5+
6+
class ExcelRtdApp(BaseApp):
7+
""""""
8+
app_name = APP_NAME
9+
app_module = __module__
10+
app_path = Path(__file__).parent
11+
display_name = "Excel RTD"
12+
engine_class = RtdEngine
13+
widget_name = "RtdManager"
14+
icon_name = "rtd.ico"

vnpy_excelrtd/engine.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
""""""
2+
3+
from typing import Set
4+
5+
from vnpy.event import Event, EventEngine
6+
from vnpy.rpc import RpcServer
7+
from vnpy.trader.engine import BaseEngine, MainEngine
8+
from vnpy.trader.object import TickData, LogData, SubscribeRequest
9+
from vnpy.trader.event import EVENT_TICK
10+
11+
12+
APP_NAME = "ExcelRtd"
13+
14+
EVENT_RTD_LOG = "eRtdLog"
15+
16+
REP_ADDRESS = "tcp://*:9001"
17+
PUB_ADDRESS = "tcp://*:9002"
18+
19+
20+
class RtdEngine(BaseEngine):
21+
"""
22+
The engine for managing RTD objects and data update.
23+
"""
24+
25+
def __init__(self, main_engine: MainEngine, event_engine: EventEngine):
26+
""""""
27+
super().__init__(main_engine, event_engine, APP_NAME)
28+
29+
self.server: RpcServer = RpcServer()
30+
self.server.register(self.subscribe)
31+
self.server.register(self.write_log)
32+
self.server.start(REP_ADDRESS, PUB_ADDRESS)
33+
34+
self.subscribed: Set[str] = set()
35+
36+
self.register_event()
37+
38+
def register_event(self) -> None:
39+
"""
40+
Register event handler.
41+
"""
42+
self.event_engine.register(EVENT_TICK, self.process_tick_event)
43+
44+
def process_tick_event(self, event: Event) -> None:
45+
"""
46+
Process tick event and update related RTD value.
47+
"""
48+
tick: TickData = event.data
49+
self.server.publish("tick", tick)
50+
51+
def write_log(self, msg: str) -> None:
52+
"""
53+
Output RTD related log message.
54+
"""
55+
log = LogData(msg=msg, gateway_name=APP_NAME)
56+
event = Event(EVENT_RTD_LOG, log)
57+
self.event_engine.put(event)
58+
59+
def subscribe(self, vt_symbol: str) -> None:
60+
"""
61+
Subscribe tick data update.
62+
"""
63+
contract = self.main_engine.get_contract(vt_symbol)
64+
if not contract:
65+
return
66+
67+
if vt_symbol in self.subscribed:
68+
return
69+
self.subscribed.add(vt_symbol)
70+
71+
req = SubscribeRequest(
72+
contract.symbol,
73+
contract.exchange
74+
)
75+
self.main_engine.subscribe(req, contract.gateway_name)
76+
77+
def close(self):
78+
""""""
79+
self.server.stop()
80+
self.server.join()

vnpy_excelrtd/ui/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .widget import RtdManager

vnpy_excelrtd/ui/rtd.ico

66.1 KB
Binary file not shown.

vnpy_excelrtd/ui/widget.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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_RTD_LOG
8+
9+
10+
class RtdManager(QtWidgets.QWidget):
11+
""""""
12+
signal_log = QtCore.pyqtSignal(Event)
13+
14+
def __init__(self, main_engine: MainEngine, event_engine: EventEngine):
15+
""""""
16+
super().__init__()
17+
18+
self.main_engine = main_engine
19+
self.event_engine = event_engine
20+
self.rm_engine = main_engine.get_engine(APP_NAME)
21+
22+
self.init_ui()
23+
self.register_event()
24+
25+
def init_ui(self) -> None:
26+
"""
27+
Init widget ui components.
28+
"""
29+
self.setWindowTitle("Excel RTD")
30+
self.resize(600, 600)
31+
32+
module_path = Path(__file__).parent.parent
33+
client_path = module_path.joinpath("vnpy_rtd.py")
34+
self.client_line = QtWidgets.QLineEdit(str(client_path))
35+
self.client_line.setReadOnly(True)
36+
37+
copy_button = QtWidgets.QPushButton("复制")
38+
copy_button.clicked.connect(self.copy_client_path)
39+
40+
self.log_monitor = QtWidgets.QTextEdit()
41+
self.log_monitor.setReadOnly(True)
42+
43+
self.port_label = QtWidgets.QLabel(
44+
"使用Socket端口:请求回应9001、广播推送9002"
45+
)
46+
47+
hbox = QtWidgets.QHBoxLayout()
48+
hbox.addWidget(self.client_line)
49+
hbox.addWidget(copy_button)
50+
51+
vbox = QtWidgets.QVBoxLayout()
52+
vbox.addLayout(hbox)
53+
vbox.addWidget(self.log_monitor)
54+
vbox.addWidget(self.port_label)
55+
self.setLayout(vbox)
56+
57+
def register_event(self) -> None:
58+
"""
59+
Register event handler.
60+
"""
61+
self.signal_log.connect(self.process_log_event)
62+
63+
self.event_engine.register(EVENT_RTD_LOG, self.signal_log.emit)
64+
65+
def process_log_event(self, event: Event) -> None:
66+
"""
67+
Show log message in monitor.
68+
"""
69+
log: LogData = event.data
70+
71+
msg = f"{log.time}: {log.msg}"
72+
self.log_monitor.append(msg)
73+
74+
def copy_client_path(self) -> None:
75+
"""
76+
Copy path of client python file to clipboard.
77+
"""
78+
self.client_line.selectAll()
79+
self.client_line.copy()

vnpy_excelrtd/vnpy_rtd.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
""""""
2+
3+
from typing import Dict, Set, Any
4+
from collections import defaultdict
5+
6+
from pyxll import RTD, xl_func
7+
8+
from vnpy.rpc import RpcClient
9+
from vnpy.trader.object import TickData
10+
11+
12+
REQ_ADDRESS = "tcp://localhost:9001"
13+
SUB_ADDRESS = "tcp://localhost:9002"
14+
15+
16+
rtd_client: "RtdClient" = None
17+
18+
19+
class ObjectRtd(RTD):
20+
"""
21+
RTD proxy for object in Python.
22+
"""
23+
24+
def __init__(self, engine: "RtdClient", name: str, field: str):
25+
"""Constructor"""
26+
super().__init__(value=0)
27+
28+
self.engine = engine
29+
self.name = name
30+
self.field = field
31+
32+
def connect(self) -> None:
33+
"""
34+
Callback when excel cell rtd is connected.
35+
"""
36+
self.engine.add_rtd(self)
37+
38+
def disconnect(self) -> None:
39+
"""
40+
Callback when excel cell rtd is disconncted.
41+
"""
42+
self.engine.remove_rtd(self)
43+
44+
def update(self, data: Any) -> None:
45+
"""
46+
Update value in excel cell.
47+
"""
48+
new_value = getattr(data, self.field, "N/A")
49+
50+
if new_value != self.value:
51+
self.value = new_value
52+
53+
54+
class RtdClient(RpcClient):
55+
"""
56+
The engine for managing RTD objects and data update.
57+
"""
58+
59+
def __init__(self):
60+
""""""
61+
super().__init__()
62+
63+
self.rtds: Dict[str, Set[ObjectRtd]] = defaultdict(set)
64+
65+
global rtd_client
66+
rtd_client = self
67+
68+
def callback(self, topic: str, data: Any) -> None:
69+
""""""
70+
tick: TickData = data
71+
buf = self.rtds[tick.vt_symbol]
72+
73+
for rtd in buf:
74+
rtd.update(tick)
75+
76+
def add_rtd(self, rtd: ObjectRtd) -> None:
77+
"""
78+
Add a new RTD into the engine..
79+
"""
80+
buf = self.rtds[rtd.name]
81+
buf.add(rtd)
82+
self.write_log(f"新增RTD连接:{rtd.name} {rtd.field}")
83+
84+
# Auto subscribe tick data
85+
self.subscribe(rtd.name)
86+
87+
def remove_rtd(self, rtd: ObjectRtd) -> None:
88+
"""
89+
Remove an existing RTD from the engine.
90+
"""
91+
buf = self.rtds[self.name]
92+
if self in buf:
93+
buf.remove(rtd)
94+
self.write_log(f"移除RTD连接:{rtd.name} {rtd.field}")
95+
96+
97+
def init_client() -> None:
98+
"""Initialize vnpy rtd client"""
99+
global rtd_client
100+
rtd_client = RtdClient()
101+
rtd_client.subscribe_topic("")
102+
rtd_client.start(REQ_ADDRESS, SUB_ADDRESS)
103+
104+
105+
@xl_func("string vt_symbol, string field: rtd")
106+
def rtd_tick_data(vt_symbol: str, field: str) -> ObjectRtd:
107+
"""
108+
Return the streaming value of the tick data field.
109+
"""
110+
if not rtd_client:
111+
init_client()
112+
113+
rtd = ObjectRtd(rtd_client, vt_symbol, field)
114+
return rtd

0 commit comments

Comments
 (0)