commit
d7577f6b54
@ -398,3 +398,7 @@ class BacktesterEngine(BaseEngine):
|
|||||||
def get_all_orders(self):
|
def get_all_orders(self):
|
||||||
""""""
|
""""""
|
||||||
return self.backtesting_engine.get_all_orders()
|
return self.backtesting_engine.get_all_orders()
|
||||||
|
|
||||||
|
def get_all_daily_results(self):
|
||||||
|
""""""
|
||||||
|
return self.backtesting_engine.get_all_daily_results()
|
||||||
|
@ -12,6 +12,7 @@ from ..engine import (
|
|||||||
from vnpy.trader.constant import Interval
|
from vnpy.trader.constant import Interval
|
||||||
from vnpy.trader.engine import MainEngine
|
from vnpy.trader.engine import MainEngine
|
||||||
from vnpy.trader.ui import QtCore, QtWidgets, QtGui
|
from vnpy.trader.ui import QtCore, QtWidgets, QtGui
|
||||||
|
from vnpy.trader.ui.widget import BaseMonitor, BaseCell, DirectionCell, EnumCell
|
||||||
from vnpy.event import Event, EventEngine
|
from vnpy.event import Event, EventEngine
|
||||||
|
|
||||||
|
|
||||||
@ -95,11 +96,26 @@ class BacktesterManager(QtWidgets.QWidget):
|
|||||||
downloading_button = QtWidgets.QPushButton("下载数据")
|
downloading_button = QtWidgets.QPushButton("下载数据")
|
||||||
downloading_button.clicked.connect(self.start_downloading)
|
downloading_button.clicked.connect(self.start_downloading)
|
||||||
|
|
||||||
|
self.order_button = QtWidgets.QPushButton("委托记录")
|
||||||
|
self.order_button.clicked.connect(self.show_backtesting_orders)
|
||||||
|
self.order_button.setEnabled(False)
|
||||||
|
|
||||||
|
self.trade_button = QtWidgets.QPushButton("成交记录")
|
||||||
|
self.trade_button.clicked.connect(self.show_backtesting_trades)
|
||||||
|
self.trade_button.setEnabled(False)
|
||||||
|
|
||||||
|
self.daily_button = QtWidgets.QPushButton("每日盈亏")
|
||||||
|
self.daily_button.clicked.connect(self.show_daily_results)
|
||||||
|
self.daily_button.setEnabled(False)
|
||||||
|
|
||||||
for button in [
|
for button in [
|
||||||
backtesting_button,
|
backtesting_button,
|
||||||
optimization_button,
|
optimization_button,
|
||||||
downloading_button,
|
downloading_button,
|
||||||
self.result_button
|
self.result_button,
|
||||||
|
self.order_button,
|
||||||
|
self.trade_button,
|
||||||
|
self.daily_button
|
||||||
]:
|
]:
|
||||||
button.setFixedHeight(button.sizeHint().height() * 2)
|
button.setFixedHeight(button.sizeHint().height() * 2)
|
||||||
|
|
||||||
@ -114,12 +130,16 @@ class BacktesterManager(QtWidgets.QWidget):
|
|||||||
form.addRow("合约乘数", self.size_line)
|
form.addRow("合约乘数", self.size_line)
|
||||||
form.addRow("价格跳动", self.pricetick_line)
|
form.addRow("价格跳动", self.pricetick_line)
|
||||||
form.addRow("回测资金", self.capital_line)
|
form.addRow("回测资金", self.capital_line)
|
||||||
form.addRow(backtesting_button)
|
|
||||||
|
|
||||||
left_vbox = QtWidgets.QVBoxLayout()
|
left_vbox = QtWidgets.QVBoxLayout()
|
||||||
left_vbox.addLayout(form)
|
left_vbox.addLayout(form)
|
||||||
|
left_vbox.addWidget(backtesting_button)
|
||||||
left_vbox.addWidget(downloading_button)
|
left_vbox.addWidget(downloading_button)
|
||||||
left_vbox.addStretch()
|
left_vbox.addStretch()
|
||||||
|
left_vbox.addWidget(self.trade_button)
|
||||||
|
left_vbox.addWidget(self.order_button)
|
||||||
|
left_vbox.addWidget(self.daily_button)
|
||||||
|
left_vbox.addStretch()
|
||||||
left_vbox.addWidget(optimization_button)
|
left_vbox.addWidget(optimization_button)
|
||||||
left_vbox.addWidget(self.result_button)
|
left_vbox.addWidget(self.result_button)
|
||||||
|
|
||||||
@ -132,6 +152,25 @@ class BacktesterManager(QtWidgets.QWidget):
|
|||||||
self.chart = BacktesterChart()
|
self.chart = BacktesterChart()
|
||||||
self.chart.setMinimumWidth(1000)
|
self.chart.setMinimumWidth(1000)
|
||||||
|
|
||||||
|
self.trade_dialog = BacktestingResultDialog(
|
||||||
|
self.main_engine,
|
||||||
|
self.event_engine,
|
||||||
|
"回测成交记录",
|
||||||
|
BacktestingTradeMonitor
|
||||||
|
)
|
||||||
|
self.order_dialog = BacktestingResultDialog(
|
||||||
|
self.main_engine,
|
||||||
|
self.event_engine,
|
||||||
|
"回测委托记录",
|
||||||
|
BacktestingOrderMonitor
|
||||||
|
)
|
||||||
|
self.daily_dialog = BacktestingResultDialog(
|
||||||
|
self.main_engine,
|
||||||
|
self.event_engine,
|
||||||
|
"回测每日盈亏",
|
||||||
|
DailyResultMonitor
|
||||||
|
)
|
||||||
|
|
||||||
# Layout
|
# Layout
|
||||||
vbox = QtWidgets.QVBoxLayout()
|
vbox = QtWidgets.QVBoxLayout()
|
||||||
vbox.addWidget(self.statistics_monitor)
|
vbox.addWidget(self.statistics_monitor)
|
||||||
@ -176,6 +215,10 @@ class BacktesterManager(QtWidgets.QWidget):
|
|||||||
df = self.backtester_engine.get_result_df()
|
df = self.backtester_engine.get_result_df()
|
||||||
self.chart.set_data(df)
|
self.chart.set_data(df)
|
||||||
|
|
||||||
|
self.trade_button.setEnabled(True)
|
||||||
|
self.order_button.setEnabled(True)
|
||||||
|
self.daily_button.setEnabled(True)
|
||||||
|
|
||||||
def process_optimization_finished_event(self, event: Event):
|
def process_optimization_finished_event(self, event: Event):
|
||||||
""""""
|
""""""
|
||||||
self.write_log("请点击[优化结果]按钮查看")
|
self.write_log("请点击[优化结果]按钮查看")
|
||||||
@ -221,6 +264,14 @@ class BacktesterManager(QtWidgets.QWidget):
|
|||||||
self.statistics_monitor.clear_data()
|
self.statistics_monitor.clear_data()
|
||||||
self.chart.clear_data()
|
self.chart.clear_data()
|
||||||
|
|
||||||
|
self.trade_button.setEnabled(False)
|
||||||
|
self.order_button.setEnabled(False)
|
||||||
|
self.daily_button.setEnabled(False)
|
||||||
|
|
||||||
|
self.trade_dialog.clear_data()
|
||||||
|
self.order_dialog.clear_data()
|
||||||
|
self.daily_dialog.clear_data()
|
||||||
|
|
||||||
def start_optimization(self):
|
def start_optimization(self):
|
||||||
""""""
|
""""""
|
||||||
class_name = self.class_combo.currentText()
|
class_name = self.class_combo.currentText()
|
||||||
@ -284,6 +335,30 @@ class BacktesterManager(QtWidgets.QWidget):
|
|||||||
)
|
)
|
||||||
dialog.exec_()
|
dialog.exec_()
|
||||||
|
|
||||||
|
def show_backtesting_trades(self):
|
||||||
|
""""""
|
||||||
|
if not self.trade_dialog.is_updated():
|
||||||
|
trades = self.backtester_engine.get_all_trades()
|
||||||
|
self.trade_dialog.update_data(trades)
|
||||||
|
|
||||||
|
self.trade_dialog.exec_()
|
||||||
|
|
||||||
|
def show_backtesting_orders(self):
|
||||||
|
""""""
|
||||||
|
if not self.order_dialog.is_updated():
|
||||||
|
orders = self.backtester_engine.get_all_orders()
|
||||||
|
self.order_dialog.update_data(orders)
|
||||||
|
|
||||||
|
self.order_dialog.exec_()
|
||||||
|
|
||||||
|
def show_daily_results(self):
|
||||||
|
""""""
|
||||||
|
if not self.daily_dialog.is_updated():
|
||||||
|
results = self.backtester_engine.get_all_daily_results()
|
||||||
|
self.daily_dialog.update_data(results)
|
||||||
|
|
||||||
|
self.daily_dialog.exec_()
|
||||||
|
|
||||||
def show(self):
|
def show(self):
|
||||||
""""""
|
""""""
|
||||||
self.showMaximized()
|
self.showMaximized()
|
||||||
@ -750,3 +825,117 @@ class OptimizationResultMonitor(QtWidgets.QDialog):
|
|||||||
vbox.addWidget(table)
|
vbox.addWidget(table)
|
||||||
|
|
||||||
self.setLayout(vbox)
|
self.setLayout(vbox)
|
||||||
|
|
||||||
|
|
||||||
|
class BacktestingTradeMonitor(BaseMonitor):
|
||||||
|
"""
|
||||||
|
Monitor for backtesting trade data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"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},
|
||||||
|
"volume": {"display": "数量", "cell": BaseCell, "update": False},
|
||||||
|
"datetime": {"display": "时间", "cell": BaseCell, "update": False},
|
||||||
|
"gateway_name": {"display": "接口", "cell": BaseCell, "update": False},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class BacktestingOrderMonitor(BaseMonitor):
|
||||||
|
"""
|
||||||
|
Monitor for backtesting order data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"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": False},
|
||||||
|
"traded": {"display": "已成交", "cell": BaseCell, "update": False},
|
||||||
|
"status": {"display": "状态", "cell": EnumCell, "update": False},
|
||||||
|
"datetime": {"display": "时间", "cell": BaseCell, "update": False},
|
||||||
|
"gateway_name": {"display": "接口", "cell": BaseCell, "update": False},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class DailyResultMonitor(BaseMonitor):
|
||||||
|
"""
|
||||||
|
Monitor for backtesting daily result.
|
||||||
|
"""
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"date": {"display": "日期", "cell": BaseCell, "update": False},
|
||||||
|
"trade_count": {"display": "成交笔数", "cell": BaseCell, "update": False},
|
||||||
|
"start_pos": {"display": "开盘持仓", "cell": BaseCell, "update": False},
|
||||||
|
"end_pos": {"display": "收盘持仓", "cell": BaseCell, "update": False},
|
||||||
|
"turnover": {"display": "成交额", "cell": BaseCell, "update": False},
|
||||||
|
"commission": {"display": "手续费", "cell": BaseCell, "update": False},
|
||||||
|
"slippage": {"display": "滑点", "cell": BaseCell, "update": False},
|
||||||
|
"trading_pnl": {"display": "交易盈亏", "cell": BaseCell, "update": False},
|
||||||
|
"holding_pnl": {"display": "持仓盈亏", "cell": BaseCell, "update": False},
|
||||||
|
"total_pnl": {"display": "总盈亏", "cell": BaseCell, "update": False},
|
||||||
|
"net_pnl": {"display": "净盈亏", "cell": BaseCell, "update": False},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class BacktestingResultDialog(QtWidgets.QDialog):
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
main_engine: MainEngine,
|
||||||
|
event_engine: EventEngine,
|
||||||
|
title: str,
|
||||||
|
table_class: QtWidgets.QTableWidget
|
||||||
|
):
|
||||||
|
""""""
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.main_engine = main_engine
|
||||||
|
self.event_engine = event_engine
|
||||||
|
self.title = title
|
||||||
|
self.table_class = table_class
|
||||||
|
|
||||||
|
self.updated = False
|
||||||
|
|
||||||
|
self.init_ui()
|
||||||
|
|
||||||
|
def init_ui(self):
|
||||||
|
""""""
|
||||||
|
self.setWindowTitle(self.title)
|
||||||
|
self.resize(1100, 600)
|
||||||
|
|
||||||
|
self.table = self.table_class(self.main_engine, self.event_engine)
|
||||||
|
|
||||||
|
vbox = QtWidgets.QVBoxLayout()
|
||||||
|
vbox.addWidget(self.table)
|
||||||
|
|
||||||
|
self.setLayout(vbox)
|
||||||
|
|
||||||
|
def clear_data(self):
|
||||||
|
""""""
|
||||||
|
self.updated = False
|
||||||
|
self.table.setRowCount(0)
|
||||||
|
|
||||||
|
def update_data(self, data: list):
|
||||||
|
""""""
|
||||||
|
self.updated = True
|
||||||
|
|
||||||
|
data.reverse()
|
||||||
|
for obj in data:
|
||||||
|
self.table.insert_new_row(obj)
|
||||||
|
|
||||||
|
def is_updated(self):
|
||||||
|
""""""
|
||||||
|
return self.updated
|
||||||
|
|
||||||
|
@ -825,6 +825,7 @@ class BacktestingEngine:
|
|||||||
status=Status.ALLTRADED,
|
status=Status.ALLTRADED,
|
||||||
gateway_name=self.gateway_name,
|
gateway_name=self.gateway_name,
|
||||||
)
|
)
|
||||||
|
order.datetime = self.datetime
|
||||||
|
|
||||||
self.limit_orders[order.vt_orderid] = order
|
self.limit_orders[order.vt_orderid] = order
|
||||||
|
|
||||||
@ -943,6 +944,7 @@ class BacktestingEngine:
|
|||||||
status=Status.NOTTRADED,
|
status=Status.NOTTRADED,
|
||||||
gateway_name=self.gateway_name,
|
gateway_name=self.gateway_name,
|
||||||
)
|
)
|
||||||
|
order.datetime = self.datetime
|
||||||
|
|
||||||
self.active_limit_orders[order.vt_orderid] = order
|
self.active_limit_orders[order.vt_orderid] = order
|
||||||
self.limit_orders[order.vt_orderid] = order
|
self.limit_orders[order.vt_orderid] = order
|
||||||
@ -1023,14 +1025,19 @@ class BacktestingEngine:
|
|||||||
"""
|
"""
|
||||||
Return all trade data of current backtesting result.
|
Return all trade data of current backtesting result.
|
||||||
"""
|
"""
|
||||||
return self.trades.values()
|
return list(self.trades.values())
|
||||||
|
|
||||||
def get_all_orders(self):
|
def get_all_orders(self):
|
||||||
"""
|
"""
|
||||||
Return all limit order data of current backtesting result.
|
Return all limit order data of current backtesting result.
|
||||||
"""
|
"""
|
||||||
return self.limit_orders.values()
|
return list(self.limit_orders.values())
|
||||||
|
|
||||||
|
def get_all_daily_results(self):
|
||||||
|
"""
|
||||||
|
Return all daily result data.
|
||||||
|
"""
|
||||||
|
return list(self.daily_results.values())
|
||||||
|
|
||||||
class DailyResult:
|
class DailyResult:
|
||||||
""""""
|
""""""
|
||||||
|
@ -237,6 +237,7 @@ class BaseMonitor(QtWidgets.QTableWidget):
|
|||||||
"""
|
"""
|
||||||
Register event handler into event engine.
|
Register event handler into event engine.
|
||||||
"""
|
"""
|
||||||
|
if self.event_type:
|
||||||
self.signal.connect(self.process_event)
|
self.signal.connect(self.process_event)
|
||||||
self.event_engine.register(self.event_type, self.signal.emit)
|
self.event_engine.register(self.event_type, self.signal.emit)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user