Kaggle量化比赛复盘: Optiver - Trading at the Close

目录

前言

一、开源方案

1. 6th获奖方案(代码未开源)

1.1. 特征工程(关键代码)

1.2. 方案解析

2. 7th获奖方案(开源)

2.1. 特征工程

2.2. 特征工程

3. 9th获奖方案(半开源)

3.1. 特征构造

3.2. 特征筛选

3.3. 模型

3.4. zero_sum(标签后处理)

4. 14th获奖方案(开源)

4.1. 方案开源链接

4.2. zero_sum(标签后处理)

5. 15th获奖方案(半开源)

5.1. 特征工程

5.2. 模型

5.2. 时序交叉验证/滚动训练

5.3. zero_sum(标签平滑技术)

6. 23th获奖方案(半开源)

6.1. 特征工程

6.2. 模型

6.3. CV交叉验证

6.4. 关于NN取舍、在线学习的思考

7. 25th获奖方案(半开源)

7.1. 特征工程

7.2. 模型

二、比赛Trick/Fancy Idea

三、思考


前言

基于集合竞价开始以来经过 N 秒的行情数据,进行target预测。

比赛链接:https://www.kaggle.com/competitions/optiver-trading-at-the-close(科学上网)


一、开源方案

1. 6th获奖方案(代码未开源)

kaggle:https://www.kaggle.com/competitions/optiver-trading-at-the-close/discussion/486040

overview:少量特征工程(特征工程在本方案中占比不大)+深度学习(3*Transformer+1*GRU)+滚动训练/增量学习+zero_sum后处理。

1.1. 特征工程(关键代码)

# 特征工程
# (1)集合竞价前n秒数据分桶
df['seconds_in_bucket_flag_1'] = df['seconds_in_bucket'] >= 300 - 60
df['seconds_in_bucket_flag_2'] = df['seconds_in_bucket'] >= 300
df['seconds_in_bucket_flag_3'] = df['seconds_in_bucket'] >= 480 - 60
df['seconds_in_bucket_flag_4'] = df['seconds_in_bucket'] >= 480

# (2)常规量化因子
df["volume"] = df['ask_size'] + df['bid_size']
df["mid_price"] = (df['ask_price'] + df['bid_price']) / 2
df["liquidity_imbalance"] = (df['bid_size'] - df['ask_size']) / df["volume"]
df["matched_imbalance"] = (df['imbalance_size'] - df['matched_size']) / (df['imbalance_size'] + df['matched_size'])
df["size_imbalance"] = df['bid_size'] / df['ask_size']
df['harmonic_imbalance'] = 2 / ((1 / df['bid_size']) + (1 / df['ask_size'] ))


# (3)基于数学算子的常规特征
from itertools import combinations
prices = ["reference_price", "far_price", "near_price", "ask_price", "bid_price", "wap"]
for c in combinations(prices, 2):
    df[f"{c[0]}_{c[1]}_imb"] = df.eval(f"({c[0]} - {c[1]})/({c[0]} + {c[1]})")

1.2. 方案解析

(1)数据预处理:用0填充缺失值,特征标准化;
(2)特征工程:模型共35-36个特征,对seconds_in_bucket(集合竞价前N秒数据进行数值分桶),以及1.1部分的其它特征工程(在本方案中,特征工程work占比不大);
(3)建模:3*transformers + 1* GRU。训练过程采用滚动训练/在线增量学习训练模型,并在滚动过程中使用最新的20day数据评估模型。此外,由于评估指标在每次训练后都不稳定,方案使用指数移动平均值来平滑数值并比较模型;
(4)后处理(比赛trick):对pre进行zero_sum处理。除一个模型外,所有模型在训练时都附加了一个约束条件,即模型输出总和为零;
(5)模型融合:平均加权——(3*transformers+1* GRU)/4。

2. 7th获奖方案(开源)

github:https://github.com/nimashahbazi/optiver-trading-close

kaggle:https://www.kaggle.com/competitions/optiver-trading-at-the-close/discussion/486169

overview:特征工程(手工常规特征、偏差特征)+模型融合(LGB+LSTM、ConvNet)+在线学习滚动更新模型参数;

2.1. 特征工程

常规特征: 

订单簿不平衡:利用公开共享的imb1、imb2等。
趋势指标:采用diff()实现时间变化。
基于交易量的累积:汇总一段时间内的交易量。
全球股票统计:计算历史股票数据的平均值、中位数和标准差。
偏差特征:基于树的模型和神经网络模型都受益于代表偏离中位数的原始特征,如下图所示:

