Strasmore Research
Deep Dives · Matt ConnorBy Matt Connor ·

grid trading bot how e work honest review

A grid trading bot dey buy for every step down and sell for every step up inside fixed range. See the market shape wey pay for the mechanism and the one wey break am.

A grid trading bot dey put order for different prices wey dey fixed: e dey put buy order for every price wey dey below the current price, and sell order for every price wey dey above am. Each time the market move down, the bot go buy. Each time e move up again, the bot go sell the thing wey e buy for lower price. The way e dey work na to collect profit from the up-and-down movement, but if the market just dey go one direction and no come back, the bot go end up with plenty position wey e no fit close.

This page go break the thing down, then measure the two market shapes wey dey decide wetin go happen. Every panel below na stored query wey use real US stock prices, wey we pin to fixed dates so the numbers fit stay the same.

How grid trading bot dey work

Three things dey set a grid: the top and bottom of the range, the number of lines inside am, and the size of order for each line. The space between lines come from the first two.

Here na the arithmetic, using round numbers wey we pick for clarity, no be from any real quote. Suppose one stock dey trade near $100 and one operator set a grid from $95 to $105 with eleven lines wey dey one dollar apart and 100 shares per line. Price drop to $99 and the buy order wey dey wait fill. Price come back to $100 and the matching sell order fill, wey close the pair for gross $100. Both orders go reset. Nothing about the company change for that hour. The position just complete one round trip between two lines wey dey close.

Two different types dey for the open-source projects wey dey public. Arithmetic grid dey space lines by fixed dollar amount. Geometric grid dey space dem by fixed percentage, wey make the percentage profit per round trip steady for wide range. Documentation also separate neutral grid, wey start flat and dey work both directions, from long grid, wey start with inventory and dey sell when price high.

The payoff shape get one simple sentence. Profit for one round trip wey complete na one grid step minus cost. The open position wey build up when price leave the range no get any cap.

The market shape wey grid need

Grid dey paid by travel, no be by direction. Two stocks fit finish period for the same price after dem cover different distances. If you add up the absolute size of every daily move, you go get the total distance wey dem travel. If you divide the net move by that distance, you go get efficiency ratio, wey be standard way to measure how directional the path be. Low ratio mean the path dey wander. High ratio mean e dey go straight.

QueryDistance wey dem travel vs net move: twelve familiar stocks, from Jan 2024 go reach Jun 2026
The exact SQL behind every number
WITH daily AS (
    SELECT ticker,
           toDate(toTimeZone(window_start, 'America/New_York')) AS dt,
           argMax(toFloat64(close), toTimeZone(window_start, 'America/New_York')) AS c
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker IN ('SPY','KO','PG','JNJ','VZ','MO','XOM','JPM','WMT','NVDA','TSLA','NFLX')
      AND toDate(toTimeZone(window_start, 'America/New_York')) BETWEEN toDate('2024-01-02') AND toDate('2026-06-30')
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
    GROUP BY ticker, dt
),
steps AS (
    SELECT ticker, dt, c,
           c / lagInFrame(c) OVER (PARTITION BY ticker ORDER BY dt) - 1 AS ret
    FROM daily
)
SELECT ticker,
       round(sum(abs(ret)) * 100, 0) AS travel_pct,
       round(abs(argMax(c, dt) / argMin(c, dt) - 1) * 100, 1) AS net_move_pct,
       round(100 * abs(argMax(c, dt) / argMin(c, dt) - 1) / sum(abs(ret)), 1) AS efficiency_pct
FROM steps
WHERE ret > -0.5 AND ret < 0.5
GROUP BY ticker
ORDER BY efficiency_pct
Run this yourself

Read the first and last rows together. PG cover 527% of cumulative daily movement and finish 0.9% from where e start, wey give efficiency of 0.2%. SPY dey for the opposite end at 14.3%, wey turn 415% of travel into 59.2% net move. One grid ladder wey dem put for the first path go meet its own lines many times. The same ladder wey dem put for the second path go dey left behind.

