SkillAgentSearch skills...

Imc Prosperity 4

JaneRT IMC Prosperity 4 (19th globally, 7th USA)

Install / Use

npx skills add heyman7913/imc-prosperity-4

Installs into whichever agent you are using.

README

<img width="3420" height="1950" alt="image" src="algorithmic/notebooks/images/prosperity cover.png" /> <br> <div align="center">

| Round | Algo XIRECS | Manual XIRECS | Cumulative | Overall Rank | |:-----:|:-----------:|:-------------:|:----------:|:------------:| | 1 | 96,624 | 87,995 | 184,619 | 935 | | 2 | 91,356 | 24,233 | 115,589 | 3000 | | 3 | 226,474 | 74,473 | 300,947 | 48 | | 4 | 186,940 | 23,566 | 210,506 | 39 | | 5 | 388,174 | 93,786 | 481,960 | 19 | | Final | 801,588 | 191,825 | 993,413 | 19 |

</div>

⭐ the repo, so you remember to check this out again for IMC Prosperity 5

Team

<div align="center"> <table> <tbody> <tr> <td align="center" valign="top" width="200px"> <a href="https://www.linkedin.com/in/himanshchitkara/"> <img src="https://github.com/heyman7913.png" width="130" style="border-radius:50%" alt="Himansh Chitkara"/> <br/><br/> <b>Himansh Chitkara</b> </a><br/> <a href="https://github.com/heyman7913">@heyman7913</a> </td> <td align="center" valign="top" width="200px"> <a href="https://www.linkedin.com/in/jeetdekivadia/"> <img src="https://github.com/jeet-dekivadia.png" width="130" style="border-radius:50%" alt="Jeet Dekivadia"/> <br/><br/> <b>Jeet Dekivadia</b> </a><br/> <a href="https://github.com/jeet-dekivadia">@jeet-dekivadia</a> </td> <td align="center" valign="top" width="200px"> <a href="https://www.linkedin.com/in/ujaan-rakshit/"> <img src="https://github.com/UjaanRakshit.png" width="130" style="border-radius:50%" alt="Ujaan Rakshit"/> <br/><br/> <b>Ujaan Rakshit</b> </a><br/> <a href="https://github.com/UjaanRakshit">@UjaanRakshit</a> </td> </tr> </tbody> </table> </div>

IMC Prosperity is a two-week algorithmic trading competition run by IMC. We're JaneRT from Georgia Tech and this is our full writeup for Prosperity 4, where we finished 19th globally (7th in USA) out of 18,000+ teams. The final ranking was determined for Rounds 3, 4, and 5 in Prosperity 4.

Every round is covered: what the products were, how we figured out what was driving them, what we built, and what we'd change. The notebooks and code are all here if you want to dig in.

Table of Contents

Tools

<details> <summary><b>Backtester and Visualizer</b></summary> <br>

Backtester

We used GeyzsoN's Rust backtester, built specifically for Prosperity 4. Once strategies got complex (multi-product state tracking, options pricing), the standard Python backtester was too slow to iterate on. In Round 5 with 50 products, we ran 20 backtests in the time the Python version completed 2.

git clone https://github.com/GeyzsoN/prosperity_rust_backtester.git
cd prosperity_rust_backtester
cp /path/to/your_trader.py traders/latest_trader.py
rust_backtester --products full

One caveat: the official Prosperity backtester uses a different random seed from local tools, so a strategy that looks clean locally can behave differently in production. We learned this in Round 2. Always do a final validation run on the official site before submitting.

Visualizer

We built this ourselves during the competition. It's one of the most useful things in this repo and we'd strongly recommend using it.

python visualizer/visualize.py
# Opens at http://127.0.0.1:8766

The visualizer turns raw metrics.json and submission.log files into a full browser workspace: PnL curves, per-product attribution, fill inspection, spread context, drawdown tracking, and side-by-side run comparison. We built it from scratch mid-competition after getting burned by misleading final PnL numbers, and used it every round after that.

Final PnL alone doesn't tell you whether you made money steadily or in one late spike. It doesn't tell you which product is dragging, whether your fills are clean, or whether a good-looking backtest was just lucky.

