<!-- Generated from TradeReady.io docs. Visit https://tradeready.io/docs for the full experience. -->

---
title: Strategy Examples
description: Four complete backtesting strategies with code — moving average crossover, RSI, breakout, and momentum rotation
---

These examples show complete strategy implementations. Each uses the same backtest loop structure — only the decision logic changes.

All examples use Python with `httpx`. The same logic translates to any language that can make HTTP requests.

---

## Setup

```python
import httpx
import statistics

BASE = "http://localhost:8000/api/v1"
HEADERS = {"X-API-Key": "ak_live_..."}

def create_and_start(label, pairs=None):
    """Create and start a backtest session."""
    r = httpx.post(f"{BASE}/backtest/create", headers=HEADERS, json={
        "start_time": "2025-06-01T00:00:00Z",
        "end_time": "2025-07-01T00:00:00Z",
        "starting_balance": 10000,
        "candle_interval": "1m",
        "strategy_label": label,
        "agent_id": "your-agent-uuid",
        "pairs": pairs,
    })
    sid = r.json()["session_id"]
    httpx.post(f"{BASE}/backtest/{sid}/start", headers=HEADERS)
    return sid

def step(sid, steps=1):
    return httpx.post(
        f"{BASE}/backtest/{sid}/step/batch",
        headers=HEADERS,
        json={"steps": steps}
    ).json()

def place_order(sid, symbol, side, order_type, quantity, price=None):
    body = {"symbol": symbol, "side": side, "type": order_type, "quantity": str(quantity)}
    if price:
        body["price"] = str(price)
    return httpx.post(f"{BASE}/backtest/{sid}/trade/order", headers=HEADERS, json=body).json()
```

---

## Strategy 1 — Simple Moving Average Crossover

**The idea:** When the fast (10-period) moving average crosses above the slow (50-period) moving average, a new uptrend may be starting — buy. When the fast crosses back below the slow, sell.

```python
sid = create_and_start("sma_crossover_v1", pairs=["BTCUSDT"])

prices = []
holding = False

while True:
    result = step(sid)  # one candle at a time
    price = float(result["prices"]["BTCUSDT"])
    prices.append(price)

    if len(prices) >= 50:
        fast_ma = statistics.mean(prices[-10:])
        slow_ma = statistics.mean(prices[-50:])

        cash = float(result["portfolio"]["available_cash"])
        equity = float(result["portfolio"]["total_equity"])

        # Buy signal: fast crossed above slow
        if fast_ma > slow_ma and not holding and cash > 10:
            qty = (cash * 0.9) / price  # use 90% of available cash
            place_order(sid, "BTCUSDT", "buy", "market", round(qty, 6))
            holding = True

        # Sell signal: fast crossed below slow
        elif fast_ma < slow_ma and holding:
            positions = httpx.get(
                f"{BASE}/backtest/{sid}/account/positions", headers=HEADERS
            ).json().get("positions", [])
            btc_qty = next(
                (float(p["quantity"]) for p in positions if p["symbol"] == "BTCUSDT"), 0
            )
            if btc_qty > 0:
                place_order(sid, "BTCUSDT", "sell", "market", round(btc_qty, 6))
                holding = False

    if result["is_complete"]:
        break

results = httpx.get(f"{BASE}/backtest/{sid}/results", headers=HEADERS).json()
print(f"SMA Crossover — ROI: {results['summary']['roi_pct']}%")
print(f"Sharpe: {results['metrics']['sharpe_ratio']}")
print(f"Trades: {results['summary']['total_trades']}")
```

**Batching tip:** Hourly strategies can step 60 candles at a time — this reduces API calls from 43,200 to 720 for a 30-day test.

---

## Strategy 2 — RSI Mean Reversion

**The idea:** When RSI drops below 30, the asset is oversold and likely to bounce. When RSI rises above 70, it is overbought and likely to pull back. Buy oversold, sell overbought.