偏差特征(横截面特征):

# 偏差特征/横截面特征/中性化处理
# new_fea = df[feature] - grouped_median,计算每个特征值与其在同一天、同一集合竞价时间分组聚合的中位数(grouped_median)的偏差
# 标准化特征值,更好地识别异常值,减少组间偏差,提高模型性能,增强数据解释性,并便于后续的数据分析工作
  def create_deviation_within_seconds(df, num_features):
      groupby_cols = ['date_id', 'seconds_in_bucket']
      new_columns = {}
      for feature in num_features:
          grouped_median = df.groupby(groupby_cols)[feature].transform('median')
          deviation_col_name = f'deviation_from_median_{feature}'
          new_columns[deviation_col_name] = df[feature] - grouped_median
      return pd.concat([df, pd.DataFrame(new_columns)], axis=1)

2.2. 特征工程

LGB+NN(LSTM and ConvNet models)+在线学习。

3. 9th获奖方案(半开源)

kaggle:https://www.kaggle.com/competitions/optiver-trading-at-the-close/discussion/486868

overview:特征工程+特征筛选(157 features)+3*XGB(different seeds)+在线学习+zero_sum标签平滑技术。

3.1. 特征构造

# (1)特征缩放(横截面特征):根据stock_id分组,基于横截面median进行(也可以加入其他算子,加减乘除等)
size_col = ['imbalance_size','matched_size','bid_size','ask_size']
for _ in size_col:
    train[f"scale_{_}"] = train[_] / train.groupby(['stock_id'])[_].transform('median')

# (2)常规特征工程/聚合
* 订单薄不平衡:imb1、imb2 特征;
* diff特征:diff() on different time window;
* rolling特征:rolling_mean/std on different time window;
* shift特征:shift() on different time window;
* 基于全局股票数据groupby()的统计特征
* MACD系列特征;
* 基于stock_id & seconds_in_bucket进行groupby()的聚合特征。

3.2. 特征筛选

对已构造特征进行分组(10~30个特征一组),逐组加入模型,观察该组特征对模型是否有提升。如果有,再逐个加入,挑选提升效果最好的Top 5~Top 10。最终筛选保留157个特征。

3.3. 模型

3*XGB(different seeds)+在线学习(re-training模型两次,T、T+30)。测试发现LGB和XGB效果相近,但XGB比LGB训练速度快很多。

3.4. zero_sum(标签后处理)

test_df['pred'] = lgb_predictions
test_df['w_pred'] = test_df['weight'] * test_df['pred']
test_df["post_num"] = test_df.groupby(["date_id","seconds_in_bucket"])['w_pred'].transform('sum') / test_df.groupby(["date_id","seconds_in_bucket"])['weight'].transform('sum')
test_df['pred'] = test_df['pred'] - test_df['post_num']

4. 14th获奖方案(开源)

kaggle link: https://www.kaggle.com/competitions/optiver-trading-at-the-close/discussion/485985

overview:特征工程(占主导作用)+模型融合(lgb,xgb,cat)。无滚动训练过程/增量学习/在线学习,纯靠稳定的特征工程和模型融合without shake in private。

4.1. 方案开源链接

https://www.kaggle.com/code/cody11null/14th-place-gold-cat-lgb-refit#FEATURE-ENGINEERING

4.2. zero_sum(标签后处理)

# weight_i * target_i = 0
def weight_adjustment(pred, test_weights, adjustment=True):
    if adjustment==True:
        pred = pred - np.average(pred, weights = test_weights)
    return pred

5. 15th获奖方案(半开源)

github:https://github.com/osyuksel/kaggle-optiver-2024/blob/main/train.py

kaggle:https://www.kaggle.com/competitions/optiver-trading-at-the-close/discussion/486086

overview:特征工程+建模,3*LGB+4*XGB(离线)+1*LGB(在线学习/滚动训练,re-trained every 5 days with a window of 60 days)。

5.1. 特征工程

大部分滚动特征起反效果,同时借鉴了其它方案的特征工程。

5.2. 模型

  • 3*LGB(离线);
  • 4*XGB(离线);
  • 1*LGB(在线学习/滚动训练,re-trained every 5 days with a window of 60 days)。

5.2. 时序交叉验证/滚动训练

按date_id日期id进行n次时序交叉验证,每次间隔5天。

5.3. zero_sum(标签平滑技术)