A concrete example from Round 3: a backtest came back with a strong number. The visualizer showed 80% of the PnL came in the last 500 ticks, we were underwater for the first 7,000 ticks, and fill edge on VELVETFRUIT was negative all morning. We'd been sitting in a bad position all day and got bailed out by a late reversal. We found the bug, fixed the z-score direction, and submitted a better strategy. Without it, that bad version ships.

Comparison mode was especially useful. When two candidate strategies had similar final scores, overlaying their PnL curves, drawdown profiles, and per-product contribution made the actual difference obvious in seconds. Stop evaluating backtests by their last number alone.

python visualizer/visualize.py                         # opens http://127.0.0.1:8766
python visualizer/visualize.py --no-browser            # headless
python visualizer/visualize.py --port 8777             # different port
python visualizer/visualize.py --runs-dir "./runs"     # custom run directory
</details>

Algorithmic Trading

<a id="round-1-market-making-and-drift"></a>

<details> <summary><b>Round 1: Market Making and Drift</b></summary> <br>

Two products, two completely different price processes: one stationary, one trending. Figuring out which is which before writing any code.

Products: ASH_COATED_OSMIUM, INTARIAN_PEPPER_ROOT | Position limit: 80 per product

Code: round_1_trader.py | Notebook: round_1.ipynb

Round 1 gave us two products and enough time to understand what we were looking at before writing anything. The right first question isn't "what algorithm should I use?" It's "what is generating this price?" Once you answer that, the strategy follows.

ASH_COATED_OSMIUM

ASH sits in a tight horizontal band around 10,000, never deviating more than ~15 ticks across 30,000 rows of historical data. An Augmented Dickey-Fuller test confirmed what the chart already showed: the series is strongly stationary, p-value essentially zero. The AR(1) coefficient is strongly negative, meaning any move away from 10,000 doesn't just tend to reverse, it almost certainly reverses within a tick or two. Half-life of a deviation is under a tick. There's nothing to predict here, only a spread to collect.

ASH market structure: mid price across all days, deviation from 10,000, spread distribution

The strategy has three layers. First: large dislocations. When the deviation exceeds 6 ticks, we take aggressively up to 25 units, with higher size at larger dislocations (buckets at 6, 9, and 12 ticks). Second: when the wall midpoint disagrees with the best midpoint, signaling a genuine book imbalance. Third, accounting for 74% of ticks: passive market making at stacked quotes.

We quoted in two layers passively because of the position limit. If all 80 units sit at the inner quotes, you fill quickly, hit the limit, and go idle for hundreds of ticks. Splitting across inner (±5 ticks) and outer (±7 ticks) keeps capacity available at both levels, and the inventory skew shifting both bid and ask toward fair value as position grows prevents runaway accumulation.

500-tick microstructure window showing bid and ask cloud around 10,000 with fill markers

The biggest early mistake was not reserving enough capacity for aggressive takes. We were filling too eagerly on the passive layer and sitting idle when clear mispricings appeared. Reserving 25 units specifically for aggressive takes, regardless of passive fills, fixed it.

In hindsight

Avellaneda-Stoikov would have replaced the ad hoc inventory skew with a principled formulation. We also never analyzed the timing of taker activity. Bots crossing the spread clustered at certain times of day, and identifying that pattern would have let us size up during high-activity windows.

INTARIAN_PEPPER_ROOT

PEPPER drifted linearly upward. We fit OLS on each training day separately and found a slope of ~0.001 XIREC per timestamp, consistent across all three days. Residual standard deviation was small enough that the cost of a bad entry was trivial compared to the value of holding 80 units across a full day's drift. Every tick you're not at maximum position is opportunity cost.

The strategy: buy 80 units at the open, hold all day, sell before the daily reset. We added a day-boundary anchor check so the model resets if PEPPER opens more than 50 XIRECs from the prior day's extrapolated trend, handling occasional gap opens.

PEPPER linear drift with per-day OLS fits

![Opportunity cost of not buying at the open](algorithmic/notebooks/images/r1_chart_05.p

Related Skills

View on GitHub
GitHub Stars14
CategoryFinance
Updated1mo ago
Forks1

Languages

Jupyter Notebook

Security Score

80/100

Audited on Jun 16, 2026

No findings