Merge pull request #1762 from vnpy/backtester-result

Backtester result
This commit is contained in:
vn.py 2019-05-30 14:01:51 +08:00 committed by GitHub
commit d7577f6b54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 208 additions and 7 deletions

View File

@ -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()

View File

@ -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("请点击[优化结果]按钮查看")
@ -220,6 +263,14 @@ class BacktesterManager(QtWidgets.QWidget):
if result: if result:
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):
"""""" """"""
@ -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

View File

@ -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:
"""""" """"""

View File

@ -237,8 +237,9 @@ class BaseMonitor(QtWidgets.QTableWidget):
""" """
Register event handler into event engine. Register event handler into event engine.
""" """
self.signal.connect(self.process_event) if self.event_type:
self.event_engine.register(self.event_type, self.signal.emit) self.signal.connect(self.process_event)
self.event_engine.register(self.event_type, self.signal.emit)
def process_event(self, event): def process_event(self, event):
""" """