Skip to content

Commit

Permalink
剥离excel_rtd模块
Browse files Browse the repository at this point in the history
  • Loading branch information
youyuanrsq committed Nov 10, 2021
1 parent 4fc8921 commit 479e473
Show file tree
Hide file tree
Showing 10 changed files with 364 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[flake8]
exclude = build,__pycache__,__init__.py
ignore =
E501 line too long, fixed by black
W503 line break before binary operator
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,33 @@
# vnpy_excelrtd
vn.py框架的Excel RTD应用模块

<p align="center">
<img src ="https://vnpy.oss-cn-shanghai.aliyuncs.com/vnpy-logo.png"/>
</p>

<p align="center">
<img src ="https://img.shields.io/badge/version-1.0.0-blueviolet.svg"/>
<img src ="https://img.shields.io/badge/platform-windows|linux|macos-yellow.svg"/>
<img src ="https://img.shields.io/badge/python-3.7-blue.svg" />
<img src ="https://img.shields.io/github/license/vnpy/vnpy.svg?color=orange"/>
</p>

## 说明

RTD全称是RealTimeData,是微软提供的主要面向金融行业中实时数据需求设计的Excel数据对接方案,而ExcelRtd模块则是vn.py官方提供的用于实现在Excel中访问vn.py程序内任意数据信息的功能模块。

## 安装

