DEX & Algo Trading

Grid Trading Strategy for Crypto Sideways Markets: Analysis and Parameter Tuning

ClawDUX TeamMarch 31, 20268 min read0 views

Grid Trading Strategy for Crypto Sideways Markets: Analysis and Parameter Tuning

Grid trading thrives in choppy, range-bound markets — exactly the conditions that frustrate trend-followers. Here's how to build, tune, and evaluate a grid strategy.

How Grid Trading Works

A grid strategy places buy and sell limit orders at fixed intervals above and below the current price:

plaintext
Sell $68,000  ─────────── Grid Level 5
Sell $67,500  ─────────── Grid Level 4
Sell $67,000  ─────────── Grid Level 3
      $66,500  ←── Current price
Buy  $66,000  ─────────── Grid Level 2
Buy  $65,500  ─────────── Grid Level 1
Buy  $65,000  ─────────── Grid Level 0

Every time the price drops to a buy level, you accumulate. Every time it rises to a sell level, you take profit. In a range-bound market, this prints money.

Implementation

python
import numpy as np
import pandas as pd
from dataclasses import dataclass, field
from typing import List, Optional

@dataclass
class GridConfig:
    lower_price: float       # Bottom of the grid
    upper_price: float       # Top of the grid
    num_grids: int           # Number of grid levels
    total_investment: float  # Total capital to deploy
    symbol: str = "BTC/USDT"

    @property
    def grid_spacing(self) -> float:
        return (self.upper_price - self.lower_price) / self.num_grids

    @property
    def order_size(self) -> float:
        return self.total_investment / self.num_grids

    @property
    def grid_levels(self) -> List[float]:
        return [
            self.lower_price + i * self.grid_spacing
            for i in range(self.num_grids + 1)
        ]

@dataclass
class GridBacktestResult:
    total_return_pct: float
    num_trades: int
    profit_per_trade: float
    max_drawdown_pct: float
    sharpe_ratio: float
    grid_captures: int  # How many grid cycles completed

def backtest_grid(
    prices: pd.Series,
    config: GridConfig,
) -> GridBacktestResult:
    """Backtest a grid strategy on historical price data."""
    levels = config.grid_levels
    position = 0.0      # Amount of base asset held
    cash = config.total_investment
    initial_value = cash
    trades = 0
    grid_captures = 0
    portfolio_values = []
    buy_fills = set()    # Track which levels have been bought

    for price in prices:
        # Check buy levels
        for i, level in enumerate(levels[:-1]):
            if price <= level and i not in buy_fills:
                # Buy at this grid level
                qty = config.order_size / price
                position += qty
                cash -= config.order_size
                buy_fills.add(i)
                trades += 1

        # Check sell levels
        for i, level in enumerate(levels[1:], 1):
            if price >= level and (i - 1) in buy_fills:
                # Sell matching position
                qty = config.order_size / levels[i - 1]  # original buy qty
                sell_value = qty * price
                position -= qty
                cash += sell_value
                buy_fills.discard(i - 1)
                trades += 1
                grid_captures += 1

        # Track portfolio value
        portfolio_value = cash + position * price
        portfolio_values.append(portfolio_value)

    # Calculate metrics
    pv = pd.Series(portfolio_values)
    total_return = (pv.iloc[-1] / initial_value - 1) * 100
    daily_returns = pv.pct_change().dropna()
    sharpe = (
        daily_returns.mean() / daily_returns.std()
        * np.sqrt(365)
    ) if daily_returns.std() > 0 else 0
    max_dd = (
        (pv.cummax() - pv) / pv.cummax()
    ).max() * 100

    return GridBacktestResult(
        total_return_pct=round(total_return, 2),
        num_trades=trades,
        profit_per_trade=round(
            (pv.iloc[-1] - initial_value) / max(trades, 1), 2
        ),
        max_drawdown_pct=round(max_dd, 2),
        sharpe_ratio=round(sharpe, 2),
        grid_captures=grid_captures,
    )

Parameter Tuning

python
def optimize_grid_params(
    prices: pd.Series,
    capital: float = 10000,
) -> dict:
    """Find optimal grid parameters via grid search."""
    best_result = None
    best_config = None

    price_range = prices.max() - prices.min()
    mid_price = prices.median()

    for num_grids in [5, 10, 15, 20, 30]:
        for range_pct in [0.05, 0.10, 0.15, 0.20]:
            lower = mid_price * (1 - range_pct)
            upper = mid_price * (1 + range_pct)

            config = GridConfig(
                lower_price=lower,
                upper_price=upper,
                num_grids=num_grids,
                total_investment=capital,
            )

            result = backtest_grid(prices, config)

            if (best_result is None or
                result.sharpe_ratio > best_result.sharpe_ratio):
                best_result = result
                best_config = config

    return {
        'config': best_config,
        'result': best_result,
    }

Optimal Parameters by Market Regime

Market Condition Grid Count Range Width Expected Sharpe
Low volatility sideways 20-30 5-10% 1.5-3.0
Medium volatility 10-15 10-15% 0.8-1.5
High volatility 5-10 15-25% 0.3-0.8
Strong trend Don't use grid Negative

Grid strategies are one of the most popular strategy types traded on ClawDUX, where the AI verification engine independently calculates grid capture rates and validates performance claims before listing.

The core logic discussed in this article has been integrated into the ClawDUX API. Access ClawDUX-core for full permissions, or browse the marketplace to discover verified trading strategies.

#grid-trading#crypto#parameter-tuning#backtesting#strategy

Related Articles