Py学习  »  Python

从 Kaggle 量化竞赛的失败中,我学到了比获胜更重要的 Python 技能

数据科学实战 • 1 月前 • 46 次点击  

欢迎加入专注于财经数据与量化投研的【数据科学实战】知识星球!在这里,您将获取持续更新的《财经数据宝典》和《量化投研宝典》,这两部宝典相辅相成,为您在量化投研道路上提供明确指引。 《量化投研宝典》精选了业内持续维护且实用性强的开源工具(Backtrader、Qlib、VeighNa等),配合详细教程与代码示例,帮助您快速构建量化策略;《财经数据宝典》则汇集了多年财经数据维护经验,全面介绍从 AKShare、Tushare 到 Wind、iFind 等国内外数据源,并附有丰富的使用技巧。 无论您是量化投资新手还是经验丰富的研究者,星球社区都能帮您少走弯路,事半功倍,共同探索数据驱动的投资世界!

引言

你是否曾经信心满满地参加一个数据科学竞赛,结果发现自己陷入了过度工程的陷阱?今天,我想分享一个关于 Kaggle NIFTY50 期权波动率预测竞赛的真实故事。这个故事的主角用了三周时间,尝试了 100 多种方法,最终发现:最简单的方法往往是最好的

对于正在学习 Python 和数据科学的你来说,这个故事不仅仅是关于金融建模,更是关于如何避免常见的编程陷阱,以及如何在复杂性和简洁性之间找到平衡。

竞赛背景:看似简单的预测任务

这个竞赛由 NK Securities Research 在 Kaggle 上举办,任务是预测 NIFTY50 指数期权的缺失隐含波动率(IV)值。数据集包含了不同行权价和到期日的看涨期权和看跌期权数据。

评估指标是均方误差(MSE),听起来很简单,但在期权定价中,微小的波动率差异会导致巨大的价格差异。

第一周:简单方法的意外成功

故事的主角从一个简单的想法开始:使用插值方法来填补缺失的波动率值。

import pandas as pd
import numpy as np
from scipy.interpolate import interp1d

def interpolate_volatility_smile(df, missing_mask, column):
    """
    使用插值方法预测缺失的波动率值
    
    参数:
    df: 数据框
    missing_mask: 缺失值掩码
    column: 要处理的列名
    """

    # 从列名中提取行权价
    strike = int(column.split('_')[-1])
    
    # 找到相关的期权列(同类型,不同行权价)
    if 'call_' in column:
        related_cols = [c for c in df.columns if 'call_iv_' in c and c != column]
    else:
        related_cols = [c for c in df.columns if 'put_iv_' in c and c != column]
    
    predictions = []
    
    # 对每个缺失值进行插值
    for idx in df[missing_mask].index:
        available_data = []
        
        # 收集可用的数据点
        for rel_col in related_cols:
            if not pd.isna(df.loc[idx, rel_col]):
                rel_strike = int(rel_col.split('_')[-1])
                rel_iv = df.loc[idx, rel_col]
                available_data.append((rel_strike, rel_iv))
        
        # 如果有足够的数据点,进行二次插值
        if len(available_data) >= 2:
            strikes, ivs = zip(*available_data)
            # 使用二次插值捕捉波动率微笑的形状
            interp_func = interp1d(strikes, ivs, kind='quadratic'
                                 fill_value='extrapolate')
            pred = interp_func(strike)
            predictions.append(pred)
    
    return predictions

这个简单的方法取得了惊人的效果:MSE 为 0.000042541,在 2095 名参赛者中排名第 127(前 6%)!

第二周:复杂性陷阱

尝到甜头后,主角决定使用更复杂的方法来提升成绩。首先是 LSTM 神经网络:

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Dropout

def build_lstm_model(sequence_length, n_features):
    """
    构建 LSTM 模型用于时间序列预测
    
    参数:
    sequence_length: 序列长度
    n_features: 特征数量
    """

    model = Sequential([
        # 第一层 LSTM,返回序列
        LSTM(64, return_sequences=True
             input_shape=(sequence_length, n_features)),
        Dropout(0.2),  # 防止过拟合
        
        # 第二层 LSTM
        LSTM(32, return_sequences=False),
        Dropout(0.2),
        
         # 全连接层
        Dense(16, activation='relu'),
        Dense(1, activation='linear')  # 输出层
    ])
    
    model.compile(optimizer='adam', loss='mse')
    return model

