Market Royale
Complete Strategy Notes
From Zero to Trading Bot — Everything Explained
IEEE Algorithmic Trading Competition
Commodities: Oil • Wood • Brick • Steel • Grain
Part 1 — What Is Trading? (The Basics)
1.1 What Does It Mean to Trade?
Trading means buying and selling something — stocks, commodities, currencies — with the goal of
making a profit. You make money by buying low and selling high, or by selling high first and buying
back at a lower price later.
In this competition, the assets are physical commodities: Oil, Wood, Brick, Steel, and Grain. The
simulation mimics how a real commodity exchange works.
1.2 Key Terms You Must Know
Term What It Means
Price / Midprice The current fair value of one unit of a commodity. Midprice = average
of best bid and best ask.
Best Bid The highest price a buyer is willing to pay right now.
Best Ask The lowest price a seller is willing to accept right now.
Spread The gap between best ask and best bid. Bigger spread = less liquid
market.
Long Position You BOUGHT units. You profit when price goes UP. You own
something.
Short Position You SOLD units you didn’t own (borrowed). You profit when price
goes DOWN. You owe something.
Flat / Neutral You hold zero units. No exposure to price moves.
PnL Profit and Loss. How much money you’ve made or lost.
Portfolio Value Cash + (units held × current price) for all commodities.
VWAP Volume Weighted Average Price. Final value is based on VWAP of
the last 30 minutes of Day 2.
1.3 How You Make Money
Going Long (Buying first): You buy 5 units of Oil at ₹100. Price rises to ₹115. You sell all 5 units.
Profit = 5 × ₹15 = ₹75.
Going Short (Selling first): You sell 5 units of Steel at ₹200 (units you don’t own yet). Price drops
to ₹180. You buy back 5 units. Profit = 5 × ₹20 = ₹100.
Short selling sounds strange but it’s completely standard. Think of it as: you borrow 5
mangoes from a friend, sell them today at ₹80, then buy 5 back at ₹60 tomorrow, return
them, and keep the ₹20 difference.
1.4 Fees and Taxes — Why They Matter
Every trade in this competition incurs a cost:
• Trading Tax: charged on every trade regardless of price.
• Dynamic Fee: extra charge when you trade far from the current market price.
• Why this matters: if you trade too frequently on small signals, fees eat your profits. Our
strategy includes a cooldown and volatility filter specifically to avoid this.
Part 2 — The Data We Work With
2.1 What is an Observation?
Every time the framework calls our bot, it passes a dictionary of Observation objects — one per
commodity. Each Observation contains a snapshot of the market at that exact moment.
Field Meaning
[Link] Current fair price of the commodity. This is what we use for all
calculations.
obs.best_bid Highest price someone is willing to buy at right now.
obs.best_ask Lowest price someone is willing to sell at right now.
[Link] A signal: -1 (bad news), 0 (no news), +1 (good news).
[Link] is the most important field. It’s the price we use to compute z-scores, moving
averages, and trends.
2.2 What Is a ‘Tick’?
A tick is one moment in time — one call to act(). Think of it like one frame in a video. Every tick, the
market updates prices and we decide whether to buy, sell, or do nothing.
Our bot has a global tick counter that increments by 1 every time act() is called. This is used to
enforce cooldowns between trades.
2.3 What Is a Rolling Window?
We cannot remember every single price since the beginning of time — that would be too much
data. Instead, we keep a “rolling window”: a fixed-size list that only holds the most recent N prices.
When a new price arrives and the window is full, the oldest price automatically drops off the left
end. We use Python’s deque(maxlen=30) for this.
Example — window of size 5:
Prices so far: [100, 102, 101, 99, 103]
New price 104 arrives → [102, 101, 99, 103, 104] (100 dropped)
This ensures our statistics always reflect the most recent market behaviour.
Part 3 — The Maths Behind the Strategy
3.1 Mean (Average Price)
The mean is the sum of all prices in our window divided by the count. It answers: “What is the
typical price over the last N ticks?”
Formula:
mean = (p1 + p2 + p3 + … + pN) / N
Example: prices = [98, 100, 102, 101, 99] → mean = 500 / 5 = 100
We compute two means in our strategy:
• Long-term mean (30 prices): the slow, stable average. Represents where the price
‘usually’ sits.
• Short-term mean (10 prices): the fast, reactive average. Responds quickly to recent
moves.
3.2 Standard Deviation
Standard deviation (std) measures how spread out the prices are around the mean. It answers:
“How much does the price typically move around?”
• Small std: prices are tightly clustered. Market is calm and flat.
• Large std: prices are all over the place. Market is volatile and moving.
How it’s calculated (step by step):
Step 1: Find the mean of the prices.
Step 2: For each price, compute (price − mean)² ← squared distance from mean.
Step 3: Average all those squared distances → this is the Variance.
Step 4: Take the square root of the variance → this is the Standard Deviation.
In the code:
variance = sum((p - mean)**2 for p in prices) / len(prices)
std = variance ** 0.5
Our strategy skips trading when std < 0.01 (MIN_STD). If the market is flat, there’s no
movement to exploit, and fees would guarantee a loss.
3.3 Z-Score — The Core Signal
The z-score is the most important number in our strategy. It answers:
“How unusual is the current price compared to recent history?”
Formula:
z_score = (current_price − mean) / std
Z-Score Value What It Means
z=0 Price is exactly at the mean. Perfectly normal.
z = +1 Price is 1 std above mean. Slightly elevated.
z = +2 or higher Price is very high vs recent history. Likely to fall back. → SELL
signal.
z = -1 Price is 1 std below mean. Slightly low.
z = -2 or lower Price is very low vs recent history. Likely to rise back. → BUY signal.
In normal statistics, only about 5% of values fall beyond ±2 standard deviations. This is why a z-
score beyond ±1.8 is our trigger — it’s statistically rare and suggests the price has deviated more
than usual.
3.4 Moving Averages and Trend
We compute two rolling averages simultaneously:
• Short MA (10 ticks): reacts quickly to recent price changes.
• Long MA (30 ticks): moves slowly. Represents the broader baseline.
The trend is computed as:
trend = (short_mean - long_mean) / long_mean
Trend Value Interpretation
trend > +0.002 Short MA is 0.2% above Long MA. Recent prices are rising faster
than usual → Upward momentum → BUY.
trend < -0.002 Short MA is 0.2% below Long MA. Recent prices are falling →
Downward momentum → SELL.
Between ±0.002 No significant trend. Skip trend-following. Fall through to mean
reversion.
Think of it like two runners: the short MA is a sprinter who reacts immediately. The long MA
is a marathon runner who keeps a steady pace. When the sprinter is clearly ahead of the
marathon runner, the whole group is accelerating — that’s a trend.
Part 4 — The Four Strategies Combined
4.1 Mean Reversion
The core idea: prices don’t stay extreme forever. They tend to revert to their average over time.
This is statistically true for most commodity markets.
Signal Action Logic
z < -1.8 BUY Price is unusually low. Bet it rises back to mean.
z > +1.8 SELL Price is unusually high. Bet it falls back to mean.
|z| < 0.3 EXIT Price is back near average. Take profit and close
position.
4.2 Trend Following
The opposite philosophy: when prices are consistently moving in one direction, keep riding that
wave rather than fighting it. Trend following and mean reversion are complementary — mean
reversion works in stable markets, trend following works in momentum markets.
Signal Action Logic
trend > +0.002 BUY Short MA above long MA → price is rising → ride
upward momentum.
trend < -0.002 SELL Short MA below long MA → price is falling → ride
downward momentum.
Trend following runs BEFORE mean reversion in our decision logic. If a trend is detected,
we skip the z-score check entirely. This prevents contradictory signals (e.g. z-score says buy
but trend says sell).
4.3 Volatility Filter
We refuse to trade when std < MIN_STD (0.01). In a flat market:
• There is no statistical edge — prices aren’t deviating enough to exploit.
• Any trade we make will incur fees but produce negligible profit.
• Z-scores become meaningless because the denominator (std) is near zero.
4.4 News Exploitation
News events ([Link] = +1 or -1) are the highest-priority signal. When news fires:
• +1 (Good News): Close any short position immediately. Then buy up to NEWS_QTY (5
units). Price expected to spike.
• -1 (Bad News): Close any long position immediately. Then sell up to NEWS_QTY (5 units).
Price expected to drop.
News completely overrides everything else. No z-score check, no trend check, no cooldown.
News is the only signal we treat as immediate, unconditional truth.
Part 5 — Risk Management
5.1 Position Limits
We never hold more than MAX_POSITION = 8 units long or short on any single commodity. This
caps our maximum loss on any one trade.
# Before buying, check there is room:
room = MAX_POSITION - current_position
qty = min(desired_qty, room)
5.2 Cooldown Between Trades
After placing any trade, we wait COOLDOWN_TICKS = 3 ticks before trading the same commodity
again. This prevents:
• Fee drag from over-trading
• Chasing rapidly oscillating prices
• Duplicate orders being sent on consecutive ticks
5.3 Dynamic Position Sizing
Not all signals are equally strong. We trade larger when signals are more extreme:
Signal Strength Trade Size
Moderate BASE_QTY = 2 units
Strong STRONG_QTY = 4 units
News event NEWS_QTY = 5 units
5.4 Exit Before Entry
Every tick, we check exits BEFORE we look for new entries. This is critical because:
• Prevents opening a new position the same tick we close an old one.
• Locks in profits as soon as the mean-reversion has played out.
• Avoids being trapped in a position longer than necessary.
Part 6 — Full Decision Flow (Every Tick)
For every commodity, every tick, this is exactly what the bot does in order:
Step Question Asked If Yes If No
1 Is midprice available? Continue Skip commodity
2 Is there news (+1 or -1)? Trade on news, skip all Move to step 3
other steps
3 Do we have enough price Continue Skip (still warming up)
history (30 ticks)?
4 Is volatility too low (std < Skip Continue
0.01)?
5 Should we EXIT an existing Exit, skip to next Continue
position? commodity
6 Are we in cooldown? Skip Continue
7 Is there a strong trend? Trade in trend direction Move to step 8
8 Is z-score extreme enough Mean-reversion trade Do nothing
(±1.8)?
The order of steps matters enormously. News beats trend. Trend beats mean reversion. Exit
beats entry. Each layer is a gate — once a decision is made, the bot skips all lower-priority
logic.
Part 7 — Every Parameter Explained
These are the knobs you can turn to change how the strategy behaves. This is where you can give
inputs to improve the strategy.
Parameter Default What Increasing It Does What Decreasing It Does
Value
SHORT_WINDOW 10 Slower reaction to recent Faster, noisier trend signal
moves
LONG_WINDOW 30 Smoother, slower baseline Baseline reacts faster (less
stable)
Z_WINDOW 25 More data for z-score Less data (more reactive)
(smoother)
Z_ENTRY_STRON 1.8 Fewer trades, only most More trades, weaker signals
G extreme deviations
Z_EXIT 0.3 Hold positions longer, target Exit faster, smaller profits per
deeper reversion trade
MIN_STD 0.01 Requires more volatility to Trades even in flat markets
trade (risky)
MAX_POSITION 8 Can hold more units (higher Smaller exposure (safer)
reward + risk)
BASE_QTY 2 Larger baseline trades (more Smaller baseline trades
fees)
STRONG_QTY 4 Bets harder on strong signals More conservative on strong
signals
NEWS_QTY 5 Bigger news bets Smaller news bets
TREND_THRESHO 0.002 Fewer trend signals, stronger More trend signals, weaker
LD required trigger
COOLDOWN_TICK 3 Less trading, lower fees, More trading, higher fees,
S slower reaction faster
Part 8 — Bugs Fixed From the Original Template
Bug 1 — Inverted News Logic
The original code closed a LONG position on positive news and closed a SHORT on negative
news. This is completely backwards.
• Original wrong logic: Positive news → sell your longs (miss the spike). Negative news →
buy back shorts (miss the drop).
• Fixed logic: Positive news → close any shorts, then BUY. Negative news → close any
longs, then SELL.
Bug 2 — [Link] Treated as a Dictionary
The original code called [Link](commodity, 0) as if [Link] were a dict. But based on the
framework spec, [Link] is a scalar value per commodity observation. This would crash at
runtime with an AttributeError.
# WRONG (original):
news = [Link](commodity, 0)
# FIXED:
news = [Link] if isinstance([Link], (int, float)) else 0
Bug 3 — Wrong Short Exit Condition
Original code exited a short when z < z_exit (price still below mean). That means it was cutting
profitable shorts early, before the price had actually recovered. The correct condition for exiting a
short is when z rises back above -Z_EXIT (price reverted toward mean from below).
Bug 4 — Zero Trades (Empty Template)
The template had only a pass statement inside the loop. It would always return an empty order list.
Zero trades = zero PnL. The entire strategy above was built to fix this.
Part 9 — How You Can Improve the Strategy
9.1 Parameter Tuning (Low Risk Changes)
These changes only adjust numbers, not logic. They’re safe to experiment with between Day 1 and
Day 2.
• If bot barely trades: lower Z_ENTRY_STRONG (e.g. 1.5), lower TREND_THRESHOLD
(e.g. 0.001), reduce COOLDOWN_TICKS (e.g. 2).
• If bot overtrading / losing to fees: raise Z_ENTRY_STRONG (e.g. 2.2), raise
COOLDOWN_TICKS (e.g. 5), raise MIN_STD (e.g. 0.02).
• If bot is too cautious on news: raise NEWS_QTY (e.g. 7), raise MAX_POSITION (e.g.
10).
9.2 Logic Improvements (Medium Risk)
• Time-of-day awareness: near the end of Day 2, VWAP of the last 30 minutes determines
final value. You could shift to holding winning positions rather than exiting them as the
deadline approaches.
• Per-commodity tuning: Oil might be more volatile than Grain. You could set different
Z_ENTRY thresholds per commodity based on Day 1 observations.
• News persistence: right now we only trade on news when it fires. You could hold the news-
driven position for a few extra ticks to capture the full price move.
9.3 What to Look for in the CSV Files
After every backtest, download and check these things in your CSV:
• Which commodities traded the most? (overtrading warning if one dominates)
• What was the average hold time per trade? (too short = fee drag)
• Did any news events fire and did the bot respond? (check timestamps)
• Were exits happening at profit or loss? (if mostly loss, z_exit threshold may be too tight)
9.4 What NOT to Do
Remember: backtest data ≠ live data.
Do not fine-tune parameters obsessively to maximise the backtest score. A strategy that
scores perfectly on the practice data may fail on live data. Prefer robust, general settings
over precisely optimised ones.
Part 10 — Quick Reference Glossary
Term Plain English Definition
Mean / Average Sum of all values divided by count. The ‘centre’ of the data.
Standard Deviation (std) How spread out prices are. Small = calm. Large = volatile.
Z-Score How many std deviations the current price is from the mean. ±2 is
extreme.
Rolling Window Only keep the last N values. Old data drops off automatically.
Long You own units. You profit if price rises.
Short You owe units. You profit if price falls.
Mean Reversion Strategy: extreme prices will return to average. Buy low, sell high.
Trend Following Strategy: momentum persists. Ride the direction the market is
moving.
Volatility Filter Skip trading when markets are too flat. No movement = no edge.
News Override Drop all analysis when news fires. React immediately.
Dynamic Sizing Trade bigger when signals are stronger. Scale down on weak
signals.
Position Limit Maximum units held at once. Caps risk per commodity.
Cooldown Wait N ticks after a trade before trading the same commodity again.
VWAP Volume Weighted Average Price. Used to determine final
competition score.
PnL Profit and Loss. Your running score.
Tick One moment in time. One call to the act() function.
Warmup Period The first 30 ticks. We collect data but do not trade yet.
Overfit Tuned perfectly for practice data but fails on real data. Avoid this.
Spread Gap between best ask and best bid. Wide spread = expensive to
trade.