[Add] widget for portfolio manager

This commit is contained in:
vn.py 2019-11-13 15:45:27 +08:00
parent c0d15ad9cb
commit 6643d8157e
4 changed files with 99 additions and 774 deletions

View File

@ -33,15 +33,16 @@ from vnpy.gateway.bitmex import BitmexGateway
# from vnpy.gateway.gateios import GateiosGateway
from vnpy.gateway.bybit import BybitGateway
from vnpy.app.cta_strategy import CtaStrategyApp
# from vnpy.app.cta_strategy import CtaStrategyApp
# from vnpy.app.csv_loader import CsvLoaderApp
# from vnpy.app.algo_trading import AlgoTradingApp
from vnpy.app.cta_backtester import CtaBacktesterApp
from vnpy.app.data_recorder import DataRecorderApp
# from vnpy.app.cta_backtester import CtaBacktesterApp
# from vnpy.app.data_recorder import DataRecorderApp
# from vnpy.app.risk_manager import RiskManagerApp
from vnpy.app.script_trader import ScriptTraderApp
from vnpy.app.rpc_service import RpcServiceApp
from vnpy.app.spread_trading import SpreadTradingApp
# from vnpy.app.script_trader import ScriptTraderApp
# from vnpy.app.rpc_service import RpcServiceApp
# from vnpy.app.spread_trading import SpreadTradingApp
from vnpy.app.portfolio_manager import PortfolioManagerApp
def main():
@ -81,15 +82,16 @@ def main():
# main_engine.add_gateway(GateiosGateway)
main_engine.add_gateway(BybitGateway)
main_engine.add_app(CtaStrategyApp)
main_engine.add_app(CtaBacktesterApp)
# main_engine.add_app(CtaStrategyApp)
# main_engine.add_app(CtaBacktesterApp)
# main_engine.add_app(CsvLoaderApp)
# main_engine.add_app(AlgoTradingApp)
main_engine.add_app(DataRecorderApp)
# main_engine.add_app(DataRecorderApp)
# main_engine.add_app(RiskManagerApp)
# main_engine.add_app(ScriptTraderApp)
# main_engine.add_app(RpcServiceApp)
main_engine.add_app(SpreadTradingApp)
# main_engine.add_app(SpreadTradingApp)
main_engine.add_app(PortfolioManagerApp)
main_window = MainWindow(main_engine, event_engine)
main_window.showMaximized()

View File

@ -5,7 +5,7 @@ from vnpy.trader.app import BaseApp
from .engine import PortfolioEngine, APP_NAME
class DataRecorderApp(BaseApp):
class PortfolioManagerApp(BaseApp):
""""""
app_name = APP_NAME
app_module = __module__

View File

@ -1,6 +1,7 @@
from datetime import datetime
from typing import Dict, List, Set
from collections import defaultdict
from copy import copy
from vnpy.event import Event, EventEngine
from vnpy.trader.engine import BaseEngine, MainEngine
@ -16,6 +17,8 @@ from vnpy.trader.utility import load_json, save_json
APP_NAME = "PortfolioManager"
EVENT_PORTFOLIO_UPDATE = "ePortfioUpdate"
EVENT_PORTFOLIO_ORDER = "ePortfioUpdate"
EVENT_PORTFOLIO_TRADE = "ePortfioUpdate"
class PortfolioEngine(BaseEngine):
@ -84,6 +87,13 @@ class PortfolioEngine(BaseEngine):
if not order.is_active():
self.active_orders.remove(order.vt_orderid)
strategy: PortfolioStrategy = self.order_strategy_map[order.vt_orderid]
strategy_order = copy(order)
strategy_order.gateway_name = strategy.name
event = Event(EVENT_PORTFOLIO_ORDER, strategy_order)
self.event_engine.put(event)
def process_trade_event(self, event: Event):
""""""
trade: TradeData = event.data
@ -99,6 +109,11 @@ class PortfolioEngine(BaseEngine):
self.put_strategy_event(strategy.name)
strategy_trade = copy(trade)
strategy_trade.gateway_name = strategy.name
event = Event(EVENT_PORTFOLIO_TRADE, strategy_trade)
self.event_engine.put(event)
def process_tick_event(self, event: Event):
""""""
tick: TickData = event.data

View File

