[Mod] strategy trading widget
This commit is contained in:
parent
6643d8157e
commit
9ee00b0a58
@ -5,11 +5,11 @@ from copy import copy
|
|||||||
|
|
||||||
from vnpy.event import Event, EventEngine
|
from vnpy.event import Event, EventEngine
|
||||||
from vnpy.trader.engine import BaseEngine, MainEngine
|
from vnpy.trader.engine import BaseEngine, MainEngine
|
||||||
from vnpy.trader.event import EVENT_TRADE, EVENT_ORDER, EVENT_TICK
|
from vnpy.trader.event import EVENT_TRADE, EVENT_ORDER, EVENT_TICK, EVENT_CONTRACT
|
||||||
from vnpy.trader.constant import Direction, Offset, OrderType
|
from vnpy.trader.constant import Direction, Offset, OrderType
|
||||||
from vnpy.trader.object import (
|
from vnpy.trader.object import (
|
||||||
OrderRequest, CancelRequest, SubscribeRequest,
|
OrderRequest, CancelRequest, SubscribeRequest,
|
||||||
OrderData, TradeData, TickData
|
OrderData, TradeData, TickData, ContractData
|
||||||
)
|
)
|
||||||
from vnpy.trader.utility import load_json, save_json
|
from vnpy.trader.utility import load_json, save_json
|
||||||
|
|
||||||
@ -17,8 +17,8 @@ from vnpy.trader.utility import load_json, save_json
|
|||||||
APP_NAME = "PortfolioManager"
|
APP_NAME = "PortfolioManager"
|
||||||
|
|
||||||
EVENT_PORTFOLIO_UPDATE = "ePortfioUpdate"
|
EVENT_PORTFOLIO_UPDATE = "ePortfioUpdate"
|
||||||
EVENT_PORTFOLIO_ORDER = "ePortfioUpdate"
|
EVENT_PORTFOLIO_ORDER = "ePortfioOrder"
|
||||||
EVENT_PORTFOLIO_TRADE = "ePortfioUpdate"
|
EVENT_PORTFOLIO_TRADE = "ePortfioTrade"
|
||||||
|
|
||||||
|
|
||||||
class PortfolioEngine(BaseEngine):
|
class PortfolioEngine(BaseEngine):
|
||||||
@ -35,7 +35,6 @@ class PortfolioEngine(BaseEngine):
|
|||||||
self.active_orders: Set[str] = set()
|
self.active_orders: Set[str] = set()
|
||||||
|
|
||||||
self.register_event()
|
self.register_event()
|
||||||
self.load_setting()
|
|
||||||
|
|
||||||
def load_setting(self):
|
def load_setting(self):
|
||||||
""""""
|
""""""
|
||||||
@ -57,7 +56,7 @@ class PortfolioEngine(BaseEngine):
|
|||||||
""""""
|
""""""
|
||||||
setting: dict = {}
|
setting: dict = {}
|
||||||
|
|
||||||
for strategy in self.strategies:
|
for strategy in self.strategies.values():
|
||||||
setting[strategy.name] = {
|
setting[strategy.name] = {
|
||||||
"name": strategy.name,
|
"name": strategy.name,
|
||||||
"vt_symbol": strategy.vt_symbol,
|
"vt_symbol": strategy.vt_symbol,
|
||||||
@ -76,6 +75,14 @@ class PortfolioEngine(BaseEngine):
|
|||||||
self.event_engine.register(EVENT_ORDER, self.process_order_event)
|
self.event_engine.register(EVENT_ORDER, self.process_order_event)
|
||||||
self.event_engine.register(EVENT_TRADE, self.process_trade_event)
|
self.event_engine.register(EVENT_TRADE, self.process_trade_event)
|
||||||
self.event_engine.register(EVENT_TICK, self.process_tick_event)
|
self.event_engine.register(EVENT_TICK, self.process_tick_event)
|
||||||
|
self.event_engine.register(EVENT_CONTRACT, self.process_contract_event)
|
||||||
|
|
||||||
|
def process_contract_event(self, event: Event):
|
||||||
|
""""""
|
||||||
|
contract: ContractData = event.data
|
||||||
|
|
||||||
|
if contract.vt_symbol in self.symbol_strategy_map:
|
||||||
|
self.subscribe_data(contract.vt_symbol)
|
||||||
|
|
||||||
def process_order_event(self, event: Event):
|
def process_order_event(self, event: Event):
|
||||||
""""""
|
""""""
|
||||||
@ -102,9 +109,9 @@ class PortfolioEngine(BaseEngine):
|
|||||||
trade.vt_orderid)
|
trade.vt_orderid)
|
||||||
if strategy:
|
if strategy:
|
||||||
strategy.update_trade(
|
strategy.update_trade(
|
||||||
trade.trade_direction,
|
trade.direction,
|
||||||
trade.trade_volume,
|
trade.volume,
|
||||||
trade.trade_price
|
trade.price
|
||||||
)
|
)
|
||||||
|
|
||||||
self.put_strategy_event(strategy.name)
|
self.put_strategy_event(strategy.name)
|
||||||
@ -128,7 +135,7 @@ class PortfolioEngine(BaseEngine):
|
|||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
vt_symbol: str,
|
vt_symbol: str,
|
||||||
size: int,
|
size: int = 0,
|
||||||
net_pos: int = 0,
|
net_pos: int = 0,
|
||||||
open_price: float = 0,
|
open_price: float = 0,
|
||||||
last_price: float = 0,
|
last_price: float = 0,
|
||||||
@ -139,6 +146,12 @@ class PortfolioEngine(BaseEngine):
|
|||||||
if name in self.strategies:
|
if name in self.strategies:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if not size:
|
||||||
|
contract = self.main_engine.get_contract(vt_symbol)
|
||||||
|
if not contract:
|
||||||
|
return False
|
||||||
|
size = contract.size
|
||||||
|
|
||||||
strategy = PortfolioStrategy(
|
strategy = PortfolioStrategy(
|
||||||
name,
|
name,
|
||||||
vt_symbol,
|
vt_symbol,
|
||||||
@ -152,8 +165,12 @@ class PortfolioEngine(BaseEngine):
|
|||||||
|
|
||||||
self.strategies[strategy.name] = strategy
|
self.strategies[strategy.name] = strategy
|
||||||
self.symbol_strategy_map[strategy.vt_symbol].append(strategy)
|
self.symbol_strategy_map[strategy.vt_symbol].append(strategy)
|
||||||
|
self.save_setting()
|
||||||
|
|
||||||
self.subscribe_data(vt_symbol)
|
self.subscribe_data(vt_symbol)
|
||||||
|
self.put_strategy_event(name)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
def remove_strategy(self, name: str):
|
def remove_strategy(self, name: str):
|
||||||
""""""
|
""""""
|
||||||
@ -238,6 +255,10 @@ class PortfolioEngine(BaseEngine):
|
|||||||
event = Event(EVENT_PORTFOLIO_UPDATE, strategy)
|
event = Event(EVENT_PORTFOLIO_UPDATE, strategy)
|
||||||
self.event_engine.put(event)
|
self.event_engine.put(event)
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
""""""
|
||||||
|
self.save_setting()
|
||||||
|
|
||||||
|
|
||||||
class PortfolioStrategy:
|
class PortfolioStrategy:
|
||||||
""""""
|
""""""
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
from vnpy.event import EventEngine, Event
|
from vnpy.event import EventEngine
|
||||||
from vnpy.trader.engine import MainEngine
|
from vnpy.trader.engine import MainEngine
|
||||||
from vnpy.trader.constant import Direction, Offset
|
from vnpy.trader.constant import Direction, Offset
|
||||||
from vnpy.trader.ui import QtWidgets, QtCore, QtGui
|
from vnpy.trader.ui import QtWidgets, QtGui
|
||||||
from vnpy.trader.ui.widget import (
|
from vnpy.trader.ui.widget import (
|
||||||
BaseMonitor, BaseCell,
|
BaseMonitor, BaseCell,
|
||||||
BidCell, AskCell,
|
PnlCell, DirectionCell, EnumCell,
|
||||||
TimeCell, PnlCell,
|
|
||||||
DirectionCell, EnumCell,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
from ..engine import (
|
from ..engine import (
|
||||||
@ -43,10 +41,18 @@ class PortfolioManager(QtWidgets.QWidget):
|
|||||||
trade_monitor = PortfolioTradeMonitor(
|
trade_monitor = PortfolioTradeMonitor(
|
||||||
self.main_engine, self.event_engine)
|
self.main_engine, self.event_engine)
|
||||||
|
|
||||||
|
trading_widget = StrategyTradingWidget(self.portfolio_engine)
|
||||||
|
management_widget = StrategyManagementWidget(
|
||||||
|
self.portfolio_engine,
|
||||||
|
trading_widget,
|
||||||
|
)
|
||||||
|
|
||||||
vbox = QtWidgets.QVBoxLayout()
|
vbox = QtWidgets.QVBoxLayout()
|
||||||
|
vbox.addWidget(management_widget)
|
||||||
vbox.addWidget(self.create_group("策略", strategy_monitor))
|
vbox.addWidget(self.create_group("策略", strategy_monitor))
|
||||||
vbox.addWidget(self.create_group("委托", order_monitor))
|
vbox.addWidget(self.create_group("委托", order_monitor))
|
||||||
vbox.addWidget(self.create_group("成交", trade_monitor))
|
vbox.addWidget(self.create_group("成交", trade_monitor))
|
||||||
|
vbox.addWidget(trading_widget)
|
||||||
|
|
||||||
self.setLayout(vbox)
|
self.setLayout(vbox)
|
||||||
|
|
||||||
@ -151,3 +157,220 @@ class PortfolioOrderMonitor(BaseMonitor):
|
|||||||
order = cell.get_data()
|
order = cell.get_data()
|
||||||
req = order.create_cancel_request()
|
req = order.create_cancel_request()
|
||||||
self.main_engine.cancel_order(req, order.gateway_name)
|
self.main_engine.cancel_order(req, order.gateway_name)
|
||||||
|
|
||||||
|
|
||||||
|
class StrategyTradingWidget(QtWidgets.QWidget):
|
||||||
|
""""""
|
||||||
|
|
||||||
|
def __init__(self, portfolio_engine: PortfolioEngine):
|
||||||
|
""""""
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.portfolio_engine = portfolio_engine
|
||||||
|
self.init_ui()
|
||||||
|
self.update_combo()
|
||||||
|
|
||||||
|
def init_ui(self):
|
||||||
|
""""""
|
||||||
|
self.name_combo = QtWidgets.QComboBox()
|
||||||
|
|
||||||
|
self.direction_combo = QtWidgets.QComboBox()
|
||||||
|
self.direction_combo.addItems(
|
||||||
|
[Direction.LONG.value, Direction.SHORT.value])
|
||||||
|
|
||||||
|
self.offset_combo = QtWidgets.QComboBox()
|
||||||
|
self.offset_combo.addItems([offset.value for offset in Offset])
|
||||||
|
|
||||||
|
double_validator = QtGui.QDoubleValidator()
|
||||||
|
double_validator.setBottom(0)
|
||||||
|
|
||||||
|
self.price_line = QtWidgets.QLineEdit()
|
||||||
|
self.price_line.setValidator(double_validator)
|
||||||
|
|
||||||
|
self.volume_line = QtWidgets.QLineEdit()
|
||||||
|
self.volume_line.setValidator(double_validator)
|
||||||
|
|
||||||
|
for w in [
|
||||||
|
self.name_combo,
|
||||||
|
self.price_line,
|
||||||
|
self.volume_line,
|
||||||
|
self.direction_combo,
|
||||||
|
self.offset_combo
|
||||||
|
]:
|
||||||
|
w.setFixedWidth(150)
|
||||||
|
|
||||||
|
send_button = QtWidgets.QPushButton("委托")
|
||||||
|
send_button.clicked.connect(self.send_order)
|
||||||
|
send_button.setFixedWidth(70)
|
||||||
|
|
||||||
|
cancel_button = QtWidgets.QPushButton("全撤")
|
||||||
|
cancel_button.clicked.connect(self.cancel_all)
|
||||||
|
cancel_button.setFixedWidth(70)
|
||||||
|
|
||||||
|
hbox = QtWidgets.QHBoxLayout()
|
||||||
|
hbox.addWidget(QtWidgets.QLabel("策略名称"))
|
||||||
|
hbox.addWidget(self.name_combo)
|
||||||
|
hbox.addWidget(QtWidgets.QLabel("方向"))
|
||||||
|
hbox.addWidget(self.direction_combo)
|
||||||
|
hbox.addWidget(QtWidgets.QLabel("开平"))
|
||||||
|
hbox.addWidget(self.offset_combo)
|
||||||
|
hbox.addWidget(QtWidgets.QLabel("价格"))
|
||||||
|
hbox.addWidget(self.price_line)
|
||||||
|
hbox.addWidget(QtWidgets.QLabel("数量"))
|
||||||
|
hbox.addWidget(self.volume_line)
|
||||||
|
hbox.addWidget(send_button)
|
||||||
|
hbox.addWidget(cancel_button)
|
||||||
|
hbox.addStretch()
|
||||||
|
|
||||||
|
self.setLayout(hbox)
|
||||||
|
|
||||||
|
def send_order(self):
|
||||||
|
""""""
|
||||||
|
name = self.name_combo.currentText()
|
||||||
|
|
||||||
|
price_text = self.price_line.text()
|
||||||
|
volume_text = self.volume_line.text()
|
||||||
|
|
||||||
|
if not price_text or not volume_text:
|
||||||
|
return
|
||||||
|
|
||||||
|
price = float(price_text)
|
||||||
|
volume = float(volume_text)
|
||||||
|
direction = Direction(self.direction_combo.currentText())
|
||||||
|
offset = Offset(self.offset_combo.currentText())
|
||||||
|
|
||||||
|
self.portfolio_engine.send_order(
|
||||||
|
name,
|
||||||
|
price,
|
||||||
|
volume,
|
||||||
|
direction,
|
||||||
|
offset
|
||||||
|
)
|
||||||
|
|
||||||
|
def cancel_all(self):
|
||||||
|
""""""
|
||||||
|
name = self.name_line.text()
|
||||||
|
self.portfolio_engine.cancel_all(name)
|
||||||
|
|
||||||
|
def update_combo(self):
|
||||||
|
""""""
|
||||||
|
strategy_names = list(self.portfolio_engine.strategies.keys())
|
||||||
|
|
||||||
|
self.name_combo.clear()
|
||||||
|
self.name_combo.addItems(strategy_names)
|
||||||
|
|
||||||
|
|
||||||
|
class StrategyManagementWidget(QtWidgets.QWidget):
|
||||||
|
""""""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
portfolio_engine: PortfolioEngine,
|
||||||
|
trading_widget: StrategyTradingWidget
|
||||||
|
):
|
||||||
|
""""""
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.portfolio_engine = portfolio_engine
|
||||||
|
self.trading_widget = trading_widget
|
||||||
|
|
||||||
|
self.init_ui()
|
||||||
|
self.update_combo()
|
||||||
|
|
||||||
|
def init_ui(self):
|
||||||
|
""""""
|
||||||
|
self.name_line = QtWidgets.QLineEdit()
|
||||||
|
self.symbol_line = QtWidgets.QLineEdit()
|
||||||
|
self.remove_combo = QtWidgets.QComboBox()
|
||||||
|
|
||||||
|
for w in [
|
||||||
|
self.name_line,
|
||||||
|
self.symbol_line,
|
||||||
|
self.remove_combo
|
||||||
|
]:
|
||||||
|
w.setFixedWidth(150)
|
||||||
|
|
||||||
|
add_button = QtWidgets.QPushButton("创建策略")
|
||||||
|
add_button.clicked.connect(self.add_strategy)
|
||||||
|
|
||||||
|
remove_button = QtWidgets.QPushButton("移除策略")
|
||||||
|
remove_button.clicked.connect(self.remove_strategy)
|
||||||
|
|
||||||
|
hbox = QtWidgets.QHBoxLayout()
|
||||||
|
hbox.addWidget(QtWidgets.QLabel("策略名称"))
|
||||||
|
hbox.addWidget(self.name_line)
|
||||||
|
hbox.addWidget(QtWidgets.QLabel("交易合约"))
|
||||||
|
hbox.addWidget(self.symbol_line)
|
||||||
|
hbox.addWidget(add_button)
|
||||||
|
hbox.addStretch()
|
||||||
|
hbox.addWidget(self.remove_combo)
|
||||||
|
hbox.addWidget(remove_button)
|
||||||
|
|
||||||
|
self.setLayout(hbox)
|
||||||
|
|
||||||
|
def add_strategy(self):
|
||||||
|
""""""
|
||||||
|
name = self.name_line.text()
|
||||||
|
vt_symbol = self.symbol_line.text()
|
||||||
|
|
||||||
|
if not name or not vt_symbol:
|
||||||
|
QtWidgets.QMessageBox.information(
|
||||||
|
self,
|
||||||
|
"提示",
|
||||||
|
"请输入策略名称和交易合约",
|
||||||
|
QtWidgets.QMessageBox.Ok
|
||||||
|
)
|
||||||
|
|
||||||
|
result = self.portfolio_engine.add_strategy(name, vt_symbol)
|
||||||
|
|
||||||
|
if result:
|
||||||
|
QtWidgets.QMessageBox.information(
|
||||||
|
self,
|
||||||
|
"提示",
|
||||||
|
"策略创建成功",
|
||||||
|
QtWidgets.QMessageBox.Ok
|
||||||
|
)
|
||||||
|
|
||||||
|
self.update_combo()
|
||||||
|
else:
|
||||||
|
QtWidgets.QMessageBox.warning(
|
||||||
|
self,
|
||||||
|
"提示",
|
||||||
|
"策略创建失败,存在重名或找不到合约",
|
||||||
|
QtWidgets.QMessageBox.Ok
|
||||||
|
)
|
||||||
|
|
||||||
|
def remove_strategy(self):
|
||||||
|
""""""
|
||||||
|
name = self.remove_combo.currentText()
|
||||||
|
|
||||||
|
if not name:
|
||||||
|
return
|
||||||
|
|
||||||
|
result = self.portfolio_engine.remove_strategy(name)
|
||||||
|
|
||||||
|
if result:
|
||||||
|
QtWidgets.QMessageBox.information(
|
||||||
|
self,
|
||||||
|
"提示",
|
||||||
|
"策略移除成功",
|
||||||
|
QtWidgets.QMessageBox.Ok
|
||||||
|
)
|
||||||
|
|
||||||
|
self.update_combo()
|
||||||
|
else:
|
||||||
|
QtWidgets.QMessageBox.warning(
|
||||||
|
self,
|
||||||
|
"提示",
|
||||||
|
"策略移除失败,不存在该策略",
|
||||||
|
QtWidgets.QMessageBox.Ok
|
||||||
|
)
|
||||||
|
|
||||||
|
def update_combo(self):
|
||||||
|
""""""
|
||||||
|
strategy_names = list(self.portfolio_engine.strategies.keys())
|
||||||
|
|
||||||
|
self.remove_combo.clear()
|
||||||
|
self.remove_combo.addItems(strategy_names)
|
||||||
|
|
||||||
|
self.trading_widget.update_combo()
|
||||||
|
Loading…
Reference in New Issue
Block a user