```python
def compute_rsi(prices, period=14):
    if len(prices) < period + 1:
        return None
    changes = [prices[i] - prices[i-1] for i in range(-period, 0)]
    gains = [max(0, c) for c in changes]
    losses = [abs(min(0, c)) for c in changes]
    avg_gain = statistics.mean(gains)
    avg_loss = statistics.mean(losses)
    if avg_loss == 0:
        return 100.0
    rs = avg_gain / avg_loss
    return 100 - (100 / (1 + rs))

sid = create_and_start("rsi_mean_reversion_v1", pairs=["BTCUSDT"])

prices = []
holding = False

while True:
    result = step(sid)
    price = float(result["prices"]["BTCUSDT"])
    prices.append(price)

    rsi = compute_rsi(prices)

    if rsi is not None:
        cash = float(result["portfolio"]["available_cash"])

        # Buy: oversold
        if rsi < 30 and not holding and cash > 10:
            qty = (cash * 0.5) / price  # use half the cash — conservative
            place_order(sid, "BTCUSDT", "buy", "market", round(qty, 6))
            # Set stop loss and take profit automatically
            place_order(sid, "BTCUSDT", "sell", "stop_loss", round(qty, 6),
                        price=price * 0.97)   # -3% stop
            place_order(sid, "BTCUSDT", "sell", "take_profit", round(qty, 6),
                        price=price * 1.05)   # +5% target
            holding = True

        # Sell: overbought (if take-profit hasn't already filled)
        elif rsi > 70 and holding:
            positions = httpx.get(
                f"{BASE}/backtest/{sid}/account/positions", headers=HEADERS
            ).json().get("positions", [])
            btc_qty = next(
                (float(p["quantity"]) for p in positions if p["symbol"] == "BTCUSDT"), 0
            )
            if btc_qty > 0:
                place_order(sid, "BTCUSDT", "sell", "market", round(btc_qty, 6))
                holding = False

    if result["is_complete"]:
        break

results = httpx.get(f"{BASE}/backtest/{sid}/results", headers=HEADERS).json()
print(f"RSI Mean Reversion — ROI: {results['summary']['roi_pct']}%")
print(f"Win Rate: {results['metrics']['win_rate']}%")
```

---

## Strategy 3 — Breakout with Stop-Loss and Take-Profit

**The idea:** When price breaks above the 24-hour high, momentum is building and the breakout may continue. Enter and protect with automatic exit orders.

```python
sid = create_and_start("breakout_v1", pairs=["BTCUSDT"])

holding = False
entry_price = None

while True:
    # Step 60 candles (one hour) between decisions
    result = step(sid, steps=60)
    price = float(result["prices"]["BTCUSDT"])

    # Get 24-hour ticker for the high
    ticker = httpx.get(
        f"{BASE}/backtest/{sid}/market/ticker/BTCUSDT", headers=HEADERS
    ).json()
    high_24h = float(ticker.get("high", price))

    # Check if our stop/take-profit orders have filled
    if holding:
        positions = httpx.get(
            f"{BASE}/backtest/{sid}/account/positions", headers=HEADERS
        ).json().get("positions", [])
        btc_qty = next(
            (float(p["quantity"]) for p in positions if p["symbol"] == "BTCUSDT"), 0
        )
        if btc_qty == 0:
            holding = False  # stop or take-profit filled

    # Buy on breakout
    if not holding and price > high_24h:
        cash = float(result["portfolio"]["available_cash"])
        if cash > 10:
            qty = (cash * 0.8) / price
            place_order(sid, "BTCUSDT", "buy", "market", round(qty, 6))
            entry_price = price

            # Automatic exits
            place_order(sid, "BTCUSDT", "sell", "stop_loss", round(qty, 6),
                        price=entry_price * 0.98)   # -2% stop
            place_order(sid, "BTCUSDT", "sell", "take_profit", round(qty, 6),
                        price=entry_price * 1.05)   # +5% target
            holding = True

    if result["is_complete"]:
        break

results = httpx.get(f"{BASE}/backtest/{sid}/results", headers=HEADERS).json()
print(f"Breakout — ROI: {results['summary']['roi_pct']}%")
print(f"Max Drawdown: {results['metrics']['max_drawdown_pct']}%")
```