Calm and range-bound be different things, though dem relate. One stock fit drift upward for very small daily increments and still leave grid stranded, wey be one reason why the names wey dem study for the low-volatility anomaly literature no be automatically grid material.

Counting the round trips

Efficiency be one lens. Grid income depend on something wey dey more concrete: how many times price actually come back to one level. If you anchor on the first close of each calendar year and count the sessions wey close for the opposite side of that anchor, you go get the number of crossings wey one single grid line for don catch. Coca-Cola (KO) na the subject here, one big, liquid consumer name wey grid documentation dey like to use.

QueryKO: crossings of di year opening level, by calendar year, from 2019 go 2025
The exact SQL behind every number
WITH daily AS (
    SELECT toDate(toTimeZone(window_start, 'America/New_York')) AS dt,
           argMax(toFloat64(close), toTimeZone(window_start, 'America/New_York')) AS c
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker = 'KO'
      AND toDate(toTimeZone(window_start, 'America/New_York')) BETWEEN toDate('2019-01-01') AND toDate('2025-12-31')
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
    GROUP BY dt
),
anchored AS (
    SELECT toYear(dt) AS year, dt, c,
           first_value(c) OVER (PARTITION BY toYear(dt) ORDER BY dt) AS anchor
    FROM daily
),
sided AS (
    SELECT year, dt, c, anchor,
           if(c >= anchor, 1, 0) AS side,
           row_number() OVER (PARTITION BY year ORDER BY dt) AS rn,
           lagInFrame(if(c >= anchor, 1, 0)) OVER (PARTITION BY year ORDER BY dt) AS prev_side
    FROM anchored
)
SELECT year,
       countIf(rn > 1 AND side != prev_side) AS crossing_count,
       count() AS sessions,
       round((argMax(c, dt) / any(anchor) - 1) * 100, 1) AS net_change_pct
FROM sided
GROUP BY year
ORDER BY year
Run this yourself

For 2019 the close cross its own opening level 8 times for 252 sessions, wey finish the year 17.9% from that level. For 2025 the count na 6 for 250 sessions, with net change of 13%. One line, one stock, and the raw material for grid dey vary many times from year to year with no way to know the coming year's count before time.

That variability na the quiet problem. Grid's advertised return dey usually quote per completed round trip, and the number of round trips dey set by the market, no be by the settings.

Where grid dey break

The failure mode get specific shape. Fix one grid for one range, then let the market leave am. Below, the ceiling and floor na the highest and lowest closes of the first twenty sessions of 2024, wey dem hold flat for the next thirty months, with the S&P 500 tracker's monthly close draw against dem.

QueryA grid wey dem fix for early 2024, and where di S&P 500 tracker go afterward
The exact SQL behind every number
WITH daily AS (
    SELECT toDate(toTimeZone(window_start, 'America/New_York')) AS dt,
           argMax(toFloat64(close), toTimeZone(window_start, 'America/New_York')) AS c
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker = 'SPY'
      AND toDate(toTimeZone(window_start, 'America/New_York')) BETWEEN toDate('2024-01-02') AND toDate('2026-06-30')
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
    GROUP BY dt
),
opening_20 AS (
    SELECT c FROM daily ORDER BY dt LIMIT 20
),
band AS (
    SELECT min(c) AS grid_low, max(c) AS grid_high FROM opening_20
),
monthly AS (
    SELECT toStartOfMonth(dt) AS month_start, argMax(c, dt) AS close_px
    FROM daily
    GROUP BY month_start
)
SELECT formatDateTime(monthly.month_start, '%Y-%m') AS month,
       round(monthly.close_px, 2) AS spy_close,
       round(band.grid_high, 2) AS grid_top,
       round(band.grid_low, 2) AS grid_bottom
FROM monthly, band
ORDER BY monthly.month_start
Run this yourself

The band run from $467.27 to $491.26. The first month for the series close at $482.92, inside am. The last of the 30 months close at $746.32, far above the ceiling, and the chart show the line leave the band and never come back.

Walk through wetin the ladder do for that path. As e dey rise through the range, the bot dey sell inventory line by line, wey dey bank one step of profit each time. Above the ceiling e hold cash and dey watch. Every further point of the advance na gain wey the operator no longer get, wey be the mild version of the failure.

