一、参数优化的核心战场:过拟合与欠拟合

做量化交易的朋友,尤其是搞暗池策略的,一定绕不开这两个词:过拟合和欠拟合。说白了,它们就是参数优化的「阴阳两面」。

1.1 欠拟合:你的策略太「懒」了

欠拟合,就是模型太简单,连历史数据里的规律都没抓住。我见过不少新手,拿一个简单的移动平均线策略去跑暗池数据,结果回测曲线跟心电图似的——毫无规律。为什么?因为暗池里的订单流特征,根本不是几条均线能描述的。

典型症状:

  • 回测收益低,夏普比率小于0.5
  • 训练集和测试集表现都很差
  • 参数稍微一动,结果就崩
我的经验: 有一次我帮团队优化一个暗池冰山订单检测策略,初始模型只有3个参数。回测结果惨不忍睹。后来我加了两个描述订单间隔分布的特征,效果立竿见影。欠拟合,说白了就是「特征工程没做到位」。

1.2 过拟合:你的策略太「聪明」了

过拟合恰恰相反。模型把历史数据里的噪声都当成了规律,学得「太像」了。我在2018年做过一个暗池策略,回测年化收益80%,最大回撤只有3%。当时我兴奋得不行,结果实盘第一周就亏了12%。

为什么会这样?因为我把某个特定时间段的市场微观结构噪声,当成了永恒规律。

典型症状:

  • 训练集收益极高,测试集收益极低
  • 参数数量远多于有效样本量
  • 策略对参数取值极其敏感
避坑指南: 我曾经犯过一个低级错误——用1000笔交易数据去优化50个参数。结果呢?回测曲线完美得像教科书,实盘却像过山车。记住一个经验法则:参数数量不要超过样本量的平方根。

二、回测框架搭建:你的实验台要稳

回测框架,就是你的「实验台」。台子不稳,实验结果全是废的。我个人习惯用Python搭建,核心组件就这几块:

2.1 数据层

暗池数据跟普通交易所数据不一样。你需要处理的是Level 3级别的订单簿数据,包括隐藏订单、冰山订单、以及各种暗池特有的订单类型。

class DarkPoolDataFeed:
    def __init__(self, data_path):
        self.data = self._load_parquet(data_path)
        self.current_idx = 0
    
    def next_tick(self):
        # 返回当前时间片的订单簿快照
        tick = self.data.iloc[self.current_idx]
        self.current_idx += 1
        return tick

2.2 策略引擎

策略引擎负责执行你的交易逻辑。这里要注意:暗池交易通常有最小成交量限制和价格保护机制,回测时必须模拟这些约束。

class DarkPoolStrategy:
    def __init__(self, params):
        self.min_volume = params['min_volume']
        self.price_tolerance = params['price_tolerance']
    
    def on_tick(self, tick):
        # 你的策略逻辑写在这里
        signal = self._generate_signal(tick)
        if signal > 0.7:
            return self._place_order(tick, 'buy')
        return None

2.3 风控模块

这个模块很多人会忽略。我建议至少包含:最大持仓限制、单笔亏损上限、日内交易次数限制。

核心要点: 回测框架的三大支柱——数据准确性、逻辑完整性、风控严格性。缺一个,你的优化结果就是空中楼阁。

三、优化目标函数设计:你到底想要什么?

很多人的目标函数就写一个「最大化收益」。嗯,这很天真。你想想看,一个收益极高但回撤50%的策略,你敢实盘吗?

3.1 常见目标函数

目标函数 公式 适用场景
夏普比率 (Rp - Rf) / σp 通用,追求风险调整后收益
卡玛比率 年化收益 / 最大回撤 厌恶回撤的投资者
索提诺比率 (Rp - Rf) / σd 只关注下行风险
收益-回撤比 总收益 / 最大回撤 短线高频策略

3.2 我的设计哲学

我个人习惯用复合目标函数。比如:

def objective_function(returns, max_drawdown, win_rate):
    sharpe = calculate_sharpe(returns)
    calmar = calculate_calmar(returns, max_drawdown)
    # 给不同指标加权
    score = 0.5 * sharpe + 0.3 * calmar + 0.2 * win_rate
    return score
经验之谈: 权重怎么设?我一般先用等权重跑一轮,看哪个指标拖后腿,再调整。比如发现夏普很高但回撤大,就加大卡玛比率的权重。

四、参数空间与搜索策略:怎么找到最优解?

参数空间,就是所有可能参数取值的集合。搜索策略,就是你怎么在这个空间里「挖宝」。

4.1 参数空间的维度诅咒

假设你有5个参数,每个参数取10个值,那总组合就是10^5 = 10万种。如果每个组合回测需要1秒,那就是27.8小时。嗯,你不可能等那么久。

4.2 搜索策略对比

搜索策略 优点 缺点 适用场景
网格搜索 简单、可并行 维度灾难 参数少于3个
随机搜索 效率高、覆盖广 可能错过最优 参数5-10个
贝叶斯优化 智能、样本效率高 实现复杂 参数10个以上
遗传算法 全局搜索能力强 收敛慢 非凸优化问题

4.3 我的实战选择

在暗池策略优化中,我通常先用随机搜索跑1000次,找到几个「热点区域」,然后用贝叶斯优化在这些区域精细搜索。这样既快又准。

from skopt import gp_minimize

def optimize_strategy(params_space):
    # 先用随机搜索粗筛
    random_results = random_search(params_space, n_iter=1000)
    # 找到top 10%的区域
    top_regions = identify_hotspots(random_results)
    # 贝叶斯精细搜索
    best_params = gp_minimize(
        objective_function,
        top_regions,
        n_calls=200,
        random_state=42
    )
    return best_params
重要提醒: 不管你用什么搜索策略,一定要做交叉验证。我习惯用5折时间序列交叉验证——把数据按时间分成5段,每次用4段训练、1段验证。这样能有效防止过拟合。

知识体系总览

下面这张图,是我对本章知识体系的总结。你可以把它当成一张「作战地图」:

参数优化核心体系 过拟合与欠拟合 回测框架搭建 优化目标函数 参数空间与搜索 训练集高 测试集低 参数过多 训练集测试集都低 特征不足 数据层 策略引擎 风控模块 夏普比率 卡玛比率 复合目标 网格搜索 随机搜索 贝叶斯优化 遗传算法

这张图把四个核心概念串起来了。你从中心出发,沿着四条分支往下走,就能看到每个模块的关键要素。我个人习惯把它打印出来贴在工位上,做优化时随时对照。

专注资料整理