Files
NewQuant/grid_search.ipynb
2025-07-01 10:14:28 +08:00

300 lines
59 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "782ec73f",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"初始化数据管理器...\n",
"数据加载成功: /mnt/d/PyProject/NewQuant/data/data/KQ_m@SHFE_rb/KQ_m@SHFE_rb_min60.csv\n",
"数据范围从 2021-12-31 14:00:00 到 2025-06-20 21:00:00\n",
"总计 5811 条记录。\n",
"\n",
"--- 开始网格搜索回测 ---\n",
"总计 1 种参数组合需要回测。\n"
]
}
],
"source": [
"from datetime import datetime\n",
"import itertools\n",
"import pandas as pd # 导入pandas用于结果存储和展示\n",
"\n",
"# 导入所有必要的模块\n",
"from src.analysis.grid_search_analyzer import GridSearchAnalyzer\n",
"from src.analysis.result_analyzer import ResultAnalyzer\n",
"from src.common_utils import generate_parameter_range\n",
"from src.data_manager import DataManager\n",
"from src.backtest_engine import BacktestEngine\n",
"from src.strategies.SimpleLimitBuyStrategy import SimpleLimitBuyStrategyShort, SimpleLimitBuyStrategyLong\n",
"\n",
"# 确保 autoreload 启用\n",
"%load_ext autoreload\n",
"%autoreload 2\n",
"\n",
"# --- 全局配置 ---\n",
"data_file_path = \"/mnt/d/PyProject/NewQuant/data/data/KQ_m@SHFE_rb/KQ_m@SHFE_rb_min60.csv\"\n",
"initial_capital = 100000.0\n",
"slippage_rate = 0.0001\n",
"commission_rate = 0.0002\n",
"global_config = {\n",
" 'symbol': 'KQ_m@SHFE_rb',\n",
"}\n",
"# --- 定义参数网格 ---\n",
"# 为策略参数定义一个字典,每个键对应一个参数,值是一个列表,包含所有要尝试的可能值\n",
"# 这些参数名必须与 SimpleLimitBuyStrategyShort 的 __init__ 方法中接收的参数名一致\n",
"param1_name = \"open_range_factor_1_ago\"\n",
"param1_values = generate_parameter_range(start=-2, end=-2, step=0.1)\n",
"param2_name = \"open_range_factor_7_ago\"\n",
"param2_values = generate_parameter_range(start=-2, end=-2, step=0.1)\n",
"optimization_metric = 'sharpe_ratio'\n",
"# 存储所有回测的结果\n",
"all_results = []\n",
"grid_results = []\n",
"\n",
"# --- 1. 初始化数据管理器 (只需要一次) ---\n",
"print(\"初始化数据管理器...\")\n",
"data_manager = DataManager(file_path=data_file_path, symbol=global_config['symbol'])\n",
"# --- 2. 遍历参数组合并运行回测 ---\n",
"\n",
"print(\"\\n--- 开始网格搜索回测 ---\")\n",
"\n",
"current_combination_idx = 0\n",
"param_combinations = list(itertools.product(param1_values, param2_values))\n",
"total_combinations = len(param_combinations)\n",
"print(f\"总计 {total_combinations} 种参数组合需要回测。\")\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "db55a49d",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"--- 正在运行组合 1/1: {'trade_volume': 1, 'open_range_factor_1_ago': -2, 'open_range_factor_7_ago': -2, 'max_position': 10, 'enable_log': False} ---\n",
"DataManager 已重置。\n",
"模拟器初始化:初始资金=100000.00, 滑点率=0.0001, 佣金率=0.0002\n",
"\n",
"--- 回测引擎初始化完成 ---\n",
" 策略: SimpleLimitBuyStrategyShort\n",
" 初始资金: 100000.00\n",
" 换月模式: 启用\n",
"\n",
"--- 回测开始 ---\n",
"SimpleLimitBuyStrategyShort 策略初始化回调被调用。\n",
"开始将 DataFrame 转换为 Bar 对象流...\n",
"到达结束时间 2025-01-01 00:00:00回测终止。\n",
"\n",
"--- 回测结束,检查并平仓所有剩余持仓 ---\n",
"--- 回测结束 ---\n",
"总计处理了 5044 根K线。\n",
"总计发生了 586 笔交易。\n",
"最终总净值: -79.78\n",
"总收益率: -100.08%\n",
"\n",
"--- 结果分析器初始化完成 ---\n",
"正在计算绩效指标...\n",
"total_return: -1.000797761896799, annualized_return:-9999999, 252 / total_days:0.23076923076923078\n",
"绩效指标计算完成。\n",
"{'初始资金': 100000.0, '最终资金': np.float64(-79.77618967989383), '总收益率': np.float64(-1.000797761896799), '年化收益率': -9999999, '最大回撤': np.float64(1.000797761896799), '夏普比率': -10, '卡玛比率': np.float64(-9992027.740996474), '总交易次数': 586, '交易成本': 555.88858968, '总实现盈亏': -49761.943799999965, '胜率': 0.0, '盈亏比': 0.0, '盈利交易次数': 0, '亏损交易次数': 293, '平均每次盈利': 0.0, '平均每次亏损': -169.83598566552888, 'initial_capital': 100000.0, 'final_capital': np.float64(-79.77618967989383), 'total_return': np.float64(-1.000797761896799), 'annualized_return': -9999999, 'max_drawdown': np.float64(1.000797761896799), 'sharpe_ratio': -10, 'calmar_ratio': np.float64(-9992027.740996474), 'total_trades': 586, 'transaction_costs': 555.88858968, 'total_realized_pnl': -49761.943799999965, 'win_rate': 0.0, 'profit_loss_ratio': 0.0, 'winning_trades_count': 0, 'losing_trades_count': 293, 'avg_profit_per_trade': 0.0, 'avg_loss_per_trade': -169.83598566552888}\n",
"\n",
"--- 网格搜索回测完毕 ---\n"
]
}
],
"source": [
"\n",
"# --- 4. 执行网格搜索循环 ---\n",
"for idx, (p1_value, p2_value) in enumerate(param_combinations):\n",
" current_combination_idx += 1\n",
" \n",
" # 在网格搜索时通常关闭策略内部的详细日志,只保留关键信息\n",
" strategy_parameters = {\n",
" # 'symbol': \"SHFE_rb2501\", # 根据您的数据文件中的品种名称调整\n",
" 'trade_volume': 1,\n",
" 'open_range_factor_1_ago': p1_value, # 示例值,需要通过网格搜索优化\n",
" 'open_range_factor_7_ago': p2_value, # 示例值\n",
" 'max_position': 10,\n",
" 'enable_log': False\n",
" }\n",
" \n",
" print(f\"\\n--- 正在运行组合 {current_combination_idx}/{total_combinations}: {strategy_parameters} ---\")\n",
" # 每次回测前,需要重置数据管理器和模拟器状态\n",
" data_manager.reset() # 确保每次回测都从数据的起点开始\n",
" # 注意BacktestEngine 的 __init__ 会创建新的 ExecutionSimulator 实例,所以不用单独重置 simulator\n",
" # 初始化回测引擎\n",
" engine = BacktestEngine(\n",
" data_manager=data_manager,\n",
" strategy_class=SimpleLimitBuyStrategyShort,\n",
" strategy_params=strategy_parameters,\n",
" initial_capital=initial_capital,\n",
" slippage_rate=slippage_rate,\n",
" commission_rate=commission_rate,\n",
" roll_over_mode=True, # 保持换月模式\n",
" start_time=datetime(2022, 1, 1),\n",
" end_time=datetime(2025, 1, 1)\n",
" )\n",
" # 运行回测\n",
" engine.run_backtest()\n",
" # --- 3. 获取回测结果并分析 ---\n",
" results = engine.get_backtest_results()\n",
" portfolio_snapshots = results[\"portfolio_snapshots\"]\n",
" trade_history = results[\"trade_history\"]\n",
" initial_capital_result = results[\"initial_capital\"]\n",
" bars = results[\"all_bars\"]\n",
" if portfolio_snapshots:\n",
" analyzer = ResultAnalyzer(portfolio_snapshots, trade_history, bars, initial_capital_result)\n",
" metrics = analyzer.calculate_all_metrics()\n",
"\n",
" print(metrics)\n",
" \n",
" # 将当前组合的参数和性能指标存储起来\n",
" result_entry = {**strategy_parameters, **metrics}\n",
" all_results.append(result_entry)\n",
" grid_results.append(\n",
" {\n",
" param1_name: p1_value,\n",
" param2_name: p2_value,\n",
" optimization_metric: metrics.get(optimization_metric, 0.0),\n",
" }\n",
" )\n",
" \n",
" # # 可以选择性地打印每个组合的关键指标\n",
" # print(f\" 组合 {current_combination_idx} 结果:\")\n",
" # print(f\" 总收益率: {metrics.get('total_return', 0):.2f}%\")\n",
" # # print(f\" 年化收益率: {metrics.get('annualized_return', 0):.2f}%\")\n",
" # print(f\" 夏普比率: {metrics.get('sharpe_ratio', 0):.2f}\")\n",
" # print(f\" 最大回撤: {metrics.get('max_drawdown', 0):.2f}%\")\n",
" # else:\n",
" # print(f\" 组合 {strategy_parameters} 没有生成投资组合快照,无法进行结果分析。\")\n",
" # result_entry = {**strategy_parameters, \"total_return_percentage\": 0, \"annualized_return\": 0, \"sharpe_ratio\": 0, \"max_drawdown\": 0} # 记录失败组合\n",
" # all_results.append(result_entry) # 依然加入结果列表,以便追踪\n",
"print(\"\\n--- 网格搜索回测完毕 ---\")\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "f1d9c77a",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"--- 所有回测结果汇总 ---\n",
" trade_volume open_range_factor_1_ago open_range_factor_7_ago \\\n",
"0 1 -2 -2 \n",
"\n",
" max_position enable_log 初始资金 最终资金 总收益率 年化收益率 最大回撤 ... \\\n",
"0 10 False 100000.0 -79.78 -1.0 -9999999 1.0 ... \n",
"\n",
" calmar_ratio total_trades transaction_costs total_realized_pnl \\\n",
"0 -9992027.74 586 555.89 -49761.94 \n",
"\n",
" win_rate profit_loss_ratio winning_trades_count losing_trades_count \\\n",
"0 0.0 0.0 0 293 \n",
"\n",
" avg_profit_per_trade avg_loss_per_trade \n",
"0 0.0 -169.84 \n",
"\n",
"[1 rows x 37 columns]\n",
"\n",
"--- 动态网格搜索完成 ---\n",
"[{'open_range_factor_1_ago': -2, 'open_range_factor_7_ago': -2, 'sharpe_ratio': -10}]\n",
"\n",
"--- 最佳参数组合 ---\n",
" open_range_factor_1_ago: -2\n",
" open_range_factor_7_ago: -2\n",
" sharpe_ratio: -10.0000\n",
"[-2, -2, -2, -2]\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"/mnt/d/PyProject/NewQuant/src/analysis/grid_search_analyzer.py:70: UserWarning: Attempting to set identical low and high xlims makes transformation singular; automatically expanding.\n",
" im = ax.imshow(heatmap_matrix, cmap='viridis', origin='lower',\n",
"/mnt/d/PyProject/NewQuant/src/analysis/grid_search_analyzer.py:70: UserWarning: Attempting to set identical low and high ylims makes transformation singular; automatically expanding.\n",
" im = ax.imshow(heatmap_matrix, cmap='viridis', origin='lower',\n"
]
},
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAA8cAAAMWCAYAAADcbfTQAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAAPYQAAD2EBqD+naQAAiEpJREFUeJzs3Xd4FNX+x/HPJqQRUigpIEgLEkI3FIMgKFVQRBFEpIqASJEiAoo0xYAo0psiYLtybehFQaoNEFQEFRFBgSAQOoQekpzfH9zszyUb2F2SbLjzfj3PPA87O3vmzOzsst98v3OOzRhjBAAAAACAhfl4uwMAAAAAAHgbwTEAAAAAwPIIjgEAAAAAlkdwDAAAAACwPIJjAAAAAIDlERwDAAAAACyP4BgAAAAAYHkExwAAAAAAyyM4BgAAAABYHsGxF4wZM0Y2m01Hjx71dlcshfN+fb788kvZbDZ9+eWX3u4KLGLSpEkqV66cfH19VaNGDW93B8BV7Ny5U82aNVNYWJhsNpuWLFni7S4BgNsIji3m888/15gxY7zdDeRjs2bN0sKFC73dDVjcihUr9PTTT+v222/XggUL9OKLL+b4Pt59911NmTIlx9t11aZNm/TEE08oPj5efn5+stlsXutLbrhw4YJGjBihMmXKqGDBgoqNjdVTTz3l7W4hl3Tt2lW//PKLxo8fr7feeku1atXK0fYPHDigMWPGaMuWLTnarqsy/8Ce3bJu3Tqv9AtAzirg7Q4gb33++eeaOXMmATKyNWvWLBUrVkzdunVzWH/HHXfo/Pnz8vf3907HYClr1qyRj4+P5s+fn2vX3Lvvvqtff/1VAwcOzJX2r+Xzzz/X66+/rmrVqqlcuXL6448/vNKP3DJs2DBNmzZNjz76qOrWrasdO3bo7bff1ssvv+ztriGHnT9/Xhs2bNCzzz6rfv365co+Dhw4oLFjx6pMmTJeqSR54IEHFBMTk2X9M888ozNnzqh27dp53icAOY/gGMhBxhhduHBBQUFB3u6KpJztj4+PjwIDA3OgV8gNZ8+eVXBwsLe7kWMOHz6soKCgG+6PMWlpacrIyHCp33369NGwYcMUFBSkfv36/c8Fx++9955atmyp+fPn29flRgXAjSgjI0Opqan/M9+pR44ckSSFh4d7tyMecPW7s1q1aqpWrZrDun379unvv//WY489dsN9VwFwjrJqLzp58qS6deum8PBwhYWFqXv37jp37lyW7d5++23Fx8crKChIRYoUUYcOHbRv3z6Hbb755hu1a9dON998swICAlSqVCkNGjRI58+ft2/TrVs3zZw5U5IcSoEkac+ePbLZbHr55Zc1c+ZMlStXTgULFlSzZs20b98+GWP0/PPPq2TJkgoKCtJ9992n48ePO/Thk08+UatWrVSiRAkFBASofPnyev7555Wenu6wXaNGjVSlShX9+OOPqlevnoKCglS2bFnNmTMny7EnJSXp999/d+l8Tp8+XZUrV1bBggVVuHBh1apVS++++65H533BggW66667FBkZqYCAAMXFxWn27NlZ2ipTpozuueceffHFF6pVq5aCgoI0d+5c+znu16+f3nnnHVWsWFGBgYGKj4/X119/naWd/fv369FHH1VUVJQCAgJUuXJlvfHGGy4dt6v9ceWYypQpo23btumrr76yXx+NGjWSlP09x++//779+ixWrJg6deqk/fv3X7WfP/zwg2w2mxYtWpTluS+++EI2m01Lly6VJJ0+fVoDBw5UmTJlFBAQoMjISDVt2lSbN292+/xIl4OuHj16KCoqSoGBgapevXqWfvzz8/Dqq6+qdOnSCgoKUsOGDfXrr79mafP333/Xgw8+qCJFiigwMFC1atXSp59+6rDNwoUL7aV3gwcPVkREhIKDg3X//ffbf1i6KrO877ffflPHjh1VuHBh1a9fX5L0888/q1u3bipXrpwCAwMVHR2tRx99VMeOHXPaxq5du675eTh//rwGDBigYsWKKSQkRK1bt9b+/ftls9myVKHkxLVss9m0YMECnT171n4dZpb6u/rZlKRly5apYcOGCgkJUWhoqGrXrm3/TmjUqJE+++wz7d27176PMmXK2F/r7nUyZcoUlS9fXgEBAfrtt99cOs6oqKgc+0Oaq9+/kuzf8UFBQapTp46++eYbNWrUyP5Zz+TKObgaHx8fGWMc1gUEBLh9bK58x3Tr1k2FChXSX3/9pebNmys4OFglSpTQuHHjsvQhIyNDU6ZMUeXKlRUYGKioqCj17t1bJ06ccNgu8/v022+/VZ06dRQYGKhy5crpzTffdPsY/vn/QeXKlRUQEKDly5dLkl5++WXVq1dPRYsWVVBQkOLj4/XBBx9k28aSJUtUpUoV++crs51/+vLLL1WrVi0FBgaq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rnHjxqZq1aoO13hGRoapV6+eqVChgn1d5o+3Jk2aOPyQGjRokPH19XX43F1L5rX88MMPZ3nO2fv8r3/9y0gyX3/9dZY2rvV5+PHHH40kM3DgQIftunXrliU4zslruWvXriY4ONil47vyOj558qQJCQkxdevWdbg2jTEO575Vq1amdOnSWdpz9zoJDQ01hw8fdvnYnLne4NiV79+LFy+aokWLmtq1azt8ny9cuNBIcvjcu3oOsnPp0iXTqVMn4+/vb4KDg8369evdPiZ3vmO6du1qJJn+/fvb12VkZJhWrVoZf39/c+TIEWOMMd98842RZN555x2HfS1fvjzL+szv039+bg4fPmwCAgLMkCFD3DoWScbHx8ds27Yty3NXvnepqammSpUq5q677srShr+/v9m1a5d93datW40kM336dPu6e++91xQsWNDs37/fvm7nzp2mQIECDteYO78xriXzszBp0qSrHpsxxmzYsMFIcghKR40aZSQ5/EExU+Zn9vvvvzeSzIIFCxye9+Q6GT58uMvHlp1ff/3V6R8TXeHqeenfv7+x2Wz2P6QYY8yxY8dMkSJFHILjw4cPG39/f9OsWTOTnp5u33bGjBlGknnjjTfc7iNgVZRVe9Hjjz/u8LhBgwY6duyYUlJSJEkfffSRMjIy1L59ex09etS+REdHq0KFClq7dq39tf8szTt79qyOHj2qevXqyRijn376yeU+tWvXTmFhYfbHdevWlSR16tRJBQoUcFifmprqULL0zz6cPn1aR48eVYMGDXTu3LkspdEFChRQ79697Y/9/f3Vu3dvHT58WD/++KN9/ZdffpmlJM6Z8PBw/f333/r++++vue21zvuVx3Lq1CkdPXpUDRs21F9//aVTp045vL5s2bJq3ry5030lJCQoPj7e/vjmm2/Wfffdpy+++ELp6ekyxujDDz/UvffeK2OMw/vcvHlznTp1yu3y4ez6484xueKHH37Q4cOH9cQTTzjcN9eqVSvFxsbqs88+u+rrH3roIV26dEkfffSRfd2KFSt08uRJPfTQQ/Z14eHh2rhxow4cOOB2H6/0+eefKzo6Wg8//LB9nZ+fnwYMGKAzZ87oq6++cti+TZs2uummm+yP69Spo7p16+rzzz+XJB0/flxr1qxR+/bt7df80aNHdezYMTVv3lw7d+7MUtbXq1cvh9LGBg0aKD09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H37o9J6ozMBzwIABat68uXx9fZ1m6txVr149FS5cWF27dtWAAQNks9n01ltvZVvqVaJECU2cOFF79uzRLbfcosWLF2vLli2aN2+e/Pz87Nt16dJFX3311TVLxpo1a6bo6GjdfvvtioqK0vbt2zVjxgy1atVKISEhbh1Ls2bN5O/vr3vvvVe9e/fWmTNn9NprrykyMtJpAHQ1VapUUfPmzTVgwAAFBATYf6z+s9R7woQJWrt2rerWrauePXsqLi5Ox48f1+bNm7Vq1aos80l7wp1jio+P1+zZs/XCCy8oJiZGkZGRWf4aL13Olk6cOFHdu3dXw4YN9fDDD+vQoUOaOnWqypQpo0GDBrnUt4ceekijRo1SYGCgevTo4ZClOn36tEqWLKkHH3xQ1atXV6FChbRq1Sp9//33Dn9xnzFjhsaOHau1a9dmmav1n3r16qW5c+eqW7du+vHHH1WmTBl98MEHWrdunaZMmZLlWomJiVH9+vXVp08fXbx4UVOmTFHRokX19NNP27eZOXOm6tevr6pVq6pnz54qV66cDh06pA0bNujvv//W1q1bXToPOSE0NFR33HGHXnrpJV26dEk33XSTVqxYkW0Fhyvi4+PVtm1bTZkyRceOHdNtt92mr776yp6R+2c2J7evZVev49DQUL366qt67LHHVLt2bftc0Fu3btW5c+fsc/XGx8dr8eLFGjx4sGrXrq1ChQrp3nvvdfs68dTevXv11ltvSbo8qJAkvfDCC5IuZ4I6d+7sUjuufv/6+/trzJgx6t+/v+666y61b99ee/bs0cKFC1W+fHmH9/J6zkFERIQSExM1ePBgVa1aVY8++qiio6P1ww8/aNGiRbrtttu0efNmPfjgg1q2bJnDd/4/ufsdExgYqOXLl6tr166qW7euli1bps8++0zPPPOMvVKnYcOG6t27txITE7VlyxY1a9ZMfn5+2rlzp95//31NnTpVDz74oEvnPSe0atVKkydPVosWLdSxY0cdPnxYM2fOVExMjH7++WeP2hwzZoxWrFih22+/XX369FF6erpmzJihKlWqOARZrv7GuB733HOP3nrrLYWFhSkuLk4bNmzQqlWrVLRoUYfthg4dqg8++EDt2rXTo48+qvj4eB0/flyffvqp5syZo+rVq6t8+fIKDw/XnDlzFBISouDgYNWtW1dly5bNkf+LXPXFF1/o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QkNDFR4erh49eujMmTMO2/z8889q0KCBAgMD7bfoepvLmeNPP/1Ud999t/z8/FSrVi37+sjISC1fvjxXOpfp999/V0ZGhubOnauYmBj9+uuv6tmzp86ePcsAYAAAAADggdTUVLVr104JCQmaP39+lufT09PVqlUrRUdHa/369Tp48KC6dOkiPz8/vfjii9m2+8gjj+jgwYNauXKlLl26pO7du6tXr1569913JUkpKSlq1qyZmjRpojlz5uiXX37Ro48+qvDwcPXq1SvXjvdabMYYl8Y/8/X1VXJysiIiIuTr66uDBw8qMjIyt/uXrUmTJmn27Nn666+/XH5NSkqKwsLCdOrUKYWGhuZi7wAAAADcqL+/M/tdeuIL8gkM9GpfMi5c0N5hI3P1HC5cuFADBw7UyZMnHdYvW7ZM99xzjw4cOKCoqChJ0pw5czRs2DAdOXJE/v7+Wdravn274uLi9P3339uTqsuXL1fLli31999/q0SJEpo9e7aeffZZJScn29sYPny4lixZot9//z3Hjy8xMVF9+vTJNjueyeWy6oiICH333XeSJGOMbDbbdXXwep06dUpFihS56jYXL15USkqKwwIAAAAAN5or45qLFy/m+j43bNigqlWr2gNjSWrevLlSUlK0bdu2bF8THh7uUG3cpEkT+fj4aOPGjfZt7rjjDofgunnz5tqxY4dOnDjhVh/feust3X777SpRooT27t0rSZoyZYo++eQT+zYjRoy4ZmAsuREcP/7447rvvvvk6+srm82m6Oho+fr6Ol1y265duzR9+nT17t37qtslJiYqLCzMvpQqVSrX+wYAAADgf4Sx5Y9FUqlSpRxim8TExFw//OTkZIfAWJL9cXJycravubLCuECBAipSpIj9NZ6068zs2bM1ePBgtWzZUidPnlR6erokKTw8XFOmTHG5HXs/Xd1wzJgx6tChg3bt2qXWrVtrwYIFLkXfVzN8+HBNnDjxqtts375dsbGx9sf79+9XixYt1K5dO/Xs2fOqrx0xYoQGDx5sf5ySkkKADAAAAOCGs2/fPoey6oCAAKfbeRJj3aimT5+u1157TW3atNGECRPs62vVqqWnnnrK7fbcmsopNjZWsbGxGj16tNq1a6eCBQtedft169apVq1a2b5xQ4YMUbdu3a7aRrly5ez/PnDggO68807Vq1fPpVGxAwICst03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",
"text/plain": [
"<Figure size 1000x800 with 2 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"\n",
"# --- 4. 汇总并报告最优结果 ---\n",
"if all_results:\n",
" results_df = pd.DataFrame(all_results)\n",
" print(\"\\n--- 所有回测结果汇总 ---\")\n",
" print(results_df.round(2)) # 打印所有组合的结果,保留两位小数\n",
"\n",
" # 根据某个指标(例如夏普比率)找到最优参数组合\n",
" # 确保 DataFrame 不为空且有 sharpe_ratio 列\n",
" if not results_df.empty and 'sharpe_ratio' in results_df.columns:\n",
" # 过滤掉NaN值如果所有夏普比率都是NaN则可能没有有效结果\n",
" normal_results = results_df[(results_df['total_trades'] > 200) & (results_df['total_return'] > 0)]\n",
" if len(normal_results) > 0:\n",
" best_result = normal_results.loc[(normal_results['sharpe_ratio'].idxmax())]\n",
" print(\"\\n--- 最优参数组合 (按夏普比率) ---\")\n",
" print(best_result)\n",
" else:\n",
" print(\"\\n没有有效的回测结果可用于找出最优参数。\")\n",
"else:\n",
" print(\"\\n没有执行任何回测。\")\n",
"print(\"\\n--- 动态网格搜索完成 ---\")\n",
"# --- 5. 使用新的 GridSearchAnalyzer 分析和可视化结果 ---\n",
"print(grid_results)\n",
"if grid_results:\n",
" grid_analyzer = GridSearchAnalyzer(grid_results, optimization_metric)\n",
" grid_analyzer.find_best_parameters()\n",
" grid_analyzer.plot_heatmap()\n",
"else:\n",
" print(\"\\n没有生成任何网格搜索结果无法进行分析。\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "quant",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.11"
}
},
"nbformat": 4,
"nbformat_minor": 5
}