# prosperity3 **Repository Path**: yiyan-duck/prosperity3 ## Basic Information - **Project Name**: prosperity3 - **Description**: 141/12620 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-04-06 - **Last Updated**: 2025-06-04 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # IMC-prosperity3 ## 比赛说明 这是IMC举办的一个为期15天的交易比赛。总共有五轮,每轮三天。从北京时间晚上七点开始新的一轮比赛,结束前一轮比赛。每一轮包含算法交易和手动交易。官方会给出近三天的商品交易数据,你要通过分析数据写出一个策略来尽可能在评测时获得更多的钱。每一轮可交易的商品是会变多的。手动交易就有点类似一些面试题,涉及到博弈论和运筹学。这个比赛是有discord讨论群的,请务必要进群!!因为官方给的赛题信息可能不够,你需要跟大家讨论。另外,官方可能会传错数据、更新文档什么的。你不看群那就吃亏了! ## 赛前准备 先看看官方文档熟悉一下框架datamodel,准备好回测器。关于工具的使用可以看[回测器的使用方法](tools-direction.md)。 官方文档地址:https://imc-prosperity.notion.site/Prosperity-3-Wiki-19ee8453a09380529731c4e6fb697ea4 ## 赛题 ### Turial round 这是热身赛,建议大家通过完成这一轮赛题来熟悉框架和比赛操作。我没做,,但是很多队伍都做了,他们在discord上讨论得如火如荼。 ### Round1 有三种商品,Rainforest Resin、Kelp、Squid Ink。他们的仓位限制都是50。其中RESIN的公允价值是10000,KELP的价格是上下波动的,INK的波动是具有某种模式的。 * RESIN:这个公允价值知道了也就是说不同时间的价格是围绕着公允价值变化的,所以只要管理好仓位就能赚钱。我们用的是网格策略。 * INK:我是真的识别不出他的模式啊!!我以为他会跟KELP有点关系,什么滞后啊相关啊。看图看半天看不出个所以然。后面我们弄了个趋势判断。先确定窗口长度,然后收集INK的中间价格数据,算出这个窗口数据里的最新数据和最旧数据的价差。通过价差我们能大概知道他的价格走势,再进行买卖。单单就只有价差其实这种方法不太稳固,所以我们加入了决定买卖的价格阈值,当ink价格<最低价+极差的20%时买入。当ink>最低价+极差的20%时卖出。 * KELP:一开始用的方法跟INK一样,后面我发现其实KELP是有均值回归趋势的。因此离第一轮结束还有一个半小时我写了个移动平均策略,先收集窗口数据算出某段时间的均值,再用最新价格跟均值比较。欸!这个效果好了一点。但是这个策略缺点是仓位很容易积累一些不良订单。 ### Round2 第二轮引进了PICNIC_BASKET1,CROISSANTS,JAMS,DJEMBES,PICNIC_BASKET2。PICNIC_BASKET1和PICNIC_BASKET2是其余三种商品的线性组合。这就跟ETF有点类似,不过这里应该不能转换。(官方文档没说清楚) * PICNIC_BASKET1和三种成分商品:用PICNIC_BASKET1价格减去成分商品组合价格得到价差,再画出价差的趋势图,对价差进行均值回归检验(单位根检验、Hurst指数)。最终发现确实是有均值回归特征的,因此构造Z-score,用Z-score跟阈值比较。Z-score大于某个阈值说明PICNIC_BASKET1的价格比较高,而成分商品的价格比较低。因此可以卖PICNIC_BASKET1而买成分商品。 * PICNIC_BASKET2和三种成分商品:跟PICNIC_BASKET1用的一样的分析方法,但是价差并不具有均值回归特征,因此转而分析滞后性。因为PICNIC_BASKET2和成分商品组合不平稳,先做了一阶差分处理,再用VAR模型去拟合。通过AIC准则确定最优的滞后阶数是4,那么就通过滞后四期的成分商品组合差分价格的系数与对应差分价格相乘求和得知当期的PICNIC_BASKET2的差分价格。通过差分价格是正的还是负的就能确定其涨跌趋势。但是之后通过对比真实的差分价格和预测的差分价格发现预测的准确率只有46%。于是选择放弃研究滞后性。 ### Round3 这一轮引进VOLCANIC ROCK和五种到期时间一致但是行权价不一样的VOLCANIC ROCK VOUCHERS。其实就是标的物和期权。 * 想法是通过使得BS期权定价模型算出的期权理论价格等于市场价格来算出隐含波动率IV,如果IV偏离市场长期水平,则说明期权价格高估了,要卖出。因为IV其实可以看作是市场投资者对未来波动率的一种预测。当IV大于历史长期水平可以说明未来波动率会很大。即可能会发生一些极端事件,影响市场。这时候大家会买入期权来对冲风险。所以期权价格会被推高,这时要做空。另外在对期权进行买卖的同时要管理标的物价格变动对期权价格的风险,因此要进行delta对冲,使得delta尽可能地接近0。因为对冲的时候发现标的物亏了很多钱,因此不进行对冲了。后面改用ink的策略来交易VOLCANIC ROCK。 ### Round4 引进了奢侈品MAGNIFICENT_MACARONS。这个马卡龙的价格是取决于各种可观察的因素,如日照时间、糖价、运输成本、进出口关税和合适的储存空间。其中每次可转换的马卡龙为10个。这里的的转换其实是跟另一个小岛做交易。 #### 吃单逻辑 对MAGNIFICENT_MACARONS和日照指数、糖的价格做了相关性分析,最后得出结果是相关系数很小。后面官方提示马卡龙和糖的价格会受阳光指数的影响。当日照指数小于临界日照指数(CSI)时,马卡龙和糖价格会大幅上涨且相关性很强。而当日照指数高于CSI时,马卡龙和糖价格倾向于围绕各自的公允价值交易。 由于时间比较紧,我通过肉眼大概确定CSI的值。通过将当前的日照指数和CSI比较,如果是小于,则买入马卡龙。最后检查当前的仓位是多头还是空头,多头的话就逐步平仓 #### 做单逻辑 做单最重要的就是确定挂单的买卖价格,所以先引进三种价格。 1. **隐含价格**(implied_price),指与A岛交易商品可能发生的费用,也是在本地市场挂单时需要参考的价格。例如隐含卖价(implied_ask_price),是在A岛上买入马卡龙的成本,当我要卖出马卡龙时就得用比隐含卖价更高的价格卖出。以下是公式说明: * implied_bid_price = 观察bid价 - 出口税 - 运输费 * implied_ask_price = 观察ask价 + 进口税 + 运输费 为什么要考虑隐含价格呢?换个说法是为什么要执着于考虑在A岛交易的成本呢?因为隐含价格可以看作是一个基准价格。为了在本地市场和A岛中获利,我会在本地市场做交易时考虑价格比隐含价格更优的市场订单。考虑一个具体实例:当我们已知从A岛买入马卡龙的成本即隐含卖价,那么我们就要找比隐含卖价更高的买单价格,以更高的价格卖出,从中获利。这个过程可以总结为A岛买入+本地市场高价卖出。同样地,我们也可以在A岛卖出+本地市场低价买入,匹配价格比隐含买价更低的卖单。 2. 当我们拿隐含价格匹配订单时,我希望得到的利润在我所能接受的范围之内。因此在这里定义了一个**包含期望利润的隐含价格**(adjusted_implied_price),这里引进 Δ 作为期望利润调整值。以下是具体的公式: * adjusted_implied_bid_price = implied_bid_price - Δ * adjusted_implied_ask_price = implied_ask_price + Δ 其中 Δ >0,取值由你想得到的最低利润决定。 3. **具有竞争力的价格**(aggressive_price),指基于本地市场最优bid、ask价格的中间价(mid_price)生成的博弈价格,用于提升订单的优先级。这里引进 δ 作为安全调整值。 * mid_price = (最优bid价 + 最优ask价)/2 * aggressive_bid_price = mid_price + δ * aggressive_ask_price = mid_price - δ * aggressive_bid_price <= implied_bid_price * aggressive_ask_price >= implied_ask_price 其中 δ 大于等于0,取值取决于对数据的观察。如果市场上最优买价和最优卖价的差值较小,那么 δ 取值也比较小,使得aggressive_bid_price比最优买价高一点,aggressive_ask_price比最优卖价低一点。反之亦然。 在计算出以上三种价格后,挂单价格就可以确定下来了: * 买入挂单价格:min(adjusted_implied_bid_price,aggressive_bid_price) * 卖出挂单价格:max(adjusted_implied_ask_price,aggressive_ask_price) 注意用以上价格挂单时不一定能保证订单成交,因为有时候市场上并没有合适的价格。 在订单成交之后,我们可以得到当前的净头寸(position)。下一步会进行转换操作来平仓。因为题目限定每次可转换的数量不超过10,此时可转换的数量为max(-10, min(10, -position))。当然我们也可以选择不转换,即平仓的时候考虑本地市场的价格跟A岛市场的观察价格,如果本地市场的价格更优则在本地市场上挂单,这时转换数量为0. #### 问题 1. 为什么aggressive_price是用的中间价格计算的而adjusted_implied_price是用的隐含价格呢? 因为设计aggressive_price是为了能够更快成交,而adjusted_implied_price是为了不亏钱,因此要分别考虑市场中间价格和成本(隐含价格)。 首先中间价格是指本地最优价格的中间价格,当我对这个价格做一个调整即±δ时就能得到aggressive_price。那在本地市场上这个价格的成交优先级会比较高,因为交易对手在交易时会先筛选出最优价格排序的订单。 而隐含价格是包含了额外费用的,是我不想亏钱,所以我得先将所有可能发生的额外费用都包含进去,对隐含价格做一个调整即±Δ得到adjusted_implied_price去匹配订单,确保做转换操作时我不会亏太多钱。因为转换的时候是平仓了嘛,平仓会产生额外费用的,如果我之前是用隐含价格去建仓的话,在小岛的价格不变的情况下这个价格是至少不亏的。 ### Round5 最后一轮没有引进新的商品,而是引进一个OwnTrade类。官方给出关于一堆昆虫的自我描述,里面还提到C罗。按道理来说这一轮应该是披露我们的交易对手是谁,然后要通过他们的特征来调整自身的策略。但是我们查看了交易数据,里面涉及到的交易对手跟官方给出的昆虫根本对不上!我们在discord上也没有找到解决方案。因此第五轮是在review之前写的代码。 * INK和VOLCANIC ROCK:用完整的数据画出他们的趋势图和密度函数图,调整买卖阈值。最后改成了当价格<最低价+极差的40%时买入。当价格>最低价+极差的60%时卖出。后面在看回测结果时发现通过价差判断趋势这个指标不太可靠,因为你不能保证这个价差趋势是会一直持续下去,万一那只是短时间回调呢?所以为了防止错失一些价格比较好的订单,我删掉这个判断,单单用价格阈值来确定买卖。 * KELP:分析趋势图发现其实他的波动并不大,很多时候趋势是一条水平线。因此改成用网格法来套利。