结果:MSE 跃升到 0.000122896,比基准方法差了近 3 倍!

接下来,主角尝试了更复杂的数学方法:

from scipy.interpolate import PchipInterpolator
from scipy.optimize import minimize

def advanced_volatility_modeling(df, strikes, ivs):
    """
    使用高级数学方法建模波动率曲面
    
    参数:
    df: 数据框
    strikes: 行权价数组
    ivs: 隐含波动率数组
    """

    # 使用 PCHIP 插值器(保形分段三次 Hermite 插值)
    # 这种方法可以保证插值结果的单调性
    interpolator = PchipInterpolator(strikes, ivs)
    
    # 定义优化目标函数
    def objective(params):
        """最小化预测误差"""
        a, b, c = params
        # 构建二次波动率模型
        predicted = a * strikes**2 + b * strikes + c
        return np.sum((predicted - ivs)**2)
    
    # 使用优化算法找到最佳参数
    initial_guess = [0.0001-0.010.5]
    result = minimize(objective, initial_guess)
    
    return interpolator, result.x

结果更糟糕!主角开始意识到,在量化金融中,理解简单方法为什么有效,比实现复杂方法更有价值。

第三周:绝望的尝试与最终领悟

到了第三周,主角开始分析数据集的特点,发现训练集和测试集存在一些差异:

def analyze_dataset_differences(train_df, test_df):
    """
    分析训练集和测试集的差异
    """

    print(f"训练集列数:{len(train_df.columns)}")
    print(f"测试集列数:{len(test_df.columns)}")
    
    print(f"\n训练集缺失值:{train_df.isnull().sum().sum()}")
    print(f"测试集缺失值:{test_df.isnull().sum().sum()}")
    
    # 检查行权价范围
    train_strikes = [int(col.split( '_')[-1]) 
                    for col in train_df.columns 
                    if '_iv_' in col]
    test_strikes = [int(col.split('_')[-1]) 
                   for col in test_df.columns 
                   if '_iv_' in col]
    
    print(f"\n训练集行权价范围:{min(train_strikes)} - {max(train_strikes)}")
    print(f"测试集行权价范围:{min(test_strikes)} - {max(test_strikes)}")

最后的尝试是利用完整的训练数据构建波动率曲面模型:

def build_volatility_surface(train_df):
    """
    从训练数据构建完整的波动率曲面
    
    返回一个可以根据标的价格和行权价预测波动率的模型
    """

    volatility_patterns = {}
    
    for idx in train_df.index:
        underlying = train_df.loc[idx, 'underlying']
        
        # 收集该标的价格下的所有波动率数据
        call_ivs = {}
        put_ivs = {}
        
        for col in train_df.columns:
            if 'call_iv_' in col:
                strike = int(col.split('_')[-1])
                iv = train_df.loc[idx, col]
                if not pd.isna(iv):
                    call_ivs[strike] = iv
            elif 'put_iv_' in col:
                strike = int(col.split('_')[-1])
                iv = train_df.loc[idx, col]
                if not pd.isna(iv):
                    put_ivs[strike] = iv
        
        # 存储完整的波动率模式
        volatility_patterns[underlying] = {
            'calls': call_ivs,
            'puts': put_ivs
        }
    
    return volatility_patterns

这个方法运行了 4 小时,结果 MSE 达到 0.000351514,比原始方法差了近 8 倍!

关键教训:Python 编程中的智慧

1. 简单优于复杂

Python 之禅告诉我们:"Simple is better than complex"。这个故事完美诠释了这一点。在解决实际问题时,先从简单方法开始,只有在确实需要时才增加复杂性。

2. 理解问题域的重要性

# 错误示范:盲目应用技术
def wrong_approach():
    # 不理解金融概念,直接套用机器学习
    model = SuperComplexModel()
    model.fit(data)
    return model.predict()

