Crypto Grid Trading Bot: The Complete 2026 Guide
Grid trading bots buy low and sell high automatically within a price range, generating income in sideways markets. Learn how grid trading works, optimal grid spacing math, and how to build one with CCXT.
Builder of AI agents, crypto trading bots, and open-source automation tools. Sharing practical guides on how to build, deploy, and profit from AI and DeFi technology.
What Is Grid Trading?
Grid trading places buy and sell orders at regular price intervals within a range. When price moves up or down, orders fill automatically โ buying low and selling high on every oscillation.
Example grid on ETH ($3,000-$3,500 range, 10 grids):
Price Level | Action at Level | Profit per Grid
$3,500 | SELL all | $50 (top)
$3,450 | SELL | โ $50 spacing
$3,400 | SELL |
$3,350 | SELL / BUY | โ Current price
$3,300 | BUY |
$3,250 | BUY |
$3,200 | BUY |
$3,150 | BUY |
$3,100 | BUY |
$3,000 | BUY all | (bottom)
Every time price oscillates $50 within the range, a buy and then sell fills, capturing $50 in profit. The more oscillations, the more profit.
Grid Trading Math
def calculate_grid_parameters(
lower_price: float,
upper_price: float,
n_grids: int,
capital_usd: float,
arithmetic: bool = True # True=arithmetic spacing, False=geometric (% spacing)
) -> dict:
"""Calculate optimal grid parameters"""
if arithmetic:
# Equal dollar spacing between grids
step = (upper_price - lower_price) / n_grids
levels = [lower_price + step * i for i in range(n_grids + 1)]
else:
# Equal percentage spacing (better for wider ranges)
ratio = (upper_price / lower_price) ** (1 / n_grids)
levels = [lower_price * (ratio ** i) for i in range(n_grids + 1)]
# Capital allocation per grid
base_capital = capital_usd / n_grids
# For each buy level, calculate quantity
orders = []
for i, price in enumerate(levels[:-1]): # Exclude top level (sell only)
qty = base_capital / price
profit_per_grid = (levels[i + 1] - price) * qty
profit_pct = (levels[i + 1] - price) / price * 100
orders.append({
'buy_price': round(price, 2),
'sell_price': round(levels[i + 1], 2),
'qty': round(qty, 6),
'profit_per_fill': round(profit_per_grid, 2),
'profit_pct': round(profit_pct, 3),
})
# Estimate daily profit (assumes X oscillations per day)
avg_daily_oscillations = 2 # Conservative estimate
daily_profit = sum(o['profit_per_fill'] for o in orders) * avg_daily_oscillations / n_grids
return {
'levels': levels,
'orders': orders,
'grid_spacing_pct': ((levels[1] - levels[0]) / levels[0]) * 100,
'capital_per_grid': base_capital,
'estimated_daily_profit': daily_profit,
'estimated_annual_yield': (daily_profit / capital_usd) * 365 * 100,
}
# Example calculation
params = calculate_grid_parameters(
lower_price=3000,
upper_price=3500,
n_grids=10,
capital_usd=5000,
arithmetic=True
)
print(f"Grid spacing: ${(params['levels'][1] - params['levels'][0]):.2f}")
print(f"Capital per grid: ${params['capital_per_grid']:.2f}")
print(f"Estimated daily profit: ${params['estimated_daily_profit']:.2f}")
print(f"Estimated annual yield: {params['estimated_annual_yield']:.1f}%")
Building the Grid Bot
import ccxt
import time
from typing import Optional
class GridBot:
def __init__(
self,
exchange: ccxt.Exchange,
symbol: str,
lower: float,
upper: float,
n_grids: int,
capital_usd: float,
):
self.exchange = exchange
self.symbol = symbol
self.lower = lower
self.upper = upper
self.n_grids = n_grids
self.capital = capital_usd
# Calculate grid levels
params = calculate_grid_parameters(lower, upper, n_grids, capital_usd)
self.levels = params['levels']
self.qty_per_grid = capital_usd / n_grids / lower # approx qty
self.open_orders = {} # {order_id: level_info}
self.filled_orders = []
self.realized_pnl = 0
def setup_initial_orders(self):
"""Place all initial grid orders based on current price"""
current_price = self.get_current_price()
print(f"\n๐ฒ Setting up grid: {self.lower} - {self.upper} ({self.n_grids} grids)")
print(f" Current price: ${current_price:.2f}")
orders_placed = 0
for i, level in enumerate(self.levels):
if level < current_price:
# Below current price โ place buy orders
qty = self.capital / self.n_grids / level
qty = self.exchange.amount_to_precision(self.symbol, qty)
price = self.exchange.price_to_precision(self.symbol, level)
try:
order = self.exchange.create_limit_buy_order(self.symbol, qty, price)
self.open_orders[order['id']] = {
'level_index': i,
'type': 'buy',
'price': level,
'qty': float(qty),
}
orders_placed += 1
except Exception as e:
print(f" โ Failed to place buy at ${level}: {e}")
elif level > current_price:
# Above current price โ place sell orders
qty = self.capital / self.n_grids / current_price
qty = self.exchange.amount_to_precision(self.symbol, qty)
price = self.exchange.price_to_precision(self.symbol, level)
try:
order = self.exchange.create_limit_sell_order(self.symbol, qty, price)
self.open_orders[order['id']] = {
'level_index': i,
'type': 'sell',
'price': level,
'qty': float(qty),
}
orders_placed += 1
except Exception as e:
print(f" โ Failed to place sell at ${level}: {e}")
print(f"โ
Placed {orders_placed} initial grid orders")
def check_and_replace_filled_orders(self):
"""Check for filled orders and replace them with opposite orders"""
for order_id, order_info in list(self.open_orders.items()):
try:
order = self.exchange.fetch_order(order_id, self.symbol)
if order['status'] == 'closed':
# Order was filled!
del self.open_orders[order_id]
level_idx = order_info['level_index']
fill_price = order['average']
qty = order['filled']
if order_info['type'] == 'buy':
# Buy filled โ place sell at next level up
next_level = self.levels[level_idx + 1]
new_order = self.exchange.create_limit_sell_order(
self.symbol, qty,
self.exchange.price_to_precision(self.symbol, next_level)
)
profit = (next_level - fill_price) * qty
print(f"๐ BUY filled at ${fill_price:.2f} โ placed SELL at ${next_level:.2f} (target ${profit:.2f} profit)")
else:
# Sell filled โ place buy at next level down
prev_level = self.levels[level_idx - 1]
new_order = self.exchange.create_limit_buy_order(
self.symbol, qty,
self.exchange.price_to_precision(self.symbol, prev_level)
)
profit = (fill_price - prev_level) * qty
self.realized_pnl += profit
print(f"๐ SELL filled at ${fill_price:.2f} โ placed BUY at ${prev_level:.2f} | Realized P&L: ${self.realized_pnl:.2f}")
self.open_orders[new_order['id']] = {
'level_index': level_idx - 1 if order_info['type'] == 'sell' else level_idx + 1,
'type': 'buy' if order_info['type'] == 'sell' else 'sell',
'price': prev_level if order_info['type'] == 'sell' else next_level,
'qty': qty,
}
except Exception as e:
print(f"Error checking order {order_id}: {e}")
def check_price_in_range(self) -> bool:
"""Stop bot if price leaves the grid range"""
price = self.get_current_price()
if price < self.lower or price > self.upper:
print(f"โ ๏ธ Price ${price:.2f} outside grid range [{self.lower}, {self.upper}]")
return False
return True
def get_current_price(self) -> float:
return self.exchange.fetch_ticker(self.symbol)['last']
def run(self, check_interval: int = 30):
"""Main grid bot loop"""
self.setup_initial_orders()
while True:
try:
if not self.check_price_in_range():
print("๐ Stopping grid bot โ price out of range")
self.exchange.cancel_all_orders(self.symbol)
break
self.check_and_replace_filled_orders()
time.sleep(check_interval)
except KeyboardInterrupt:
print(f"\nโน๏ธ Bot stopped. Realized P&L: ${self.realized_pnl:.2f}")
self.exchange.cancel_all_orders(self.symbol)
break
# Run grid bot
exchange = ccxt.binance({'apiKey': KEY, 'secret': SECRET})
bot = GridBot(exchange, 'ETH/USDT', lower=3000, upper=3500, n_grids=10, capital_usd=5000)
bot.run()
When Grid Trading Works (and Fails)
Works well when:
- Price oscillates within a predictable range (sideways/consolidation)
- High volatility within the range (more fills = more profit)
- Low trend bias โ ranging, not trending
Fails when:
- Price breaks out of the range (all buys filled, price drops = unrealized loss)
- Strong trending market (keeps filling one side only)
Pro tip: Set your grid range based on the 30-day ATR (Average True Range). A range of ยฑ2 ATR from current price gives a statistically reasonable expectation that price stays within bounds.
Grid trading is one of the simplest profitable bot strategies to build and one of the most widely used by retail algorithmic traders. Master it before moving to more complex approaches.
Tagged in
Related Articles
Crypto Bot Risk Management: The 10 Rules That Separate Winners From Losers
7 min read
Crypto BotsHow to Build a Self-Healing Trading Bot That Fixes Its Own Errors
5 min read
Crypto BotsPump.fun and Solana Meme Coin Bots: How to Automate the Hottest Trend
5 min read
Crypto BotsHow to Build a Crypto Portfolio Auto-Rebalancing Bot
5 min read