找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3569

积分

0

好友

490

主题
发表于 18 小时前 | 查看: 1| 回复: 0

很多量化交易初学者都有一个误区:花大量时间寻找那个“最优参数”,以为找到了就能稳定赚钱。但现实往往是——在回测中表现完美的策略,一到实盘就崩溃。

问题出在哪里?验证方法太弱了。

今天这篇文章,我们通过一个完整的 Python 案例,带你了解如何用「参数扫描」和「鲁棒性测试」来判断一个策略是否真的可靠,而不是碰巧在某个参数下表现好。

核心思想:从“找最优”到“验证稳定性”

传统做法是:测试一堆参数,选表现最好的那个。

正确做法是:测试一堆参数,看附近的参数表现是否也不错

为什么?因为市场会变化,数据有噪声。如果你的策略只在某一个精确参数下有效,稍微偏一点就亏钱,那它大概率是“过拟合”的产物,实盘必死。

一个健壮的策略,应该像一座“小山丘”——在一个参数区间内都表现良好,而不是一根“针尖”——只在某个点上有效。

策略介绍:入场靠“弱势”,出场靠“动量”

我们测试的策略逻辑很简单:

  • 入场条件:当日开盘价低于加权移动平均线(WMA),说明短期相对弱势
  • 出场条件:MACD 线上穿信号线,说明动量开始恢复

用一句话概括:逢低买入,动量确认后卖出

这个逻辑是合理的——不是随便堆砌指标,而是有明确的交易思路。

实战案例:用 Python 进行参数扫描

第一步:单参数扫描(WMA 周期)

我们不假设 WMA = 20 就是最好的,而是测试 16 到 24 的所有值:

import pandas as pd
import numpy as np
import vectorbt as vbt

# 参数扫描:测试 WMA 周期从 16 到 24
results = []

for wma_p in range(16, 25):
    # 用当前 WMA 周期重新计算指标
    tmp = df.copy()

    # 计算加权移动平均线
    weights = np.arange(1, wma_p + 1)
    tmp['WMA'] = tmp['Close'].rolling(wma_p).apply(
        lambda prices: np.dot(prices, weights) / weights.sum(),
        raw=True
    )

    # 入场信号:开盘价低于 WMA
    tmp["WMA_Open_Below"] = tmp["Open"] < tmp["WMA"]

    # 出场信号:MACD 上穿信号线(这里简化,假设已计算好)
    entries = tmp["WMA_Open_Below"].shift(1).fillna(False)
    exits = tmp["MACD_cross_above_signal"].shift(1).fillna(False)

    # 回测
    pf = vbt.Portfolio.from_signals(
        close=tmp["Open"],
        entries=entries.to_numpy(),
        exits=exits.to_numpy(),
        init_cash=100_000,  # 初始资金 10 万
        fees=0.001,         # 手续费 0.1%
        slippage=0.002,     # 滑点 0.2%
        freq="1d"
    )

    # 记录结果
    results.append({
        "WMA_PERIOD": wma_p,
        "Total Return": pf.total_return(),
        "Sharpe": pf.sharpe_ratio(),
        "MaxDD": pf.max_drawdown()
    })

# 转换为 DataFrame 查看结果
results_df = pd.DataFrame(results).set_index("WMA_PERIOD")
print(results_df)

第二步:查看结果——寻找“平台”而非“针尖”

好的结果应该是这样的:

WMA_PERIOD Total Return Sharpe MaxDD
16 22.5 0.52 0.85
17 24.1 0.55 0.83
18 25.3 0.58 0.82
19 26.8 0.56 0.84
20 23.7 0.57 0.88

关键观察点:

  • 多个相邻参数都有正收益 ✓
  • 没有出现“20 赚钱,19 和 21 都亏钱”的情况 ✓
  • 整体呈现平滑的“小山丘”形态 ✓

这说明策略的逻辑本身在起作用,而不是某个参数碰巧撞上了。

第三步:多参数网格搜索(MACD 三参数)

MACD 有三个参数:快线、慢线、信号线。我们用三维网格搜索来验证:

from itertools import product

# 定义参数范围
FAST_RANGE = range(10, 15)    # 快线周期:10-14
SIGNAL_RANGE = range(7, 12)   # 信号线周期:7-11
SLOW_RANGE = range(22, 31)    # 慢线周期:22-30

records = []