安装需要基于2.7.0版本以上的[VN Studio](https://www.vnpy.com)

直接使用pip命令:

```
pip install vnpy_excelrtd
```

下载解压后在cmd中运行

```
python setup.py install
```
37 changes: 37 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[metadata]
name = vnpy_excelrtd
version = 1.0.0
url = https://www.vnpy.com
license = MIT
author = Xiaoyou Chen
author_email = [email protected]
description = excel RTD application for vn.py quant trading framework.
long_description = file: README.md
long_description_content_type = text/markdown
keywords =
quant
quantitative
investment
trading
algotrading
classifiers =
Development Status :: 5 - Production/Stable
Operating System :: OS Independent
Programming Language :: Python :: 3
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Topic :: Office/Business :: Financial :: Investment
Programming Language :: Python :: Implementation :: CPython
License :: OSI Approved :: MIT License
Natural Language :: Chinese (Simplified)

[options]
packages = find:
zip_safe = False
install_requires =
importlib_metadata

[options.package_data]
* = *.ico
3 changes: 3 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from setuptools import setup

setup()
14 changes: 14 additions & 0 deletions vnpy_excelrtd/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from pathlib import Path
from vnpy.trader.app import BaseApp
from .engine import RtdEngine, APP_NAME


class ExcelRtdApp(BaseApp):
""""""
app_name = APP_NAME
app_module = __module__
app_path = Path(__file__).parent
display_name = "Excel RTD"
engine_class = RtdEngine
widget_name = "RtdManager"
icon_name = "rtd.ico"
80 changes: 80 additions & 0 deletions vnpy_excelrtd/engine.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
""""""

from typing import Set

from vnpy.event import Event, EventEngine
from vnpy.rpc import RpcServer
from vnpy.trader.engine import BaseEngine, MainEngine
from vnpy.trader.object import TickData, LogData, SubscribeRequest
from vnpy.trader.event import EVENT_TICK


APP_NAME = "ExcelRtd"

EVENT_RTD_LOG = "eRtdLog"

REP_ADDRESS = "tcp://*:9001"
PUB_ADDRESS = "tcp://*:9002"


class RtdEngine(BaseEngine):
"""
The engine for managing RTD objects and data update.
"""

def __init__(self, main_engine: MainEngine, event_engine: EventEngine):
""""""
super().__init__(main_engine, event_engine, APP_NAME)

self.server: RpcServer = RpcServer()
self.server.register(self.subscribe)
self.server.register(self.write_log)
self.server.start(REP_ADDRESS, PUB_ADDRESS)

self.subscribed: Set[str] = set()

self.register_event()

def register_event(self) -> None:
"""
Register event handler.
"""
self.event_engine.register(EVENT_TICK, self.process_tick_event)

def process_tick_event(self, event: Event) -> None:
"""
Process tick event and update related RTD value.
"""
tick: TickData = event.data
self.server.publish("tick", tick)

def write_log(self, msg: str) -> None:
"""
Output RTD related log message.
"""
log = LogData(msg=msg, gateway_name=APP_NAME)
event = Event(EVENT_RTD_LOG, log)
self.event_engine.put(event)

def subscribe(self, vt_symbol: str) -> None:
"""
Subscribe tick data update.
"""
contract = self.main_engine.get_contract(vt_symbol)
if not contract:
return

if vt_symbol in self.subscribed:
return
self.subscribed.add(vt_symbol)

req = SubscribeRequest(
contract.symbol,
contract.exchange
)
self.main_engine.subscribe(req, contract.gateway_name)

def close(self):
""""""
self.server.stop()
self.server.join()
1 change: 1 addition & 0 deletions vnpy_excelrtd/ui/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .widget import RtdManager
Binary file added vnpy_excelrtd/ui/rtd.ico
Binary file not shown.
79 changes: 79 additions & 0 deletions vnpy_excelrtd/ui/widget.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
from pathlib import Path

from vnpy.event import EventEngine, Event
from vnpy.trader.engine import MainEngine
from vnpy.trader.ui import QtWidgets, QtCore
from vnpy.trader.object import LogData
from ..engine import APP_NAME, EVENT_RTD_LOG


class RtdManager(QtWidgets.QWidget):
""""""
signal_log = QtCore.pyqtSignal(Event)

def __init__(self, main_engine: MainEngine, event_engine: EventEngine):
""""""
super().__init__()

self.main_engine = main_engine
self.event_engine = event_engine
self.rm_engine = main_engine.get_engine(APP_NAME)

self.init_ui()
self.register_event()

def init_ui(self) -> None:
"""
Init widget ui components.
"""
self.setWindowTitle("Excel RTD")
self.resize(600, 600)

module_path = Path(__file__).parent.parent
client_path = module_path.joinpath("vnpy_rtd.py")
self.client_line = QtWidgets.QLineEdit(str(client_path))
self.client_line.setReadOnly(True)

copy_button = QtWidgets.QPushButton("复制")
copy_button.clicked.connect(self.copy_client_path)

self.log_monitor = QtWidgets.QTextEdit()
self.log_monitor.setReadOnly(True)

self.port_label = QtWidgets.QLabel(
"使用Socket端口:请求回应9001、广播推送9002"
)

hbox = QtWidgets.QHBoxLayout()
hbox.addWidget(self.client_line)
hbox.addWidget(copy_button)

vbox = QtWidgets.QVBoxLayout()
vbox.addLayout(hbox)
vbox.addWidget(self.log_monitor)
vbox.addWidget(self.port_label)
self.setLayout(vbox)

def register_event(self) -> None:
"""
Register event handler.
"""
self.signal_log.connect(self.process_log_event)

self.event_engine.register(EVENT_RTD_LOG, self.signal_log.emit)

def process_log_event(self, event: Event) -> None:
"""
Show log message in monitor.
"""
log: LogData = event.data

msg = f"{log.time}: {log.msg}"
self.log_monitor.append(msg)

def copy_client_path(self) -> None:
"""
Copy path of client python file to clipboard.
"""
self.client_line.selectAll()
self.client_line.copy()
114 changes: 114 additions & 0 deletions vnpy_excelrtd/vnpy_rtd.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
""""""

from typing import Dict, Set, Any
from collections import defaultdict

from pyxll import RTD, xl_func

from vnpy.rpc import RpcClient
from vnpy.trader.object import TickData


REQ_ADDRESS = "tcp://localhost:9001"
SUB_ADDRESS = "tcp://localhost:9002"


rtd_client: "RtdClient" = None


class ObjectRtd(RTD):
"""
RTD proxy for object in Python.
"""

def __init__(self, engine: "RtdClient", name: str, field: str):
"""Constructor"""
super().__init__(value=0)

self.engine = engine
self.name = name
self.field = field

def connect(self) -> None:
"""
Callback when excel cell rtd is connected.
"""
self.engine.add_rtd(self)

def disconnect(self) -> None:
"""
Callback when excel cell rtd is disconncted.
"""
self.engine.remove_rtd(self)

def update(self, data: Any) -> None:
"""
Update value in excel cell.
"""
new_value = getattr(data, self.field, "N/A")

if new_value != self.value:
self.value = new_value


class RtdClient(RpcClient):
"""
The engine for managing RTD objects and data update.
"""

def __init__(self):
""""""
super().__init__()

self.rtds: Dict[str, Set[ObjectRtd]] = defaultdict(set)

global rtd_client
rtd_client = self

def callback(self, topic: str, data: Any) -> None:
""""""
tick: TickData = data
buf = self.rtds[tick.vt_symbol]

for rtd in buf:
rtd.update(tick)

def add_rtd(self, rtd: ObjectRtd) -> None:
"""
Add a new RTD into the engine..
"""
buf = self.rtds[rtd.name]
buf.add(rtd)
self.write_log(f"新增RTD连接:{rtd.name} {rtd.field}")

# Auto subscribe tick data
self.subscribe(rtd.name)

def remove_rtd(self, rtd: ObjectRtd) -> None:
"""
Remove an existing RTD from the engine.
"""
buf = self.rtds[self.name]
if self in buf:
buf.remove(rtd)
self.write_log(f"移除RTD连接:{rtd.name} {rtd.field}")


def init_client() -> None:
"""Initialize vnpy rtd client"""
global rtd_client
rtd_client = RtdClient()
rtd_client.subscribe_topic("")
rtd_client.start(REQ_ADDRESS, SUB_ADDRESS)


@xl_func("string vt_symbol, string field: rtd")
def rtd_tick_data(vt_symbol: str, field: str) -> ObjectRtd:
"""
Return the streaming value of the tick data field.
"""
if not rtd_client:
init_client()

rtd = ObjectRtd(rtd_client, vt_symbol, field)
return rtd

0 comments on commit 479e473

Please sign in to comment.