同3.4. & 4.2.

6. 23th获奖方案(半开源)

kaggle:https://www.kaggle.com/competitions/optiver-trading-at-the-close/discussion/485993

overview:Feature is all you need,特征工程(200~300个——>75个,占主导作用)+树模型(LGB 、CAT、XGB)+NN(MLP, 1D-CNN, and transformer_encoder)+无滚动训练/增量学习过程、舍弃了NN模型。

6.1. 特征工程

  • 日内统计特征;
  • 跨日统计数据(遗传算法挖掘);
  • 截面统计特征。

官网讲述了特征构造思路,但具体如何实现尚无源码。详情见:https://www.kaggle.com/competitions/optiver-trading-at-the-close/discussion/462664

6.2. 模型

  • CatBoost:成为最有效的模型(效果OK);
  • LGB和XGB:表现出相似的性能特征,使模型一起提高了约0.0010至0.0012(效果一般);
  • NN:由MLP、1D-CNN和变压器编码器层组成,最终形成一个完全连接的层(效果较差、最终舍弃)。

6.3. CV交叉验证

6.4. 关于NN取舍、在线学习的思考

未加入滚动训练/增量学习,同时舍弃了NN模型无滚动训练/增量学习——分析数据观察到,纳斯达克在6月至12月期间市场波动很大。担心该模型对11月回撤极其敏感性,可能严重影响未来的数据;舍弃NN模型——权衡NN的稳定性和波动性。

7. 25th获奖方案(半开源)

github:https://github.com/nlztrk/Optiver-Trading-at-the-Close/blob/main/seed_full_train.py

kaggle:https://www.kaggle.com/competitions/optiver-trading-at-the-close/discussion/485967

overview:特征工程+ML模型(2*XGB) + NN模型(LSTM,CNN,Transformer and MLP)。

7.1. 特征工程

# 用于XGB模型训练,NN模型仅使用原始特征
"""
(1)加权WAP;
(2)WAP指数比率;
(3)WAP指数差异;
(4)对数收益和实现波动性(对于wap、ask_price和bid_price等特征的对数收益);
(5)价格差(询价价格和出价价格之间的差);
(6)价差强度(价差流动性不平衡);
(7)成交量(询价量+出价量);
(8)中间价格(询价和出价价格的平均值);
(9)流动性不平衡(出价和询价订单的大小差异,归一化为它们的和);
(10)匹配不平衡(匹配大小的不平衡归一化为匹配大小和不平衡大小的和);
(11)大小不平衡 (出价大小/询价大小);
(12)不平衡动量 (不平衡大小相对于匹配大小随时间变化的变化率);
(13)市场紧迫性 (价格差流动性不平衡);
(14)深度压力(出价和询价大小的差异*远近价格的差异);
(15)价差深度比(价格差/出价和询价大小的总和);
(16)中间价格变动(50秒内中间价格的变动);
(17)谐波不平衡(出价和询价大小的谐波平均值);
(18)反向WAP(从出价和询价价格和大小计算的加权平均价格);
(19)出价-询价价差不平衡交互;
(20)出价-询价价差百分比;
(21)价差与成交量比率(出价-询价价差与匹配大小的比率);
(22)订单簿深度与成交量比率(出价和询价大小之和与匹配大小的比率);
(23)平均交易大小(每秒匹配交易的平均大小);
(24)相对价差(出价-询价价差与WAP的比率);
(25)对所有股票进行排名特征;
"""
# 使用不同种子对5个XGBS进行MAE损失训练,并使用中位数混合生成预测。

7.2. 模型

  • 2*XGB:一个训练特征集1(简化特征)、一个训练特征集1(在简化特征集上增加更多的特征),以探索不同特征组合对模型性能的影响;
  • NN系列模型:LSTM,CNN,Transformer and MLP。仅用原始特征、未添加任何特征工程,对时间戳归一化,对特征log缩放;
  • 无增量学习/滚动训练/时序交叉验证,依赖特征工程+模型鲁棒性。

