Backtesting Big Trades
Do large aggressive trades predict short-term price direction?
We tested big trades in crude oil (CL) and the E-mini S&P 500 (ES) from August 31 – September 4, 2026.
- CL:
- 50+ contracts · 430 trades
- ES:
- 120+ contracts · 525 trades
Moves are in each instrument's ticks: +1 means one tick up, −1 means one tick down. n is the number of trades.
What happens after a big trade?
| Side | Market | n | 10s | 30s | 1m | 5m |
|---|---|---|---|---|---|---|
| Buys | CL | 220 | -1.1 | -0.9 | -0.8 | +1.4 |
| ES | 258 | +0.4 | -0.5 | -0.5 | +1.0 | |
| Sells | CL | 210 | +0.5 | +1.3 | +1.0 | +2.3 |
| ES | 267 | +2.2 | +1.0 | +0.1 | +1.3 |
Following the aggressor did not give a consistent directional signal.
Does range location matter?
We grouped trades by their position in the preceding 30 minute range.
Buys
| Location | Market | n | 10s | 30s | 1m | 5m |
|---|---|---|---|---|---|---|
| Top 20% | CL | 123 | -0.7 | -0.4 | -0.1 | +4.3 |
| ES | 100 | 0.0 | -2.4 | -3.1 | -1.1 | |
| Middle 60% | CL | 68 | -1.9 | -2.4 | -3.1 | -3.4 |
| ES | 125 | +0.2 | +0.4 | +1.1 | +3.4 | |
| Bottom 20% | CL | 29 | -0.8 | +0.3 | +1.4 | +0.3 |
| ES | 33 | +2.3 | +2.2 | +1.3 | -2.0 |
Sells
| Location | Market | n | 10s | 30s | 1m | 5m |
|---|---|---|---|---|---|---|
| Top 20% | CL | 39 | -0.2 | +1.0 | -0.1 | +4.0 |
| ES | 59 | +1.9 | -2.7 | -4.6 | +1.1 | |
| Middle 60% | CL | 90 | +0.2 | +1.5 | +1.3 | -2.8 |
| ES | 148 | +2.1 | +1.7 | +1.5 | +1.6 | |
| Bottom 20% | CL | 81 | +1.3 | +1.3 | +1.2 | +0.8 |
| ES | 60 | +2.7 | +2.7 | +1.5 | +0.9 |
No clear pattern. Large buys do have a tendency to occur at the top of a range.
Does size matter?
We grouped trades by contract size in each market. Q1 contains the smallest qualifying trades; Q4 the largest.
Buys
| Quartile | Market | Contract range | n | 10s | 30s | 1m | 5m |
|---|---|---|---|---|---|---|---|
| Q1 | CL | 50–54 | 55 | -1.1 | +0.4 | +0.5 | +3.0 |
| ES | 120–137 | 65 | -0.2 | -1.7 | -1.3 | +0.2 | |
| Q2 | CL | 55–74 | 55 | -0.6 | -1.4 | -1.9 | -0.7 |
| ES | 138–165 | 64 | +0.6 | -0.7 | -1.6 | -0.4 | |
| Q3 | CL | 75–101 | 57 | -1.3 | -1.5 | -0.6 | +1.5 |
| ES | 166–240 | 65 | +1.6 | +0.6 | +0.3 | +0.9 | |
| Q4 | CL | 102–617 | 53 | -1.4 | -1.2 | -1.4 | +1.8 |
| ES | 241–1344 | 64 | -0.4 | 0.0 | +0.5 | +3.2 |
Sells
| Quartile | Market | Contract range | n | 10s | 30s | 1m | 5m |
|---|---|---|---|---|---|---|---|
| Q1 | CL | 50–54 | 54 | -0.1 | +0.8 | +0.8 | -1.3 |
| ES | 120–137 | 70 | +1.8 | +0.1 | -0.7 | +0.1 | |
| Q2 | CL | 55–74 | 51 | +1.3 | +2.1 | +2.4 | +4.9 |
| ES | 138–165 | 64 | +2.5 | +1.8 | +0.1 | +1.6 | |
| Q3 | CL | 75–101 | 57 | +0.1 | +1.0 | +0.9 | +3.6 |
| ES | 166–240 | 66 | +1.3 | -0.3 | -1.0 | -1.3 | |
| Q4 | CL | 102–617 | 48 | +0.9 | +1.5 | -0.2 | +1.9 |
| ES | 241–1344 | 67 | +3.1 | +2.3 | +2.1 | +4.9 |
Larger trades did not consistently produce stronger follow-through. ES's largest buys were followed by higher prices at 5 minutes, but so were its largest sells.
Does liquidity matter?
We split buys and sells in each market into quartiles by average contracts per level swept. Q1 is the thinnest liquidity; Q4 is the most concentrated.
Buys
| Quartile | Market | Avg contracts / level | Avg levels swept | 10s | 30s | 1m | 5m |
|---|---|---|---|---|---|---|---|
| Q1 | CL | 11.4 | 7.3 | -2.6 | -2.1 | -1.2 | -4.9 |
| ES | 36.3 | 6.1 | -1.6 | -3.0 | -2.8 | -2.5 | |
| Q2 | CL | 16.0 | 5.3 | -1.6 | -2.0 | -3.0 | -0.3 |
| ES | 59.7 | 3.2 | +0.3 | -1.2 | -0.5 | +2.6 | |
| Q3 | CL | 37.0 | 3.4 | -0.9 | -0.6 | -0.7 | +4.3 |
| ES | 97.9 | 2.5 | +0.9 | +0.7 | 0.0 | +1.7 | |
| Q4 | CL | 121.1 | 1.1 | +0.8 | +1.1 | +1.6 | +6.6 |
| ES | 199.3 | 1.3 | +2.0 | +1.6 | +1.3 | +2.1 |
Sells
| Quartile | Market | Avg contracts / level | Avg levels swept | 10s | 30s | 1m | 5m |
|---|---|---|---|---|---|---|---|
| Q1 | CL | 11.7 | 5.9 | +2.3 | +4.0 | +4.2 | +6.5 |
| ES | 38.4 | 7.4 | +1.5 | +1.3 | +0.8 | +1.7 | |
| Q2 | CL | 15.6 | 5.1 | +0.4 | +0.6 | +0.6 | +2.5 |
| ES | 69.1 | 2.9 | +3.4 | +1.8 | +1.7 | +1.2 | |
| Q3 | CL | 27.7 | 4.2 | +0.5 | +0.6 | -0.5 | -0.8 |
| ES | 118.3 | 1.9 | +1.7 | -0.3 | -1.3 | +1.2 | |
| Q4 | CL | 104.1 | 1.0 | -1.1 | 0.0 | -0.3 | +0.9 |
| ES | 219.7 | 1.2 | +2.2 | +1.1 | -0.7 | +1.1 |
Concentrated buys were followed by higher prices in both markets; concentrated sells did not lead to lower prices at 5 minutes.
Big buys and sells that hit thin liquidity tended to reverse. The separation was clearer in CL: ES sell quartiles all finished with similar positive 5 minute moves.
Can we trade it?
We turned the thin-liquditiy pattern into trading rules, exiting after 5 minutes with no stop or target. Both results include commission.
| Market | Round trips | Wins / losses | Win rate | Net PnL |
|---|---|---|---|---|
| CL | 90 | 53 / 37 | 58.9% | +$1,636.40 |
| ES | 49 | 17 / 32 | 34.7% | -$73.70 |
While ES finished negative, each round trip is $3.80 in commission - without including commissions this would be profitable ($112.50). This could be worth pursuing.
Try and modify the algo yourself
Paste this into Algos for CL or ES. This version adds a 7-tick stop and limits trading to 09:30–15:55 New York time.
// Fades big trades that sweep thin liquidity.
// https://marketbyorder.com/docs/api/algos
interface SymbolParams {
minSize: number
thinThreshold: number
}
const SYMBOL_PARAMS: Record<string, SymbolParams> = {
ES: { minSize: 120, thinThreshold: 45 },
CL: { minSize: 50, thinThreshold: 15 },
}
const DEFAULT_PARAMS = SYMBOL_PARAMS.ES
function paramsFor(sym: string): SymbolParams {
const key = Object.keys(SYMBOL_PARAMS).find(k => sym.toUpperCase().startsWith(k))
if (!key) {
print(`Thin Liquidity Fade: no tuned params for symbol "${sym}", falling back to ES defaults`)
return DEFAULT_PARAMS
}
return SYMBOL_PARAMS[key]
}
const { minSize: MIN_SIZE, thinThreshold: THIN_THRESHOLD } = paramsFor(symbol)
const QTY = 1
const HOLD_MS = 5 * 60_000
const SL_TICKS = 7
const RTH_START = 9 * 60 + 30 // minutes after midnight, New York time
const RTH_END = 16 * 60 // minutes after midnight, New York time
const FLATTEN_BUFFER_MIN = 5
const exchangeTime = new Intl.DateTimeFormat('en-US', {
timeZone: 'America/New_York',
hourCycle: 'h23',
weekday: 'short',
hour: 'numeric',
minute: 'numeric',
})
const sessionByMinute = new Map<number, { clock: number; weekday: string }>()
const sessionInfo = (ms: number) => {
const minute = Math.floor(ms / 60000)
const cached = sessionByMinute.get(minute)
if (cached) return cached
const parts = exchangeTime.formatToParts(ms)
const get = (type: string) => parts.find((part) => part.type === type)?.value ?? ''
const info = { clock: Number(get('hour')) * 60 + Number(get('minute')), weekday: get('weekday') }
sessionByMinute.set(minute, info)
return info
}
const isRth = (ms: number) => {
const { clock, weekday } = sessionInfo(ms)
return weekday !== 'Sat' && weekday !== 'Sun' && clock >= RTH_START && clock < RTH_END
}
const canEnter = (ms: number) => {
const { clock, weekday } = sessionInfo(ms)
return weekday !== 'Sat' && weekday !== 'Sun' && clock >= RTH_START && clock < RTH_END - FLATTEN_BUFFER_MIN
}
let exitTime: number | null = null
onUpdate(({ time, bid, ask, trades }) => {
const pos = orders.position()
if (pos.side !== "flat" && !canEnter(time)) {
orders.flatten()
exitTime = null
return
}
if (orders.working().length > 0 && !canEnter(time)) {
orders.cancelAll()
}
if (pos.side !== "flat" && exitTime !== null && time >= exitTime) {
orders.flatten()
exitTime = null
return
}
if (pos.side !== "flat" || orders.working().length > 0 || !canEnter(time)) return
for (const trade of trades) {
if (trade.side === "none" || trade.size < MIN_SIZE) continue
const levels = trade.levels.length
const cpl = trade.size / levels
if (cpl > THIN_THRESHOLD) continue
if (trade.side === "buy") {
const sl = bid + SL_TICKS * tickSize
orders.sell(QTY, { sl })
print(`FADE SELL: big BUY ${trade.size} swept ${levels} levels (cpl=${cpl.toFixed(1)}) - thin liquidity, sl=${sl.toFixed(2)}`)
} else {
const sl = ask - SL_TICKS * tickSize
orders.buy(QTY, { sl })
print(`FADE BUY: big SELL ${trade.size} swept ${levels} levels (cpl=${cpl.toFixed(1)}) - thin liquidity, sl=${sl.toFixed(2)}`)
}
exitTime = time + HOLD_MS
break
}
})Or explore the prints with the Big Trades indicator .