Day 59 — Static Slippage: Forcing Unfavorable Fills by N Ticks
“Slippage isn’t a rounding error. It’s the market taking a fee you forgot to budget for — and collecting it on every single fill.”
Before You Start
What you need:
Python 3.11 or higher
An Alpaca paper trading account (free at alpaca.markets)
A terminal you’re comfortable using
Install dependencies:
pip install alpaca-py rich numpy pandas pytest
Set your Alpaca paper credentials (these are not your real money credentials — paper trading is a sandbox):
export ALPACA_API_KEY="your_paper_key"
export ALPACA_SECRET_KEY="your_paper_secret"
If you don’t have credentials yet, that’s fine. The demo runs in dry-run mode without them, and you can complete all four mandatory pass checks locally.
Part 1 — The Problem
The “Flat BPS” Trap
Here’s what a junior engineer writes after reading their first backtest tutorial:
# The career-ending pattern
entry_price = bar.close
slippage_cost = entry_price * 0.0005 # "0.5 bps, close enough"
adjusted_pnl = raw_pnl - slippage_cost
This is wrong in four distinct ways, each capable of causing real capital loss:
Direction blindness. When you BUY, slippage pushes your fill price up. When you SELL, it pushes it down. Applying a symmetric P&L deduction means your short positions are overstated — the system believes it sold at a better price than it did.
No tick granularity. Markets move in discrete increments: $0.01 for US equities, $0.25 for ES futures.
price * 0.0005gives$150.075001. That price does not exist. You’ve modeled a fiction that no exchange will ever execute.Post-hoc application. Applying slippage to P&L after the fact masks the entry price your system used for sizing, stop placement, and risk management. Your position sizer fires using the clean price. Your stops are set from the clean price. Only the P&L summary gets penalized — live, you will size and stop incorrectly.
No live integration path. A scalar subtracted from a DataFrame column has no correspondent in an actual order submission. Alpaca has no
slippage=0.0005parameter. You must compute the adversely adjusted limit price before you submit the order.
The Failure Mode
The slow bleed: Your backtest shows 18% annual return after “slippage.” You go live. Fills come back 2–4 ticks worse on every trade because you’re using marketable limit orders during moderate volatility. Actual return collapses to 6%. You spent six months backtesting a fantasy.
The stop-ruin scenario: You run a mean-reversion strategy with 3-tick stops. Your backtest used clean closes for entry. Live, your effective entry is already 2 ticks adversely slipped — your real stop distance is 1 tick. Noise stops you out 80% of the time.
The technical root cause: Floating-point arithmetic applied to prices instead of integer tick arithmetic.
# Float: wrong
float(185.42) + 3 * float(0.01) # → 185.45000000000002
# Decimal: correct
Decimal('185.42') + 3 * Decimal('0.01') # → 185.45
Across 50,000 fills, float accumulation generates meaningful P&L noise and misplaced stops. Prices must be modeled as integer tick counts, manipulated as integers, then converted back. This is non-negotiable.



