diff --git a/tests/backtesting/turtle.ipynb b/tests/backtesting/turtle.ipynb index 9c3883c4..9807424b 100644 --- a/tests/backtesting/turtle.ipynb +++ b/tests/backtesting/turtle.ipynb @@ -7,7 +7,7 @@ "outputs": [], "source": [ "#%%\n", - "from vnpy.app.cta_strategy.backtesting import BacktestingEngine\n", + "from vnpy.app.cta_strategy.backtesting import BacktestingEngine, OptimizationSetting\n", "from vnpy.app.cta_strategy.strategies.atr_rsi_strategy import (\n", " AtrRsiStrategy,\n", ")\n", @@ -16,95 +16,35 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "#%%\n", "engine = BacktestingEngine()\n", "engine.set_parameters(\n", - " vt_symbol=\"XBTUSD.BITMEX\",\n", + " vt_symbol=\"IF88.CFFEX\",\n", " interval=\"1m\",\n", - " start=datetime(2013, 1, 1),\n", + " start=datetime(2019, 1, 1),\n", " end=datetime(2019, 4, 30),\n", - " rate=3.0/10000,\n", + " rate=0.3/10000,\n", " slippage=0.2,\n", " size=300,\n", " pricetick=0.2,\n", " capital=1_000_000,\n", - ")" + ")\n", + "engine.add_strategy(AtrRsiStrategy, {})" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": { "scrolled": false }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2019-03-27 11:54:16.262535\t开始加载历史数据\n", - "2019-03-27 11:54:30.753671\t历史数据加载完成,数据量:147566\n", - "2019-03-27 11:54:31.390710\t策略初始化完成\n", - "2019-03-27 11:54:31.390710\t开始回放历史数据\n", - "2019-03-27 11:54:39.884536\t历史数据回放结束\n", - "2019-03-27 11:54:39.884536\t开始计算逐日盯市盈亏\n", - "2019-03-27 11:54:39.900121\t逐日盯市盈亏计算完成\n", - "2019-03-27 11:54:39.900121\t开始计算策略统计指标\n", - "2019-03-27 11:54:39.915706\t------------------------------\n", - "2019-03-27 11:54:39.915706\t首个交易日:\t2018-01-11\n", - "2019-03-27 11:54:39.915706\t最后交易日:\t2019-02-15\n", - "2019-03-27 11:54:39.915706\t总交易日:\t94\n", - "2019-03-27 11:54:39.915706\t盈利交易日:\t27\n", - "2019-03-27 11:54:39.915706\t亏损交易日:\t67\n", - "2019-03-27 11:54:39.915706\t起始资金:\t1,000,000.00\n", - "2019-03-27 11:54:39.915706\t结束资金:\t-6,174,412.20\n", - "2019-03-27 11:54:39.915706\t总收益率:\t-717.44%\n", - "2019-03-27 11:54:39.915706\t年化收益:\t-1,831.76%\n", - "2019-03-27 11:54:39.915706\t最大回撤: \t-8,415,878.66\n", - "2019-03-27 11:54:39.915706\t百分比最大回撤: -702.44%\n", - "2019-03-27 11:54:39.915706\t总盈亏:\t-7,174,412.20\n", - "2019-03-27 11:54:39.915706\t总手续费:\t6,900,212.20\n", - "2019-03-27 11:54:39.915706\t总滑点:\t477,180.00\n", - "2019-03-27 11:54:39.915706\t总成交金额:\t23,000,707,320.00\n", - "2019-03-27 11:54:39.915706\t总成交笔数:\t7953\n", - "2019-03-27 11:54:39.915706\t日均盈亏:\t-76,323.53\n", - "2019-03-27 11:54:39.915706\t日均手续费:\t73,406.51\n", - "2019-03-27 11:54:39.915706\t日均滑点:\t5,076.38\n", - "2019-03-27 11:54:39.915706\t日均成交金额:\t244,688,375.74\n", - "2019-03-27 11:54:39.915706\t日均成交笔数:\t84.6063829787234\n", - "2019-03-27 11:54:39.915706\t日均收益率:\t-0.52%\n", - "2019-03-27 11:54:39.915706\t收益标准差:\t29.62%\n", - "2019-03-27 11:54:39.915706\tSharpe Ratio:\t-0.27\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "C:\\Github\\vnpy\\vnpy\\app\\cta_strategy\\backtesting.py:331: RuntimeWarning: invalid value encountered in log\n", - " df[\"return\"] = np.log(df[\"balance\"] / df[\"balance\"].shift(1)).fillna(0)\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "#%%\n", - "engine.add_strategy(AtrRsiStrategy, {})\n", "engine.load_data()\n", "engine.run_backtesting()\n", "df = engine.calculate_result()\n", @@ -112,6 +52,119 @@ "engine.show_chart()" ] }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2019-04-15 22:19:49.696835\t参数:{'atr_length': 22}, 目标:121.19996051999999\n", + "2019-04-15 22:19:49.709531\t参数:{'atr_length': 23}, 目标:116.54901966000013\n", + "2019-04-15 22:19:49.710507\t参数:{'atr_length': 24}, 目标:113.29820520000014\n" + ] + }, + { + "data": { + "text/plain": [ + "[(\"{'atr_length': 22}\",\n", + " 121.19996051999999,\n", + " {'start_date': datetime.date(2013, 1, 18),\n", + " 'end_date': datetime.date(2019, 4, 11),\n", + " 'total_days': 1514,\n", + " 'profit_days': 763,\n", + " 'loss_days': 750,\n", + " 'capital': 1000000,\n", + " 'end_balance': 2211999.6052,\n", + " 'max_drawdown': -248787.6971999996,\n", + " 'max_ddpercent': -12.636908338002794,\n", + " 'total_net_pnl': 1211999.6052000003,\n", + " 'daily_net_pnl': 800.5281408190227,\n", + " 'total_commission': 242400.39479999998,\n", + " 'daily_commission': 160.10594108322323,\n", + " 'total_slippage': 481860.0,\n", + " 'daily_slippage': 318.2694848084544,\n", + " 'total_turnover': 8080013160.0,\n", + " 'daily_turnover': 5336864.702774108,\n", + " 'total_trade_count': 8031,\n", + " 'daily_trade_count': 5.30449141347424,\n", + " 'total_return': 121.19996051999999,\n", + " 'annual_return': 19.212675379656538,\n", + " 'daily_return': 0.052348808029058974,\n", + " 'return_std': 0.9487639654919149,\n", + " 'sharpe_ratio': 0.854779772691872,\n", + " 'return_drawdown_ratio': 9.590950355754112}),\n", + " (\"{'atr_length': 23}\",\n", + " 116.54901966000013,\n", + " {'start_date': datetime.date(2013, 1, 18),\n", + " 'end_date': datetime.date(2019, 4, 11),\n", + " 'total_days': 1514,\n", + " 'profit_days': 759,\n", + " 'loss_days': 754,\n", + " 'capital': 1000000,\n", + " 'end_balance': 2165490.1966000013,\n", + " 'max_drawdown': -232904.1239999996,\n", + " 'max_ddpercent': -13.536251422505968,\n", + " 'total_net_pnl': 1165490.1966000004,\n", + " 'daily_net_pnl': 769.8085842800531,\n", + " 'total_commission': 242769.80339999998,\n", + " 'daily_commission': 160.34993619550858,\n", + " 'total_slippage': 482700.0,\n", + " 'daily_slippage': 318.82430647291943,\n", + " 'total_turnover': 8092326780.0,\n", + " 'daily_turnover': 5344997.873183619,\n", + " 'total_trade_count': 8045,\n", + " 'daily_trade_count': 5.313738441215324,\n", + " 'total_return': 116.54901966000013,\n", + " 'annual_return': 18.475406022721288,\n", + " 'daily_return': 0.0509452313711608,\n", + " 'return_std': 0.961380153488665,\n", + " 'sharpe_ratio': 0.8209448965768181,\n", + " 'return_drawdown_ratio': 8.610139987960078}),\n", + " (\"{'atr_length': 24}\",\n", + " 113.29820520000014,\n", + " {'start_date': datetime.date(2013, 1, 18),\n", + " 'end_date': datetime.date(2019, 4, 11),\n", + " 'total_days': 1514,\n", + " 'profit_days': 760,\n", + " 'loss_days': 753,\n", + " 'capital': 1000000,\n", + " 'end_balance': 2132982.0520000015,\n", + " 'max_drawdown': -236503.9475999996,\n", + " 'max_ddpercent': -13.23872340727957,\n", + " 'total_net_pnl': 1132982.0520000013,\n", + " 'daily_net_pnl': 748.3368903566719,\n", + " 'total_commission': 242817.948,\n", + " 'daily_commission': 160.3817357992074,\n", + " 'total_slippage': 482700.0,\n", + " 'daily_slippage': 318.82430647291943,\n", + " 'total_turnover': 8093931600.0,\n", + " 'daily_turnover': 5346057.85997358,\n", + " 'total_trade_count': 8045,\n", + " 'daily_trade_count': 5.313738441215324,\n", + " 'total_return': 113.29820520000014,\n", + " 'annual_return': 17.96008536856013,\n", + " 'daily_return': 0.049946173936258026,\n", + " 'return_std': 0.959328411709829,\n", + " 'sharpe_ratio': 0.8065671672003681,\n", + " 'return_drawdown_ratio': 8.558091419728651})]" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "setting = OptimizationSetting()\n", + "setting.set_target(\"total_return\")\n", + "setting.add_parameter(\"atr_length\", 22, 24, 1)\n", + "\n", + "engine.run_optimization(setting)" + ] + }, { "cell_type": "code", "execution_count": null, diff --git a/vnpy/app/cta_backtester/engine.py b/vnpy/app/cta_backtester/engine.py index 4dc80589..40c45f87 100644 --- a/vnpy/app/cta_backtester/engine.py +++ b/vnpy/app/cta_backtester/engine.py @@ -9,7 +9,8 @@ from vnpy.event import Event, EventEngine from vnpy.trader.engine import BaseEngine, MainEngine from vnpy.app.cta_strategy import ( CtaTemplate, - BacktestingEngine + BacktestingEngine, + OptimizationSetting ) @@ -33,9 +34,13 @@ class BacktesterEngine(BaseEngine): self.backtesting_engine = None self.thread = None + # Backtesting reuslt self.result_df = None self.result_statistics = None + # Optimization result + self.result_values = None + self.load_strategy_class() def init_engine(self): @@ -162,7 +167,7 @@ class BacktesterEngine(BaseEngine): setting: dict ): if self.thread: - self.write_log("已有回测在运行中,请等待完成") + self.write_log("已有回测或者优化在运行中,请等待完成") return False self.write_log("-" * 40) @@ -194,7 +199,102 @@ class BacktesterEngine(BaseEngine): """""" return self.result_statistics + def get_result_values(self): + """""" + return self.result_values + def get_default_setting(self, class_name: str): """""" strategy_class = self.classes[class_name] return strategy_class.get_class_parameters() + + def run_optimization( + self, + class_name: str, + vt_symbol: str, + interval: str, + start: datetime, + end: datetime, + rate: float, + slippage: float, + size: int, + pricetick: float, + capital: int, + optimization_setting: OptimizationSetting): + """""" + self.write_log("开始多进程参数优化") + + self.result_values = None + + engine = self.backtesting_engine + engine.clear_data() + + engine.set_parameters( + vt_symbol=vt_symbol, + interval=interval, + start=start, + end=end, + rate=rate, + slippage=slippage, + size=size, + pricetick=pricetick, + capital=capital + ) + + strategy_class = self.classes[class_name] + engine.add_strategy( + strategy_class, + {} + ) + + self.result_values = engine.run_optimization( + optimization_setting, + output=False + ) + + # Clear thread object handler. + self.thread = None + self.write_log("多进程参数优化完成") + + # Put optimization done event + event = Event(EVENT_BACKTESTER_OPTIMIZATION_FINISHED) + self.event_engine.put(event) + + def start_optimization( + self, + class_name: str, + vt_symbol: str, + interval: str, + start: datetime, + end: datetime, + rate: float, + slippage: float, + size: int, + pricetick: float, + capital: int, + optimization_setting: OptimizationSetting + ): + if self.thread: + self.write_log("已有回测或者优化在运行中,请等待完成") + return False + + self.write_log("-" * 40) + self.thread = Thread( + target=self.run_optimization, + args=( + class_name, + vt_symbol, + interval, + start, + end, + rate, + slippage, + size, + pricetick, + capital, + optimization_setting + ) + ) + self.thread.start() + + return True diff --git a/vnpy/app/cta_backtester/ui/widget.py b/vnpy/app/cta_backtester/ui/widget.py index d8cde991..bbed6991 100644 --- a/vnpy/app/cta_backtester/ui/widget.py +++ b/vnpy/app/cta_backtester/ui/widget.py @@ -1,19 +1,18 @@ -from datetime import datetime, timedelta - -import pyqtgraph as pg import numpy as np - -from vnpy.event import Event, EventEngine -from vnpy.trader.ui import QtCore, QtWidgets, QtGui -from vnpy.trader.engine import MainEngine -from vnpy.trader.constant import Interval +import pyqtgraph as pg +from datetime import datetime, timedelta from ..engine import ( APP_NAME, EVENT_BACKTESTER_LOG, EVENT_BACKTESTER_BACKTESTING_FINISHED, - EVENT_BACKTESTER_OPTIMIZATION_FINISHED + EVENT_BACKTESTER_OPTIMIZATION_FINISHED, + OptimizationSetting ) +from vnpy.trader.constant import Interval +from vnpy.trader.engine import MainEngine +from vnpy.trader.ui import QtCore, QtWidgets, QtGui +from vnpy.event import Event, EventEngine class BacktesterManager(QtWidgets.QWidget): @@ -34,6 +33,8 @@ class BacktesterManager(QtWidgets.QWidget): self.class_names = [] self.settings = {} + self.target_display = "" + self.init_strategy_settings() self.init_ui() self.register_event() @@ -81,8 +82,15 @@ class BacktesterManager(QtWidgets.QWidget): self.pricetick_line = QtWidgets.QLineEdit("0.2") self.capital_line = QtWidgets.QLineEdit("1000000") - start_button = QtWidgets.QPushButton("开始回测") - start_button.clicked.connect(self.start_backtesting) + backtesting_button = QtWidgets.QPushButton("开始回测") + backtesting_button.clicked.connect(self.start_backtesting) + + optimization_button = QtWidgets.QPushButton("参数优化") + optimization_button.clicked.connect(self.start_optimization) + + self.result_button = QtWidgets.QPushButton("优化结果") + self.result_button.clicked.connect(self.show_optimization_result) + self.result_button.setEnabled(False) form = QtWidgets.QFormLayout() form.addRow("交易策略", self.class_combo) @@ -95,7 +103,13 @@ class BacktesterManager(QtWidgets.QWidget): form.addRow("合约乘数", self.size_line) form.addRow("价格跳动", self.pricetick_line) form.addRow("回测资金", self.capital_line) - form.addRow(start_button) + form.addRow(backtesting_button) + + left_vbox = QtWidgets.QVBoxLayout() + left_vbox.addLayout(form) + left_vbox.addStretch() + left_vbox.addWidget(optimization_button) + left_vbox.addWidget(self.result_button) # Result part self.statistics_monitor = StatisticsMonitor() @@ -112,7 +126,7 @@ class BacktesterManager(QtWidgets.QWidget): vbox.addWidget(self.log_monitor) hbox = QtWidgets.QHBoxLayout() - hbox.addLayout(form) + hbox.addLayout(left_vbox) hbox.addLayout(vbox) hbox.addWidget(self.chart) self.setLayout(hbox) @@ -134,6 +148,10 @@ class BacktesterManager(QtWidgets.QWidget): def process_log_event(self, event: Event): """""" msg = event.data + self.write_log(msg) + + def write_log(self, msg): + """""" timestamp = datetime.now().strftime("%H:%M:%S") msg = f"{timestamp}\t{msg}" self.log_monitor.append(msg) @@ -148,7 +166,8 @@ class BacktesterManager(QtWidgets.QWidget): def process_optimization_finished_event(self, event: Event): """""" - pass + self.write_log("请点击[优化结果]按钮查看") + self.result_button.setEnabled(True) def start_backtesting(self): """""" @@ -164,7 +183,7 @@ class BacktesterManager(QtWidgets.QWidget): capital = float(self.capital_line.text()) old_setting = self.settings[class_name] - dialog = SettingEditor(class_name, old_setting) + dialog = BacktestingSettingEditor(class_name, old_setting) i = dialog.exec() if i != dialog.Accepted: return @@ -190,6 +209,54 @@ class BacktesterManager(QtWidgets.QWidget): self.statistics_monitor.clear_data() self.chart.clear_data() + def start_optimization(self): + """""" + class_name = self.class_combo.currentText() + vt_symbol = self.symbol_line.text() + interval = self.interval_combo.currentText() + start = self.start_date_edit.date().toPyDate() + end = self.end_date_edit.date().toPyDate() + rate = float(self.rate_line.text()) + slippage = float(self.slippage_line.text()) + size = float(self.size_line.text()) + pricetick = float(self.pricetick_line.text()) + capital = float(self.capital_line.text()) + + parameters = self.settings[class_name] + dialog = OptimizationSettingEditor(class_name, parameters) + i = dialog.exec() + if i != dialog.Accepted: + return + + optimization_setting = dialog.get_setting() + self.target_display = dialog.target_display + + self.backtester_engine.start_optimization( + class_name, + vt_symbol, + interval, + start, + end, + rate, + slippage, + size, + pricetick, + capital, + optimization_setting + ) + + self.result_button.setEnabled(False) + + def show_optimization_result(self): + """""" + result_values = self.backtester_engine.get_result_values() + + dialog = OptimizationResultMonitor( + result_values, + self.target_display + ) + dialog.exec_() + def show(self): """""" self.showMaximized() @@ -286,7 +353,7 @@ class StatisticsMonitor(QtWidgets.QTableWidget): cell.setText(str(value)) -class SettingEditor(QtWidgets.QDialog): +class BacktestingSettingEditor(QtWidgets.QDialog): """ For creating new strategy and editing strategy parameters. """ @@ -295,7 +362,7 @@ class SettingEditor(QtWidgets.QDialog): self, class_name: str, parameters: dict ): """""" - super(SettingEditor, self).__init__() + super(BacktestingSettingEditor, self).__init__() self.class_name = class_name self.parameters = parameters @@ -474,3 +541,164 @@ class DateAxis(pg.AxisItem): dt = self.dates.get(v, "") strings.append(str(dt)) return strings + + +class OptimizationSettingEditor(QtWidgets.QDialog): + """ + For setting up parameters for optimization. + """ + DISPLAY_NAME_MAP = { + "总收益率": "total_return", + "夏普比率": "sharpe_ratio", + "收益回撤比": "return_drawdown_ratio", + "日均盈亏": "daily_net_pnl" + } + + def __init__( + self, class_name: str, parameters: dict + ): + """""" + super().__init__() + + self.class_name = class_name + self.parameters = parameters + self.edits = {} + + self.optimization_setting = None + + self.init_ui() + + def init_ui(self): + """""" + QLabel = QtWidgets.QLabel + + self.target_combo = QtWidgets.QComboBox() + self.target_combo.addItems(list(self.DISPLAY_NAME_MAP.keys())) + + grid = QtWidgets.QGridLayout() + grid.addWidget(QLabel("目标"), 0, 0) + grid.addWidget(self.target_combo, 0, 1, 1, 3) + grid.addWidget(QLabel("参数"), 1, 0) + grid.addWidget(QLabel("开始"), 1, 1) + grid.addWidget(QLabel("步进"), 1, 2) + grid.addWidget(QLabel("结束"), 1, 3) + + # Add vt_symbol and name edit if add new strategy + self.setWindowTitle(f"优化参数配置:{self.class_name}") + + validator = QtGui.QDoubleValidator() + row = 2 + + for name, value in self.parameters.items(): + type_ = type(value) + if type_ not in [int, float]: + continue + + start_edit = QtWidgets.QLineEdit(str(value)) + step_edit = QtWidgets.QLineEdit(str(1)) + end_edit = QtWidgets.QLineEdit(str(value)) + + for edit in [start_edit, step_edit, end_edit]: + edit.setValidator(validator) + + grid.addWidget(QLabel(name), row, 0) + grid.addWidget(start_edit, row, 1) + grid.addWidget(step_edit, row, 2) + grid.addWidget(end_edit, row, 3) + + self.edits[name] = { + "type": type_, + "start": start_edit, + "step": step_edit, + "end": end_edit + } + + row += 1 + + button = QtWidgets.QPushButton("确定") + button.clicked.connect(self.generate_setting) + grid.addWidget(button, row, 0, 1, 4) + + self.setLayout(grid) + + def generate_setting(self): + """""" + self.optimization_setting = OptimizationSetting() + + self.target_display = self.target_combo.currentText() + target_name = self.DISPLAY_NAME_MAP[self.target_display] + self.optimization_setting.set_target(target_name) + + for name, d in self.edits.items(): + type_ = d["type"] + start_value = type_(d["start"].text()) + step_value = type_(d["step"].text()) + end_value = type_(d["end"].text()) + + if start_value == end_value: + self.optimization_setting.add_parameter(name, start_value) + else: + self.optimization_setting.add_parameter( + name, + start_value, + end_value, + step_value + ) + + self.accept() + + def get_setting(self): + """""" + return self.optimization_setting + + +class OptimizationResultMonitor(QtWidgets.QDialog): + """ + For viewing optimization result. + """ + + def __init__( + self, result_values: list, target_display: str + ): + """""" + super().__init__() + + self.result_values = result_values + self.target_display = target_display + + self.init_ui() + + def init_ui(self): + """""" + self.setWindowTitle("参数优化结果") + self.resize(1100, 500) + + table = QtWidgets.QTableWidget() + + table.setColumnCount(2) + table.setRowCount(len(self.result_values)) + table.setHorizontalHeaderLabels(["参数", self.target_display]) + table.verticalHeader().setVisible(False) + + table.horizontalHeader().setSectionResizeMode( + 0, QtWidgets.QHeaderView.ResizeToContents + ) + table.horizontalHeader().setSectionResizeMode( + 1, QtWidgets.QHeaderView.Stretch + ) + + for n, tp in enumerate(self.result_values): + setting, target_value, _ = tp + setting_cell = QtWidgets.QTableWidgetItem(str(setting)) + target_cell = QtWidgets.QTableWidgetItem(str(target_value)) + + setting_cell.setTextAlignment(QtCore.Qt.AlignCenter) + target_cell.setTextAlignment(QtCore.Qt.AlignCenter) + + table.setItem(n, 0, setting_cell) + table.setItem(n, 1, target_cell) + + vbox = QtWidgets.QVBoxLayout() + vbox.addWidget(table) + + self.setLayout(vbox) diff --git a/vnpy/app/cta_strategy/backtesting.py b/vnpy/app/cta_strategy/backtesting.py index c1ba8e67..aeb5ddc1 100644 --- a/vnpy/app/cta_strategy/backtesting.py +++ b/vnpy/app/cta_strategy/backtesting.py @@ -460,7 +460,7 @@ class BacktestingEngine: plt.show() - def run_optimization(self, optimization_setting: OptimizationSetting): + def run_optimization(self, optimization_setting: OptimizationSetting, output=True): """""" # Get optimization setting and target settings = optimization_setting.generate_setting() @@ -503,9 +503,10 @@ class BacktestingEngine: result_values = [result.get() for result in results] result_values.sort(reverse=True, key=lambda result: result[1]) - for value in result_values: - msg = f"参数:{value[0]}, 目标:{value[1]}" - self.output(msg) + if output: + for value in result_values: + msg = f"参数:{value[0]}, 目标:{value[1]}" + self.output(msg) return result_values @@ -957,7 +958,7 @@ def optimize( engine.load_data() engine.run_backtesting() engine.calculate_result() - statistics = engine.calculate_statistics() + statistics = engine.calculate_statistics(output=False) target_value = statistics[target_name] return (str(setting), target_value, statistics)