> **Info:**
> Stop-loss and take-profit orders check against every candle during a batch step. Even if you skip 60 candles, your -2% stop will trigger on the correct candle within the batch.

---

## Strategy 4 — Multi-Pair Momentum Rotation

**The idea:** Every hour, rank all pairs by their 24-hour performance. Hold the top 3. Sell anything that falls out of the top 3 and buy whatever enters it. Always stay with the strongest movers.

```python
PAIRS = ["BTCUSDT", "ETHUSDT", "SOLUSDT", "BNBUSDT", "XRPUSDT"]

sid = create_and_start("momentum_rotation_v1", pairs=PAIRS)

while True:
    # Step one hour (60 1-minute candles) at a time
    result = step(sid, steps=60)

    if result["is_complete"]:
        break

    # Rank pairs by 24h performance
    rankings = []
    for sym in PAIRS:
        ticker = httpx.get(
            f"{BASE}/backtest/{sid}/market/ticker/{sym}", headers=HEADERS
        ).json()
        change_pct = float(ticker.get("change_pct", 0))
        rankings.append((sym, change_pct))

    rankings.sort(key=lambda x: x[1], reverse=True)
    top_3 = {sym for sym, _ in rankings[:3]}

    # Get current holdings
    positions = httpx.get(
        f"{BASE}/backtest/{sid}/account/positions", headers=HEADERS
    ).json().get("positions", [])
    held = {p["symbol"] for p in positions}

    # Sell anything not in top 3
    for pos in positions:
        if pos["symbol"] not in top_3:
            place_order(sid, pos["symbol"], "sell", "market",
                        round(float(pos["quantity"]), 6))

    # Calculate how much to allocate per new position
    portfolio = result["portfolio"]
    cash = float(portfolio["available_cash"])
    to_buy = top_3 - held
    if to_buy and cash > 10:
        alloc = (cash * 0.9) / len(to_buy)
        for sym in to_buy:
            price = float(result["prices"][sym])
            qty = alloc / price
            place_order(sid, sym, "buy", "market", round(qty, 6))

results = httpx.get(f"{BASE}/backtest/{sid}/results", headers=HEADERS).json()
print(f"Momentum Rotation — ROI: {results['summary']['roi_pct']}%")
print(f"Trades: {results['summary']['total_trades']}")
```

---

## Position Sizing Patterns

All examples above use a simple fixed-percentage approach. Here are three common sizing patterns:

```python
# Fixed percentage: use 10% of equity per trade
qty = (available_cash * 0.10) / current_price

# Equal weight: divide evenly across N positions
qty = (total_equity / N) / current_price

# Risk-based (Kelly-lite): size based on stop-loss distance
risk_amount = total_equity * 0.02  # risk 2% of equity per trade
stop_distance = entry_price - stop_price
qty = risk_amount / stop_distance
```

---

## Comparing All Strategies

After running all four, compare them:

```python
session_ids = ["bt_sma", "bt_rsi", "bt_breakout", "bt_rotation"]

comparison = httpx.get(
    f"{BASE}/backtest/compare",
    headers=HEADERS,
    params={"sessions": ",".join(session_ids)}
).json()

for c in comparison["comparisons"]:
    print(f"{c['strategy_label']:25} | ROI: {c['roi_pct']:6.1f}% | "
          f"Sharpe: {c['sharpe_ratio']:.2f} | DD: {c['max_drawdown_pct']:.1f}%")

print(f"\nBest ROI: {comparison['best_by_roi']}")
print(f"Best Sharpe: {comparison['best_by_sharpe']}")
```

---

## Next Steps

- [Strategy Testing](/docs/strategies/testing) — automated multi-episode testing across random historical periods
- [Strategies: Indicators](/docs/strategies/indicators) — use platform-computed indicators in your strategy definition
- [Gymnasium Environments](/docs/gym) — train an RL agent that goes beyond rule-based logic
