471. Optiver Realized Volatility Prediction | optiver-realized-volatility-prediction
首先,非常感谢 Optiver 赞助这次比赛。这让我学到了很多关于金融和数据科学的知识,整个过程非常有趣。
大家可能不会感到惊讶,我的公开排行榜(Public LB)成绩主要归功于“数据泄露”,所以要感谢发布这个帖子(虽然后来删除了账号)的人。
我使用了与 @nyanpn 在帖子中描述的类似方法,只有一些细微的差别,比如我也重新排序了 NN 的 time-id 并从中衍生了一些特征。
不用说,这样的解决方案实际上无法在生产环境中实施,因此本文将重点介绍我学到的其他特性和技术,这些是我在其他地方没有看到提到的。
我还想说,我在数据科学和量化金融方面都相当缺乏经验,我不知道如果没有 time_id 聚合,我的 LB 排名会是多少,所以不要把这当作顶级解决方案。这些想法提高了我的 CV 和 LB 分数,但我不确定仅靠它们是否能让我进入 LB 榜首。
我的提交是基于 "lgbm baseline" 的分支开始的,感谢 @alexislyon 提供如此有帮助的学习资源。
虽然我更改了大部分特征工程,但我的最终模型仍然基于 50/50 的 LGBM 和 FFNN 集成。
大多数公开解决方案都使用 seconds_in_bucket 在 100 秒时间段内的聚合。我发现,与其直接传递这些特征,不如在这些特征上拟合一个线性回归模型,然后传递均值、斜率和误差,这样更有效:
获取输入数组的均值、斜率、误差的函数:
def get_dir_stats(arr):
mat = np.array([np.ones(len(arr)), np.arange(len(arr))]).transpose()
coefs, err, _, _ = lstsq(mat, arr)
return np.array([coefs[0] + (len(arr)/2)*coefs[1], coefs[1], err])
当我们想要分析一个特征随时间变化的行为时,我们会计算该特征在 100 秒时间段内的值,并将这些特征的数组(例如 [rv_0-100, rv_100-200, ...])传递给上述函数。
这最终使我的 CV 和 LB 提高了约 0.00100,我相信这也有助于提高可读性,进而帮助我进行特征选择。
在我的最终提交中,没有将 Stock_id 作为分类特征使用,因为我认为它不能很好地适应未来的数据。举个极端的例子,想象一下如果我们数据集中的一只股票是 GameStop,而数据集是在 2021 年之前采集的会发生什么。
我确实使用了 stock_id 聚合,但仅限于最近的 N 个 time_id。这确实需要价格去标准化,但我仍然认为即使我们只能访问每次预测的过去 time_id,这也是可行的方法。
这可能不是一个很原创的概念,但我没有在其他地方看到过讨论。我很想知道在没有 time_id 聚合的情况下,最成功的方法是什么。
我使用了带有 XGBRegressor 模型的 BorutaShap 进行特征选择。在 Kaggle 内核上运行了一整夜后,我仍然剩下大约 60 个我不确定的特征。我尝试在移除不重要的特征后重新运行,但收益递减。如果我有更多时间,我会尝试保存程序的状态并每隔 8 小时在新会话中恢复。或者,我可以尝试使用不错的 CPU 在本地运行。
有趣的是,大多数被认为不重要的特征都是特定领域的(大多数四次矩特征,很多来自 "lgbm baseline" 笔记本的交易特征等),而不是那些不断增加的聚合特征。