@ -1,7 +1,3 @@
"""
Widget for spread trading.
"""
from vnpy.event import EventEngine, Event
from vnpy.trader.engine import MainEngine
from vnpy.trader.constant import Direction, Offset
@ -14,18 +10,15 @@ from vnpy.trader.ui.widget import (
)
from ..engine import (
SpreadEngine,
SpreadStrategyEngine,
PortfolioEngine,
APP_NAME,
EVENT_SPREAD_DATA,
EVENT_SPREAD_POS,
EVENT_SPREAD_LOG,
EVENT_SPREAD_ALGO,
EVENT_SPREAD_STRATEGY
EVENT_PORTFOLIO_UPDATE,
EVENT_PORTFOLIO_TRADE,
EVENT_PORTFOLIO_ORDER
)
class SpreadManager(QtWidgets.QWidget):
class PortfolioManager(QtWidgets.QWidget):
""""""
def __init__(self, main_engine: MainEngine, event_engine: EventEngine):
@ -35,50 +28,31 @@ class SpreadManager(QtWidgets.QWidget):
self.main_engine = main_engine
self.event_engine = event_engine
self.spread_engine = main_engine.get_engine(APP_NAME)
self.portfolio_engine = main_engine.get_engine(APP_NAME)
self.init_ui()
def init_ui(self):
""""""
self.setWindowTitle("价差交易")
self.setWindowTitle("投资组合")
self.algo_dialog = SpreadAlgoWidget(self.spread_engine)
algo_group = self.create_group("交易", self.algo_dialog)
algo_group.setMaximumWidth(300)
strategy_monitor = PortfolioStrategyMonitor(
self.main_engine, self.event_engine)
order_monitor = PortfolioOrderMonitor(
self.main_engine, self.event_engine)
trade_monitor = PortfolioTradeMonitor(
self.main_engine, self.event_engine)
self.data_monitor = SpreadDataMonitor(
self.main_engine,
self.event_engine
)
self.log_monitor = SpreadLogMonitor(
self.main_engine,
self.event_engine
)
self.algo_monitor = SpreadAlgoMonitor(
self.spread_engine
)
vbox = QtWidgets.QVBoxLayout()
vbox.addWidget(self.create_group("策略", strategy_monitor))
vbox.addWidget(self.create_group("委托", order_monitor))
vbox.addWidget(self.create_group("成交", trade_monitor))
self.strategy_monitor = SpreadStrategyMonitor(
self.spread_engine
)
grid = QtWidgets.QGridLayout()
grid.addWidget(self.create_group("价差", self.data_monitor), 0, 0)
grid.addWidget(self.create_group("日志", self.log_monitor), 1, 0)
grid.addWidget(self.create_group("算法", self.algo_monitor), 0, 1)
grid.addWidget(self.create_group("策略", self.strategy_monitor), 1, 1)
hbox = QtWidgets.QHBoxLayout()
hbox.addWidget(algo_group)
hbox.addLayout(grid)
self.setLayout(hbox)
self.setLayout(vbox)
def show(self):
""""""
self.spread_engine.start()
self.algo_dialog.update_class_combo()
self.portfolio_engine.load_setting()
self.showMaximized()
def create_group(self, title: str, widget: QtWidgets.QWidget):
@ -94,752 +68,86 @@ class SpreadManager(QtWidgets.QWidget):
return group
class SpreadDataMonitor(BaseMonitor):
class PortfolioStrategyMonitor(BaseMonitor):
"""
Monitor for spread data.
Monitor for portfolio strategy.
"""
event_type = EVENT_SPREAD_DATA
event_type = EVENT_PORTFOLIO_UPDATE
data_key = "name"
sorting = False
headers = {
"name": {"display": "名称", "cell": BaseCell, "update": False},
"bid_volume": {"display": "买量", "cell": BidCell, "update": True},
"bid_price": {"display": "买价", "cell": BidCell, "update": True},
"ask_price": {"display": "卖价", "cell": AskCell, "update": True},
"ask_volume": {"display": "卖量", "cell": AskCell, "update": True},
"net_pos": {"display": "净仓", "cell": PnlCell, "update": True},
"datetime": {"display": "时间", "cell": TimeCell, "update": True},
"price_formula": {"display": "定价", "cell": BaseCell, "update": False},
"trading_formula": {"display": "交易", "cell": BaseCell, "update": False},
"name": {"display": "策略名称", "cell": BaseCell, "update": False},
"vt_symbol": {"display": "交易合约", "cell": BaseCell, "update": False},
"size": {"display": "合约乘数", "cell": BaseCell, "update": False},
"net_pos": {"display": "策略持仓", "cell": BaseCell, "update": True},
"open_price": {"display": "持仓价格", "cell": BaseCell, "update": True},
"last_price": {"display": "最新价格", "cell": BaseCell, "update": True},
"pos_pnl": {"display": "持仓盈亏", "cell": PnlCell, "update": True},
"create_time": {"display": "创建时间", "cell": BaseCell, "update": False},
}
def register_event(self):
class PortfolioTradeMonitor(BaseMonitor):
"""
Register event handler into event engine.
"""
super().register_event()
self.event_engine.register(EVENT_SPREAD_POS, self.signal.emit)
class SpreadLogMonitor(QtWidgets.QTextEdit):
"""
Monitor for log data.
"""
signal = 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.init_ui()
self.register_event()
def init_ui(self):
""""""
self.setReadOnly(True)
def register_event(self):
""""""
self.signal.connect(self.process_log_event)
self.event_engine.register(EVENT_SPREAD_LOG, self.signal.emit)
def process_log_event(self, event: Event):
""""""
log = event.data
msg = f"{log.time.strftime('%H:%M:%S')}\t{log.msg}"
self.append(msg)
class SpreadAlgoMonitor(BaseMonitor):
"""
Monitor for algo status.
Monitor for trade data.
"""
event_type = EVENT_SPREAD_ALGO
data_key = "algoid"
sorting = False
event_type = EVENT_PORTFOLIO_TRADE
data_key = ""
headers = {
"algoid": {"display": "算法", "cell": BaseCell, "update": False},
"spread_name": {"display": "价差", "cell": BaseCell, "update": False},
"gateway_name": {"display": "接口名称", "cell": BaseCell, "update": False},
"tradeid": {"display": "成交号 ", "cell": BaseCell, "update": False},
"orderid": {"display": "委托号", "cell": BaseCell, "update": False},
"symbol": {"display": "代码", "cell": BaseCell, "update": False},
"exchange": {"display": "交易所", "cell": EnumCell, "update": False},
"direction": {"display": "方向", "cell": DirectionCell, "update": False},
"offset": {"display": "开平", "cell": EnumCell, "update": False},
"price": {"display": "价格", "cell": BaseCell, "update": False},
"payup": {"display": "超价", "cell": BaseCell, "update": False},
"volume": {"display": "数量", "cell": BaseCell, "update": False},
"traded_volume": {"display": "成交", "cell": BaseCell, "update": True},
"interval": {"display": "间隔", "cell": BaseCell, "update": False},
"count": {"display": "计数", "cell": BaseCell, "update": True},
"status": {"display": "状态", "cell": EnumCell, "update": True},
"time": {"display": "时间", "cell": BaseCell, "update": False},
}
def __init__(self, spread_engine: SpreadEngine):
""""""
super().__init__(spread_engine.main_engine, spread_engine.event_engine)
self.spread_engine = spread_engine
class PortfolioOrderMonitor(BaseMonitor):
"""
Monitor for order data.
"""
event_type = EVENT_PORTFOLIO_ORDER
data_key = "vt_orderid"
sorting = True
headers = {
"gateway_name": {"display": "接口名称", "cell": BaseCell, "update": False},
"orderid": {"display": "委托号", "cell": BaseCell, "update": False},
"symbol": {"display": "代码", "cell": BaseCell, "update": False},
"exchange": {"display": "交易所", "cell": EnumCell, "update": False},
"type": {"display": "类型", "cell": EnumCell, "update": False},
"direction": {"display": "方向", "cell": DirectionCell, "update": False},
"offset": {"display": "开平", "cell": EnumCell, "update": False},
"price": {"display": "价格", "cell": BaseCell, "update": False},
"volume": {"display": "总数量", "cell": BaseCell, "update": True},
"traded": {"display": "已成交", "cell": BaseCell, "update": True},
"status": {"display": "状态", "cell": EnumCell, "update": True},
"time": {"display": "时间", "cell": BaseCell, "update": True},
}
def init_ui(self):
"""
Connect signal.
"""
super().init_ui()
super(PortfolioOrderMonitor, self).init_ui()
self.setToolTip("双击单元格停止算法")
self.itemDoubleClicked.connect(self.stop_algo)
self.setToolTip("双击单元格撤单")
self.itemDoubleClicked.connect(self.cancel_order)
def stop_algo(self, cell):
def cancel_order(self, cell):
"""
Stop algo if cell double clicked.
Cancel order if cell double clicked.
"""
algo = cell.get_data()
self.spread_engine.stop_algo(algo.algoid)
class SpreadAlgoWidget(QtWidgets.QFrame):
""""""
def __init__(self, spread_engine: SpreadEngine):
""""""
super().__init__()
self.spread_engine: SpreadEngine = spread_engine
self.strategy_engine: SpreadStrategyEngine = spread_engine.strategy_engine
self.init_ui()
def init_ui(self):
""""""
self.setWindowTitle("启动算法")
self.setFrameShape(self.Box)
self.setLineWidth(1)
self.name_line = QtWidgets.QLineEdit()
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.NONE.value, Offset.OPEN.value, Offset.CLOSE.value]
)
float_validator = QtGui.QDoubleValidator()
self.price_line = QtWidgets.QLineEdit()
self.price_line.setValidator(float_validator)
self.volume_line = QtWidgets.QLineEdit()
self.volume_line.setValidator(float_validator)
int_validator = QtGui.QIntValidator()
self.payup_line = QtWidgets.QLineEdit()
self.payup_line.setValidator(int_validator)
self.interval_line = QtWidgets.QLineEdit()
self.interval_line.setValidator(int_validator)
button_start = QtWidgets.QPushButton("启动")
button_start.clicked.connect(self.start_algo)
self.lock_combo = QtWidgets.QComboBox()
self.lock_combo.addItems(
["", ""]
)
self.class_combo = QtWidgets.QComboBox()
add_button = QtWidgets.QPushButton("添加策略")
add_button.clicked.connect(self.add_strategy)
init_button = QtWidgets.QPushButton("全部初始化")
init_button.clicked.connect(self.strategy_engine.init_all_strategies)
start_button = QtWidgets.QPushButton("全部启动")
start_button.clicked.connect(self.strategy_engine.start_all_strategies)
stop_button = QtWidgets.QPushButton("全部停止")
stop_button.clicked.connect(self.strategy_engine.stop_all_strategies)
add_spread_button = QtWidgets.QPushButton("创建价差")
add_spread_button.clicked.connect(self.add_spread)
remove_spread_button = QtWidgets.QPushButton("移除价差")
remove_spread_button.clicked.connect(self.remove_spread)
form = QtWidgets.QFormLayout()
form.addRow("价差", self.name_line)
form.addRow("方向", self.direction_combo)
form.addRow("开平", self.offset_combo)
form.addRow("价格", self.price_line)
form.addRow("数量", self.volume_line)
form.addRow("超价", self.payup_line)
form.addRow("间隔", self.interval_line)
form.addRow("锁仓", self.lock_combo)
form.addRow(button_start)
vbox = QtWidgets.QVBoxLayout()
vbox.addLayout(form)
vbox.addStretch()
vbox.addWidget(self.class_combo)
vbox.addWidget(add_button)
vbox.addWidget(init_button)
vbox.addWidget(start_button)
vbox.addWidget(stop_button)
vbox.addStretch()
vbox.addWidget(add_spread_button)
vbox.addWidget(remove_spread_button)
self.setLayout(vbox)
def start_algo(self):
""""""
name = self.name_line.text()
direction = Direction(self.direction_combo.currentText())
offset = Offset(self.offset_combo.currentText())
price = float(self.price_line.text())
volume = float(self.volume_line.text())
payup = int(self.payup_line.text())
interval = int(self.interval_line.text())
lock_str = self.lock_combo.currentText()
if lock_str == "":
lock = True
else:
lock = False
self.spread_engine.start_algo(
name, direction, offset, price, volume, payup, interval, lock
)
def add_spread(self):
""""""
dialog = SpreadDataDialog(self.spread_engine)
dialog.exec_()
def remove_spread(self):
""""""
dialog = SpreadRemoveDialog(self.spread_engine)
dialog.exec_()
def update_class_combo(self):
""""""
self.class_combo.addItems(
self.strategy_engine.get_all_strategy_class_names()
)
def remove_strategy(self, strategy_name):
""""""
manager = self.managers.pop(strategy_name)
manager.deleteLater()
def add_strategy(self):
""""""
class_name = str(self.class_combo.currentText())
if not class_name:
return
parameters = self.strategy_engine.get_strategy_class_parameters(
class_name)
editor = SettingEditor(parameters, class_name=class_name)
n = editor.exec_()
if n == editor.Accepted:
setting = editor.get_setting()
spread_name = setting.pop("spread_name")
strategy_name = setting.pop("strategy_name")
self.strategy_engine.add_strategy(
class_name, strategy_name, spread_name, setting
)
class SpreadDataDialog(QtWidgets.QDialog):
""""""
def __init__(self, spread_engine: SpreadEngine):
""""""
super().__init__()
self.spread_engine: SpreadEngine = spread_engine
self.leg_widgets = []
self.init_ui()
def init_ui(self):
""""""
self.setWindowTitle("创建价差")
self.name_line = QtWidgets.QLineEdit()
self.active_line = QtWidgets.QLineEdit()
self.min_volume_combo = QtWidgets.QComboBox()
self.min_volume_combo.addItems([
"1",
"0.1",
"0.01",
"0.001",
"0.0001",
"0.00001",
"0.000001",
])
self.grid = QtWidgets.QGridLayout()
button_add = QtWidgets.QPushButton("创建价差")
button_add.clicked.connect(self.add_spread)
Label = QtWidgets.QLabel
grid = QtWidgets.QGridLayout()
grid.addWidget(Label("价差名称"), 0, 0)
grid.addWidget(self.name_line, 0, 1, 1, 4)
grid.addWidget(Label("主动腿代码"), 1, 0)
grid.addWidget(self.active_line, 1, 1, 1, 4)
grid.addWidget(Label("最小交易量"), 2, 0)
grid.addWidget(self.min_volume_combo, 2, 1, 1, 4)
grid.addWidget(Label(""), 3, 0)
grid.addWidget(Label("本地代码"), 4, 1)
grid.addWidget(Label("价格乘数"), 4, 2)
grid.addWidget(Label("交易乘数"), 4, 3)
grid.addWidget(Label("合约模式"), 4, 4)
int_validator = QtGui.QIntValidator()
leg_count = 5
for i in range(leg_count):
symbol_line = QtWidgets.QLineEdit()
price_line = QtWidgets.QLineEdit()
price_line.setValidator(int_validator)
trading_line = QtWidgets.QLineEdit()
trading_line.setValidator(int_validator)
inverse_combo = QtWidgets.QComboBox()
inverse_combo.addItems(["正向", "反向"])
grid.addWidget(Label("{}".format(i + 1)), 5 + i, 0)
grid.addWidget(symbol_line, 5 + i, 1)
grid.addWidget(price_line, 5 + i, 2)
grid.addWidget(trading_line, 5 + i, 3)
grid.addWidget(inverse_combo, 5 + i, 4)
d = {
"symbol": symbol_line,
"price": price_line,
"trading": trading_line,
"inverse": inverse_combo
}
self.leg_widgets.append(d)
grid.addWidget(Label(""), 5 + leg_count, 0,)
grid.addWidget(button_add, 6 + leg_count, 0, 1, 5)
self.setLayout(grid)
def add_spread(self):
""""""
spread_name = self.name_line.text()
if not spread_name:
QtWidgets.QMessageBox.warning(
self,
"创建失败",
"请输入价差名称",
QtWidgets.QMessageBox.Ok
)
return
active_symbol = self.active_line.text()
min_volume = float(self.min_volume_combo.currentText())
leg_settings = {}
for d in self.leg_widgets:
try:
vt_symbol = d["symbol"].text()
price_multiplier = int(d["price"].text())
trading_multiplier = int(d["trading"].text())
if d["inverse"].currentText() == "正向":
inverse_contract = False
else:
inverse_contract = True
leg_settings[vt_symbol] = {
"vt_symbol": vt_symbol,
"price_multiplier": price_multiplier,
"trading_multiplier": trading_multiplier,
"inverse_contract": inverse_contract
}
except ValueError:
pass
if len(leg_settings) < 2:
QtWidgets.QMessageBox.warning(
self,
"创建失败",
"价差最少需要2条腿",
QtWidgets.QMessageBox.Ok
)
return
if active_symbol not in leg_settings:
QtWidgets.QMessageBox.warning(
self,
"创建失败",
"各条腿中找不到主动腿代码",
QtWidgets.QMessageBox.Ok
)
return
self.spread_engine.add_spread(
spread_name,
list(leg_settings.values()),
active_symbol,
min_volume
)
self.accept()
class SpreadRemoveDialog(QtWidgets.QDialog):
""""""
def __init__(self, spread_engine: SpreadEngine):
""""""
super().__init__()
self.spread_engine: SpreadEngine = spread_engine
self.init_ui()
def init_ui(self):
""""""
self.setWindowTitle("移除价差")
self.setMinimumWidth(300)
self.name_combo = QtWidgets.QComboBox()
spreads = self.spread_engine.get_all_spreads()
for spread in spreads:
self.name_combo.addItem(spread.name)
button_remove = QtWidgets.QPushButton("移除")
button_remove.clicked.connect(self.remove_spread)
hbox = QtWidgets.QHBoxLayout()
hbox.addWidget(self.name_combo)
hbox.addWidget(button_remove)
self.setLayout(hbox)
def remove_spread(self):
""""""
spread_name = self.name_combo.currentText()
self.spread_engine.remove_spread(spread_name)
self.accept()
class SpreadStrategyMonitor(QtWidgets.QWidget):
""""""
signal_strategy = QtCore.pyqtSignal(Event)
def __init__(self, spread_engine: SpreadEngine):
super().__init__()
self.strategy_engine = spread_engine.strategy_engine
self.main_engine = spread_engine.main_engine
self.event_engine = spread_engine.event_engine
self.managers = {}
self.init_ui()
self.register_event()
def init_ui(self):
""""""
self.scroll_layout = QtWidgets.QVBoxLayout()
self.scroll_layout.addStretch()
scroll_widget = QtWidgets.QWidget()
scroll_widget.setLayout(self.scroll_layout)
scroll_area = QtWidgets.QScrollArea()
scroll_area.setWidgetResizable(True)
scroll_area.setWidget(scroll_widget)
vbox = QtWidgets.QVBoxLayout()
vbox.addWidget(scroll_area)
self.setLayout(vbox)
def register_event(self):
""""""
self.signal_strategy.connect(self.process_strategy_event)
self.event_engine.register(
EVENT_SPREAD_STRATEGY, self.signal_strategy.emit
)
def process_strategy_event(self, event):
"""
Update strategy status onto its monitor.
"""
data = event.data
strategy_name = data["strategy_name"]
if strategy_name in self.managers:
manager = self.managers[strategy_name]
manager.update_data(data)
else:
manager = SpreadStrategyWidget(self, self.strategy_engine, data)
self.scroll_layout.insertWidget(0, manager)
self.managers[strategy_name] = manager
def remove_strategy(self, strategy_name):
""""""
manager = self.managers.pop(strategy_name)
manager.deleteLater()
class SpreadStrategyWidget(QtWidgets.QFrame):
"""
Manager for a strategy
"""
def __init__(
self,
strategy_monitor: SpreadStrategyMonitor,
strategy_engine: SpreadStrategyEngine,
data: dict
):
""""""
super().__init__()
self.strategy_monitor = strategy_monitor
self.strategy_engine = strategy_engine
self.strategy_name = data["strategy_name"]
self._data = data
self.init_ui()
def init_ui(self):
""""""
self.setFixedHeight(300)
self.setFrameShape(self.Box)
self.setLineWidth(1)
init_button = QtWidgets.QPushButton("初始化")
init_button.clicked.connect(self.init_strategy)
start_button = QtWidgets.QPushButton("启动")
start_button.clicked.connect(self.start_strategy)
stop_button = QtWidgets.QPushButton("停止")
stop_button.clicked.connect(self.stop_strategy)
edit_button = QtWidgets.QPushButton("编辑")
edit_button.clicked.connect(self.edit_strategy)
remove_button = QtWidgets.QPushButton("移除")
remove_button.clicked.connect(self.remove_strategy)
strategy_name = self._data["strategy_name"]
spread_name = self._data["spread_name"]
class_name = self._data["class_name"]
author = self._data["author"]
label_text = (
f"{strategy_name} - {spread_name} ({class_name} by {author})"
)
label = QtWidgets.QLabel(label_text)
label.setAlignment(QtCore.Qt.AlignCenter)
self.parameters_monitor = StrategyDataMonitor(self._data["parameters"])
self.variables_monitor = StrategyDataMonitor(self._data["variables"])
hbox = QtWidgets.QHBoxLayout()
hbox.addWidget(init_button)
hbox.addWidget(start_button)
hbox.addWidget(stop_button)
hbox.addWidget(edit_button)
hbox.addWidget(remove_button)
vbox = QtWidgets.QVBoxLayout()
vbox.addWidget(label)
vbox.addLayout(hbox)
vbox.addWidget(self.parameters_monitor)
vbox.addWidget(self.variables_monitor)
self.setLayout(vbox)
def update_data(self, data: dict):
""""""
self._data = data
self.parameters_monitor.update_data(data["parameters"])
self.variables_monitor.update_data(data["variables"])
def init_strategy(self):
""""""
self.strategy_engine.init_strategy(self.strategy_name)
def start_strategy(self):
""""""
self.strategy_engine.start_strategy(self.strategy_name)
def stop_strategy(self):
""""""
self.strategy_engine.stop_strategy(self.strategy_name)
def edit_strategy(self):
""""""
strategy_name = self._data["strategy_name"]
parameters = self.strategy_engine.get_strategy_parameters(
strategy_name)
editor = SettingEditor(parameters, strategy_name=strategy_name)
n = editor.exec_()
if n == editor.Accepted:
setting = editor.get_setting()
self.strategy_engine.edit_strategy(strategy_name, setting)
def remove_strategy(self):
""""""
result = self.strategy_engine.remove_strategy(self.strategy_name)
# Only remove strategy gui manager if it has been removed from engine
if result:
self.strategy_monitor.remove_strategy(self.strategy_name)
class StrategyDataMonitor(QtWidgets.QTableWidget):
"""
Table monitor for parameters and variables.
"""
def __init__(self, data: dict):
""""""
super().__init__()
self._data = data
self.cells = {}
self.init_ui()
def init_ui(self):
""""""
labels = list(self._data.keys())
self.setColumnCount(len(labels))
self.setHorizontalHeaderLabels(labels)
self.setRowCount(1)
self.verticalHeader().setSectionResizeMode(
QtWidgets.QHeaderView.Stretch
)
self.verticalHeader().setVisible(False)
self.setEditTriggers(self.NoEditTriggers)
for column, name in enumerate(self._data.keys()):
value = self._data[name]
cell = QtWidgets.QTableWidgetItem(str(value))
cell.setTextAlignment(QtCore.Qt.AlignCenter)
self.setItem(0, column, cell)
self.cells[name] = cell
def update_data(self, data: dict):
""""""
for name, value in data.items():
cell = self.cells[name]
cell.setText(str(value))
class SettingEditor(QtWidgets.QDialog):
"""
For creating new strategy and editing strategy parameters.
"""
def __init__(
self, parameters: dict, strategy_name: str = "", class_name: str = ""
):
""""""
super(SettingEditor, self).__init__()
self.parameters = parameters
self.strategy_name = strategy_name
self.class_name = class_name
self.edits = {}
self.init_ui()
def init_ui(self):
""""""
form = QtWidgets.QFormLayout()
# Add spread_name and name edit if add new strategy
if self.class_name:
self.setWindowTitle(f"添加策略:{self.class_name}")
button_text = "添加"
parameters = {"strategy_name": "", "spread_name": ""}
parameters.update(self.parameters)
else:
self.setWindowTitle(f"参数编辑:{self.strategy_name}")
button_text = "确定"
parameters = self.parameters
for name, value in parameters.items():
type_ = type(value)
edit = QtWidgets.QLineEdit(str(value))
if type_ is int:
validator = QtGui.QIntValidator()
edit.setValidator(validator)
elif type_ is float:
validator = QtGui.QDoubleValidator()
edit.setValidator(validator)
form.addRow(f"{name} {type_}", edit)
self.edits[name] = (edit, type_)
button = QtWidgets.QPushButton(button_text)
button.clicked.connect(self.accept)
form.addRow(button)
self.setLayout(form)
def get_setting(self):
""""""
setting = {}
if self.class_name:
setting["class_name"] = self.class_name
for name, tp in self.edits.items():
edit, type_ = tp
value_text = edit.text()
if type_ == bool:
if value_text == "True":
value = True
else:
value = False
else:
value = type_(value_text)
setting[name] = value
return setting
order = cell.get_data()
req = order.create_cancel_request()
self.main_engine.cancel_order(req, order.gateway_name)