二、比赛Trick/Fancy Idea

  1. 特征构造:Big Common Feature Improvement by Simple Changes (kaggle.com)
  2. 特征筛选:方法(1)根据CAT等树模评估特征重要性,通过select_features()筛选Top N特征。select_features - CatBoost | CatBoost。方法(2)分组测试——>逐个测试,保留提升效果最好的Top N(详情见3.2部分)。
  3. 解决NN Shake问题:在线学习/增量学习/滚动训练+模型集成(NN+XGB+CAT)+NN dropout 0.6。
  4. 解决Shake问题:“在线/最新数据”和“离线/历史数据”学习混合。模型集成(3*LGB、1*XGB离线训练+1*LGB滚动训练/增量学习)。3*LGB+1*XGB离线训练保证模型稳定性、保留通用知识;加入1个滚动训练/增量学习的LGB,实时更新模型,增强模型的泛化性。
  5.  zero_sum技术(label平滑):https://github.com/gotoConversion/goto_conversion/

    # 通过zero_sum对模型的预测结果进行调整,通过计算和减去加权平均预测值,使得所有股票的预测目标加权和为零;
    # 这是一种常见的归一化技术,特别是在投资组合管理中,确保预测的总体偏差被消除,有助于避免整体市场的偏差对预测结果的影响。
    test_df['pred'] = pred
    test_df['w_pred'] = test_df['weight'] * test_df['pred']
    test_df["post_num"] = test_df.groupby(["date_id","seconds_in_bucket"])['w_pred'].transform('sum') / test_df.groupby(["date_id","seconds_in_bucket"])['weight'].transform('sum')
    test_df['pred'] = test_df['pred'] - test_df['post_num']
    
  6. 减少内存、加快训练速度:reduce_mem_usage()函数,转换数据类型。

    # Memory reduction
    def reduce_mem_usage(df, verbose=0):
        """
        Iterate through all numeric columns of a dataframe and modify the data type to reduce memory usage.
        """;
    
        start_mem = df.memory_usage().sum() / 1024**2
    
        for col in df.columns:
            col_type = df[col].dtype
    
            if col_type != object and col != "target":
                c_min = df[col].min()
                c_max = df[col].max()
    
                if str(col_type)[:3] == "int" or str(col_type)[:4] == "uint":
                    if c_min > np.iinfo(np.int8).min and c_max < np.iinfo(np.int8).max:
                        df[col] = df[col].astype(np.int8)
                    elif c_min > np.iinfo(np.int16).min and c_max < np.iinfo(np.int16).max:
                        df[col] = df[col].astype(np.int16)
                    elif c_min > np.iinfo(np.int32).min and c_max < np.iinfo(np.int32).max:
                        df[col] = df[col].astype(np.int32)
                    elif c_min > np.iinfo(np.int64).min and c_max < np.iinfo(np.int64).max:
                        df[col] = df[col].astype(np.int64)
                else:
                    if c_min > np.finfo(np.float16).min and c_max < np.finfo(np.float16).max:
                        df[col] = df[col].astype(np.float32)
                    elif c_min > np.finfo(np.float32).min and c_max < np.finfo(np.float32).max:
                        df[col] = df[col].astype(np.float32)
                    else:
                        df[col] = df[col].astype(np.float32)
        return df
  7. 特征分布漂移分析:对特征逐个进行分布漂移分析,对分布漂移比较严重的特征进行剔除/nan值等填充。 

  8. 参考链接:https://zhuanlan.zhihu.com/p/673383174


三、思考

        在量化领域,开源方案特征工程比较常规,基于领域经验的规则特征、常规统计特征(mean、std、max、min)、横截面特征(偏差特征、rank特征等等)、特征缩放、特征直接的算子计算(加减乘除)等等。在比赛中比较fancy的是:

(1)zero_sum标签平衡技术,可以消除整体市场偏差、提高跨股票的可比性、提高风险管理能力、提高策略的稳健性、避免过度拟合、提高预测的解释性等等;

(2)离线模型、在线模型/模型rolling/增量学习混合,“在线/最新数据”和“离线/历史数据”学习混合,既保持了模型的通用知识,提高模型的稳定性,又通过模型更新提高模型的泛化性;

(3)使用模型增量学习技术,不断更新模型,适应市场行情数据的流动性;

(4)对单个特征进行分布漂移分析,从而找到时序数据分布漂移的根源;

(5)特征筛选方面,使用树模特征筛选功能/分组筛选技术,从数百特征中筛出真正work的特征,避免了模型过拟合现象,加快了运行速度。

(6)其中有几个方案未体现滚动训练/在线学习的思想,时序领域极有可能出现未来数据分布与已有数据分布不一致的情况,没有加入rolling训练在未来数据测试时shake的风险极高,除非特征+模型鲁棒性足够稳定。所以,时序领域,建议加入滚动训练/在线学习的思想。

  • 31
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值