# 遍历所有参数组合
for fast, signal, slow in product(FAST_RANGE, SIGNAL_RANGE, SLOW_RANGE):
    # 用当前参数计算 MACD
    tmp = df.copy()
    tmp["EMA_fast"] = tmp["Close"].ewm(span=fast, adjust=False).mean()
    tmp["EMA_slow"] = tmp["Close"].ewm(span=slow, adjust=False).mean()
    tmp["MACD"] = tmp["EMA_fast"] - tmp["EMA_slow"]
    tmp["Signal"] = tmp["MACD"].ewm(span=signal, adjust=False).mean()

    # 出场信号:MACD 上穿信号线
    tmp["MACD_prev"] = tmp["MACD"].shift(1)
    tmp["Signal_prev"] = tmp["Signal"].shift(1)
    exits = (tmp["MACD_prev"] <= tmp["Signal_prev"]) & (tmp["MACD"] > tmp["Signal"])

    # 回测(入场条件不变)
    pf = vbt.Portfolio.from_signals(
        close=tmp["Open"],
        entries=entries.shift(1).fillna(False).to_numpy(),
        exits=exits.shift(1).fillna(False).to_numpy(),
        init_cash=100_000,
        fees=0.001,
        slippage=0.002,
        freq="1d"
    )

    # 记录参数和收益
    records.append((fast, signal, slow, pf.total_return()))

# 转换为 DataFrame
grid_df = pd.DataFrame(records, columns=["fast", "signal", "slow", "ret"])
print(grid_df.describe())

第四步:可视化参数空间

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# 提取数据
x = grid_df["fast"].values
y = grid_df["signal"].values
z = grid_df["slow"].values
r = grid_df["ret"].values  # 收益率用颜色表示

# 创建 3D 散点图
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection="3d")

sc = ax.scatter(x, y, z, c=r, cmap="viridis", s=60)

ax.set_xlabel("MACD 快线周期")
ax.set_ylabel("MACD 信号线周期")
ax.set_zlabel("MACD 慢线周期")
ax.set_title("MACD 参数性能分布图")

fig.colorbar(sc, ax=ax, label="总收益率")
plt.show()

理想的结果是:图中有大片连续的“亮色区域”(高收益),而不是零星分散的几个点。

如何判断策略是否值得继续研究?

通过上述测试后,一个值得深入研究的 量化策略 应该满足:

  1. 逻辑清晰:入场和出场规则有明确的市场含义,不是随便拼凑
  2. 参数稳定:存在一个参数“平台”,相邻参数表现相近
  3. 表面平滑:三维参数空间中,性能渐变而非突变
  4. 成本可控:扣除手续费和滑点后仍然盈利
  5. 风险合理:最大回撤、胜率、盈亏比等指标健康

只有同时满足这些条件,策略才值得进一步做「压力测试」(Stress Test)和「前推优化」(Walk-Forward Analysis)。

常见误区提醒

误区 正确做法
只看最优参数的收益 同时检查相邻参数的表现
忽略交易成本 始终模拟真实的手续费和滑点
追求极高收益 优先关注策略的稳定性和风险
在全部数据上优化 划分训练集和测试集,避免过拟合

总结

本文的核心观点很简单:好策略不是“找”出来的,而是“验证”出来的。

通过参数扫描,我们关注的不是“哪个参数最赚钱”,而是“策略在多大范围内都能赚钱”。一个只在单一参数下表现好的策略,像针尖一样脆弱;而一个在参数区间内普遍有效的策略,像小山丘一样稳固。

记住这句话:

脆弱的策略有针尖,稳健的策略有山丘。

希望这篇文章能帮助你建立正确的策略验证思维,少走弯路,更扎实地推进你的量化研究。想了解更多技术实战与深度讨论,欢迎访问 云栈社区


参考文章

  1. 用 Python 打造股票预测系统:Transformer 模型教程(一)
  2. 用 Python 打造股票预测系统:Transformer 模型教程(二)
  3. 用 Python 打造股票预测系统:Transformer 模型教程(三)
  4. 用 Python 打造股票预测系统:Transformer 模型教程(完结)
  5. 揭秘隐马尔可夫模型:因子投资的制胜武器
  6. YOLO 也能预测股市涨跌?计算机视觉在股票市场预测中的应用
  7. 金融 AI 助手:FinGPT 让你轻松掌握市场分析
  8. 量化交易秘籍:为什么专业交易员都在用对数收益率?
  9. Python 量化投资利器:Ridge、Lasso 和 Elastic Net 回归详解
  10. 掌握金融波动率模型:完整 Python 实现指南



上一篇:与前上司聊完,我悟了:35+嵌入式开发者的职业突围与年龄真相
下一篇:美国国防部CSRMC框架启示:构建动态防御与运营韧性的网络安全新思路
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-2-28 20:45 , Processed in 0.613332 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表