目录
前言
基于集合竞价开始以来经过 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
- 特征构造:Big Common Feature Improvement by Simple Changes (kaggle.com)。
- 特征筛选:方法(1)根据CAT等树模评估特征重要性,通过select_features()筛选Top N特征。select_features - CatBoost | CatBoost。方法(2)分组测试——>逐个测试,保留提升效果最好的Top N(详情见3.2部分)。
- 解决NN Shake问题:在线学习/增量学习/滚动训练+模型集成(NN+XGB+CAT)+NN dropout 0.6。
- 解决Shake问题:“在线/最新数据”和“离线/历史数据”学习混合。模型集成(3*LGB、1*XGB离线训练+1*LGB滚动训练/增量学习)。3*LGB+1*XGB离线训练保证模型稳定性、保留通用知识;加入1个滚动训练/增量学习的LGB,实时更新模型,增强模型的泛化性。
-
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']
-
减少内存、加快训练速度: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
-
特征分布漂移分析:对特征逐个进行分布漂移分析,对分布漂移比较严重的特征进行剔除/nan值等填充。
三、思考
在量化领域,开源方案特征工程比较常规,基于领域经验的规则特征、常规统计特征(mean、std、max、min)、横截面特征(偏差特征、rank特征等等)、特征缩放、特征直接的算子计算(加减乘除)等等。在比赛中比较fancy的是:
(1)zero_sum标签平衡技术,可以消除整体市场偏差、提高跨股票的可比性、提高风险管理能力、提高策略的稳健性、避免过度拟合、提高预测的解释性等等;
(2)离线模型、在线模型/模型rolling/增量学习混合,“在线/最新数据”和“离线/历史数据”学习混合,既保持了模型的通用知识,提高模型的稳定性,又通过模型更新提高模型的泛化性;
(3)使用模型增量学习技术,不断更新模型,适应市场行情数据的流动性;
(4)对单个特征进行分布漂移分析,从而找到时序数据分布漂移的根源;
(5)特征筛选方面,使用树模特征筛选功能/分组筛选技术,从数百特征中筛出真正work的特征,避免了模型过拟合现象,加快了运行速度。
(6)其中有几个方案未体现滚动训练/在线学习的思想,时序领域极有可能出现未来数据分布与已有数据分布不一致的情况,没有加入rolling训练在未来数据测试时shake的风险极高,除非特征+模型鲁棒性足够稳定。所以,时序领域,建议加入滚动训练/在线学习的思想。
标签:Optiver,df,模型,Kaggle,特征,test,Trading,np,col From: https://blog.csdn.net/qq_51175703/article/details/137109118