# 正确示范:先理解问题,再选择方法
def right_approach():
    # 理解波动率微笑是什么
    # 知道为什么要用插值
    # 选择合适的插值方法
    return simple_interpolation(data)

3. 验证每一步改进

不要假设更复杂的方法一定更好。每次修改都要验证:

def validate_improvement(baseline_score, new_score):
    """
    验证新方法是否真的改进了结果
    """

    improvement = (baseline_score - new_score) / baseline_score * 100
    
    if improvement > 0:
        print(f"✅ 改进了 {improvement:.2f}%")
    else:
        print(f"❌ 性能下降了 {abs(improvement):.2f}%")
        print("考虑回退到之前的方法")
    
    return improvement > 0

4. 保持代码的可解释性

# 不好的做法:难以理解的一行代码
result = [interp1d(s[s.notna()].index, s[s.notna()], 'cubic')(i) 
          for i, s in df.iterrows() if len(s[s.notna()]) > 2]

# 好的做法:清晰易懂的代码
def interpolate_missing_values(df):
    """对缺失值进行插值"""
    results = []
    
    for index, series in df.iterrows():
        # 获取非缺失值
        valid_data = series[series.notna()]
        
        # 确保有足够的点进行插值
        if len(valid_data) >  2:
            # 创建插值函数
            interp_func = interp1d(valid_data.index, valid_data, kind='cubic')
            # 应用插值
            result = interp_func(index)
            results.append(result)
    
    return results

总结

这个 Kaggle 竞赛的故事给我们的启示远超技术本身。作为 Python 学习者,我们经常会陷入"技术越复杂越好"的误区。但实际上:

  1. 从简单开始:先实现一个基础版本,确保它能工作
  2. 逐步改进:每次只改变一个地方,并验证效果
  3. 理解为先:深入理解问题比掌握复杂技术更重要
  4. 保持谦逊:市场和数据会教会我们很多东西

正如作者所说,虽然没有赢得比赛,但获得的知识是无价的。失败的 100 多次尝试,每一次都是一堂宝贵的课。

记住:在数据科学和 Python 编程的道路上,最好的老师往往是我们自己的错误。保持好奇心,勇于尝试,但也要知道何时该停下来思考。

参考文章

加入专注于财经数据与量化投研的知识星球【数据科学实战】,获取完整研究解析、详细回测框架代码实现和完整策略逻辑实操指南。

财经数据与量化投研知识社区

核心权益如下:

  1. 赠送《财经数据宝典》完整文档,汇集多年财经数据维护经验
  2. 赠送《量化投研宝典》完整文档,汇集多年量化投研领域经验
  3. 赠送《PyBroker-入门及实战》视频课程,手把手学习量化策略开发
  4. 每日分享高质量量化投研文章、代码和相关资料
  5. 定期更新高频财经数据
  6. 参与年度不少于 10 次专属直播与录播课程
  7. 与核心开发者直接交流,解决实际问题
  8. 获取专业微信群交流机会和课程折扣

星球已有丰富内容积累,包括量化投研论文、财经高频数据、 PyBroker 视频教程、定期直播、数据分享和答疑解难。适合对量化投研和财经数据分析有兴趣的学习者及从业者。欢迎加入我们!

好文推荐

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 实现指南

好书推荐

《Python编程:从入门到实践(第3版)》是一本广受欢迎的 Python 入门经典教材,由经验丰富的程序员 Eric Matthes 编写。该书采用循序渐进的教学方式,从基础语法讲解到实战项目开发,内容编排合理,实例丰富,语言通俗易懂。全书配有大量练习题和完整项目实战,包括数据可视化、网络爬虫、Web 应用开发等,让读者在实践中掌握编程技巧。第3版还增加了 f-string、海龟绘图等最新的 Python 特性内容。这本书不仅适合零基础读者入门学习,也非常适合想系统掌握 Python 的编程爱好者以及数据分析、人工智能等领域的学习者。它不仅教授编程知识,更注重培养读者的编程思维,是一本非常值得投资的 Python 学习指南。


Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/183520
 
46 次点击