The severe version na the mirror image. Price fall through the floor and the bot don buy for every line for the way down, so e now hold the full ladder of inventory for average cost wey dey above the market, with each additional step wey dey widen the unrealized loss. One range wey once produce steady, small, successful trades turn into one single concentrated position. Grid marketing dey describe this as the bot being "stuck"; for brokerage statement e be open drawdown with no built-in exit.

Cost dey scale with the trade count

Grid dey complete many small trades, and each one dey pay the full cost of one round trip: the spread, plus commissions or exchange fees where dem apply. The bid-ask spread na the piece wey dem dey leave out of grid math, since e never appear for statement as line item.

The illustrative arithmetic dey quick. For $100 stock with one-cent spread, one round trip wey cross the spread for both sides dey give up roughly two cents, or 0.02% of notional. Against one-dollar grid step, that be about 2% of the gross profit per trip. If you widen the spread to five cents, wey be routine figure for thinner name, the same trip dey give up ten cents against one dollar of gross, or 10%. If you tighten the grid to ten-cent steps to catch more oscillation, e go cut the gross per trip to ten cents while the cost remain the same, and the five-cent spread now consume the whole trade.

That relationship dey set floor under useful grid spacing: steps must stay large relative to round-trip costs, and the tighter the grid, the more of its income belong to the venue, no be the operator.

Why backtests dey flatter grid bots

Grid strategies dey produce unusually attractive backtests, and four structural reasons dey explain most of am.

  1. Range selection with hindsight. Bounds wey dem choose after dem see the data go contain the data. One grid wey dem fit to period wey range go show high hit rate for that period.
  2. The unclosed ladder. One test wey stop for chosen end date dey often count realized round trips while e treat leftover inventory generously. The honest accounting dey mark the open position to market for the final day and include that loss for the result.
  3. Fill assumptions. Simulated resting orders dey fill whenever price touch dem. Real queues no dey always fill for the touch, and the trips wey fail to fill dey disproportionately be the profitable ones for fast markets.
  4. Parameter density. Bounds, line count, spacing, and order size give four dials over one price series, wey be enough to fit almost any historical window.

The crossing counts above na the counterweight to all four. Dem come from price history alone, with no settings attached, and dem dey vary year to year for way wey no parameter choice fit control. Anybody wey dey evaluate grid claim fit ask one single question of am: how many completed round trips this one depend on, and where that count come from? One period of practice for simulator, with the open ladder mark to market for the end, go answer am more honestly than chart of realized trips.

FAQ

Grid trading bots dey actually make money?

Grid dey earn capped profit for each completed round trip and carry uncapped open position when price leave its range. Whether the realized trips outweigh the open position for any period depend on how much the market oscillate inside that range, wey be property of the market, no be of the software.

Wetin market condition dey suit grid bot?

The mechanism dey build for price wey dey come back to the same levels many times: high total distance traveled relative to net movement. For the twelve-name panel above, efficiency ratios run from 0.2% for the wandering end to 14.3% for the directional end, and the ladder mechanism dey meet its own lines only for the first end.

Wetin happen when price break out of the grid range?

Above the range the bot don sell its inventory and dey sit for cash while the market continue without am. Below the range e hold every line wey e buy for the way down, for average cost wey dey above the current price, and each further decline dey widen that unrealized loss.

How grid trading different from dollar-cost averaging?

Both dey buy more as price fall, and there the similarity end. Dollar-cost averaging dey add fixed amount for fixed schedule with no exit rule and no upper bound. Grid dey buy and sell for price triggers inside range wey dem choose before time, and its behavior change completely once price leave that range.

Tighter grid spacing dey produce more income?

Tighter spacing dey raise the number of round trips and lower the gross profit for each one, while the round-trip cost remain the same. Past point wey the spread set, added trips dey subtract from the total, no be add to am.


Every figure above come from stored, versioned query over real equity prices; open the SQL under any panel to check am, or measure travel against net movement for your own list of names for the Strasmore terminal.

#grid trading#trading bots#automation#mean reversion#market data