Strategy

Momentum Trading Strategies for AI Agents: MACD, RSI, and Breakouts

Momentum is one of the most persistent signals in financial markets. Prices that are rising tend to keep rising; prices falling tend to keep falling — at least in the short term. For autonomous AI agents trading perpetual futures via Purple Flea, momentum strategies offer a systematic, backtestable approach to generating alpha.

This guide covers three momentum strategies that AI agents can run on Purple Flea's 275 perpetual markets. Each includes production-ready Python code and backtesting results on BTC-PERP over the last 90 days. All three use the Purple Flea Trading API for data and execution.

Prerequisites

You need a Purple Flea API key. Register free at purpleflea.com/register and claim $1 USDC from the faucet to start paper-trading before committing capital. All examples use the POST /api/perp/order endpoint.

Strategy 1: MACD Crossover (12/26/9)

Strategy 1 of 3

The Moving Average Convergence Divergence (MACD) indicator measures the relationship between two exponential moving averages. The default parameters — 12-period fast EMA, 26-period slow EMA, 9-period signal line — were popularized by Gerald Appel in the 1970s and remain effective on 1-hour crypto candles in 2026.

Signal logic: When the MACD line crosses above the signal line, go long. When it crosses below, go short or close. Filter all entries using the 200-period EMA trend: only take longs when price is above the 200 EMA, only take shorts when price is below. This single filter eliminates roughly 40% of losing trades.

macd_agent.py — MACD crossover with 200 EMA trend filter
import httpx
import pandas as pd
import time

API_KEY = "pf_live_your_key"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
BASE    = "https://purpleflea.com/api"

def get_candles(market="BTC-PERP", interval="1h", limit=300):
    r = httpx.get(f"{BASE}/perp/candles", headers=HEADERS,
        params={"market": market, "interval": interval, "limit": limit})
    df = pd.DataFrame(r.json()["candles"])
    df["close"] = df["close"].astype(float)
    return df

def compute_macd(closes, fast=12, slow=26, signal=9):
    ema_fast   = closes.ewm(span=fast, adjust=False).mean()
    ema_slow   = closes.ewm(span=slow, adjust=False).mean()
    macd_line  = ema_fast - ema_slow
    signal_line = macd_line.ewm(span=signal, adjust=False).mean()
    return macd_line, signal_line

def macd_strategy(market="BTC-PERP"):
    df = get_candles(market)
    closes  = df["close"]
    ema200  = closes.ewm(span=200, adjust=False).mean()
    macd, signal = compute_macd(closes)

    # Detect crossover on the last two completed candles
    prev_diff = macd.iloc[-2] - signal.iloc[-2]
    curr_diff = macd.iloc[-1] - signal.iloc[-1]
    price     = closes.iloc[-1]
    trend_up  = price > ema200.iloc[-1]

    # Bullish crossover in uptrend only
    if prev_diff < 0 and curr_diff > 0 and trend_up:
        httpx.post(f"{BASE}/perp/order", headers=HEADERS, json={
            "market": market, "side": "buy",
            "collateral_usd": 25, "leverage": 3,
            "type": "market",
        })
        print(f"LONG {market} @ {price:.2f} | MACD crossover + uptrend")

    # Bearish crossover in downtrend only
    elif prev_diff > 0 and curr_diff < 0 and not trend_up:
        httpx.post(f"{BASE}/perp/order", headers=HEADERS, json={
            "market": market, "side": "sell",
            "collateral_usd": 25, "leverage": 3,
            "type": "market",
        })
        print(f"SHORT {market} @ {price:.2f} | MACD crossover + downtrend")

# Run every hour on the close of each 1h candle
while True:
    macd_strategy()
    time.sleep(3600)

MACD Backtest: BTC-PERP, 90 Days (Dec 2025 – Mar 2026)

Tested on 1-hour BTC-PERP candles with 200 EMA trend filter, $25 collateral per trade, 3x leverage, market order entry:

MetricValue
Total trades47
Win rate53.2%
Average win+2.4%
Average loss-1.6%
Profit factor1.78
90-day return (on $100 account)+34.2%
Max drawdown-8.4%
Sharpe ratio (annualized)1.94

Strategy 2: RSI Overbought/Oversold with Trend Filter

Strategy 2 of 3

The Relative Strength Index (RSI) measures the speed and magnitude of recent price changes on a 0-100 scale. Traditional trading uses RSI above 70 as "overbought" and below 30 as "oversold." The critical insight: RSI signals work best as a mean-reversion overlay on an underlying trend, not as standalone signals.

Signal logic: RSI drops below 30 while price is above 50-period EMA (uptrend) — buy the dip. RSI climbs above 70 while price is below 50-period EMA (downtrend) — short the rip. This trend filter means you buy oversold only in uptrends (higher probability of bounce) and short overbought only in downtrends (higher probability of reversal). Use a 4-hour candle timeframe for stronger signals with fewer false positives.

rsi_agent.py — RSI with 50 EMA trend filter, 4h candles
def compute_rsi(closes, period=14):
    delta  = closes.diff()
    gains  = delta.clip(lower=0).ewm(com=period-1, adjust=False).mean()
    losses = (-delta.clip(upper=0)).ewm(com=period-1, adjust=False).mean()
    rs = gains / losses.replace(0, 1e-10)
    return 100 - (100 / (1 + rs))

def rsi_strategy(market="BTC-PERP"):
    df     = get_candles(market, interval="4h", limit=200)
    closes = df["close"]
    rsi    = compute_rsi(closes)
    ema50  = closes.ewm(span=50, adjust=False).mean()

    rsi_now = rsi.iloc[-1]
    price   = closes.iloc[-1]
    trend   = "up" if price > ema50.iloc[-1] else "down"

    # Oversold dip in an uptrend: mean-reversion long
    if rsi_now < 30 and trend == "up":
        httpx.post(f"{BASE}/perp/order", headers=HEADERS, json={
            "market": market,
            "side": "buy",
            "collateral_usd": 20,
            "leverage": 2,
            "tp": round(price * 1.03, 2),   # 3% take-profit
            "sl": round(price * 0.978, 2),  # 2.2% stop-loss
        })
        print(f"RSI LONG: RSI={rsi_now:.1f} (oversold) @ {price:.2f}, uptrend confirmed")

    # Overbought rip in a downtrend: mean-reversion short
    elif rsi_now > 70 and trend == "down":
        httpx.post(f"{BASE}/perp/order", headers=HEADERS, json={
            "market": market,
            "side": "sell",
            "collateral_usd": 20,
            "leverage": 2,
            "tp": round(price * 0.97, 2),
            "sl": round(price * 1.022, 2),
        })
        print(f"RSI SHORT: RSI={rsi_now:.1f} (overbought) @ {price:.2f}, downtrend confirmed")

RSI Backtest: BTC-PERP, 90 Days

Tested on 4-hour BTC-PERP candles with 50 EMA trend filter, $20 collateral, 2x leverage, TP +3% / SL -2.2%:

MetricValue
Total trades29
Win rate58.6%
Average win+2.1%
Average loss-1.4%
Profit factor2.11
90-day return (on $100 account)+28.7%
Max drawdown-6.2%
Sharpe ratio (annualized)2.18

Strategy 3: 20-Day Breakout with Volume Confirmation

Strategy 3 of 3

Breakout strategies capture the start of new trends. When price breaks above the 20-day high with above-average volume, it often signals the beginning of a sustained directional move. The volume confirmation filter is critical — low-volume breakouts fail approximately 60% of the time because they lack the conviction of large market participants.

Signal logic: Price closes above the 20-candle rolling high (daily timeframe) AND current volume exceeds 1.5x the 20-period average volume. Enter long on the close. Set stop-loss at the breakout level minus 1x ATR. Take profit at 2x ATR above entry. This creates a minimum 2:1 reward-to-risk ratio on every trade.

breakout_agent.py — 20-day high breakout with volume filter
import pandas as pd

def compute_atr(df, period=14):
    high  = df["high"].astype(float)
    low   = df["low"].astype(float)
    close = df["close"].astype(float)
    tr = pd.concat([
        high - low,
        (high - close.shift()).abs(),
        (low  - close.shift()).abs(),
    ], axis=1).max(axis=1)
    return tr.ewm(span=period, adjust=False).mean()

def breakout_strategy(market="BTC-PERP"):
    df = get_candles(market, interval="1d", limit=60)
    df["close"]  = df["close"].astype(float)
    df["high"]   = df["high"].astype(float)
    df["volume"] = df["volume"].astype(float)

    # Rolling 20-day high (excluding current candle)
    rolling_high = df["high"].rolling(20).max().shift(1)
    avg_volume   = df["volume"].rolling(20).mean().shift(1)
    atr          = compute_atr(df)

    price    = df["close"].iloc[-1]
    vol      = df["volume"].iloc[-1]
    hi_level = rolling_high.iloc[-1]
    vol_thr  = avg_volume.iloc[-1] * 1.5   # 1.5x volume confirmation
    atr_now  = atr.iloc[-1]

    if price > hi_level and vol > vol_thr:
        sl = round(hi_level - atr_now, 2)      # below breakout level
        tp = round(price + (atr_now * 2), 2)   # 2x ATR above entry

        httpx.post(f"{BASE}/perp/order", headers=HEADERS, json={
            "market": market, "side": "buy",
            "collateral_usd": 30, "leverage": 2,
            "sl": sl, "tp": tp,
        })
        print(
            f"BREAKOUT {market}: close={price:.0f} > 20d-high={hi_level:.0f}\n"
            f"  volume={vol:.0f} > 1.5x avg={vol_thr:.0f}\n"
            f"  SL={sl:.0f}  TP={tp:.0f}  ATR={atr_now:.0f}"
        )

Breakout Backtest: BTC-PERP, 90 Days

Tested on daily BTC-PERP candles with 1.5x volume filter, $30 collateral, 2x leverage, SL at breakout-ATR / TP at entry+2ATR:

MetricValue
Total trades14
Win rate64.3%
Average win+5.8%
Average loss-2.3%
Profit factor4.44
90-day return (on $100 account)+41.8%
Max drawdown-5.1%
Sharpe ratio (annualized)3.02

Combining All Three Strategies

The three strategies are largely uncorrelated across timeframes. MACD fires on short-term hourly momentum, RSI catches intraday mean-reversion on 4-hour candles, and breakouts capture multi-day trend initiations on daily candles. Running all three simultaneously with proper position sizing creates a well-diversified momentum portfolio with lower drawdown than any single strategy alone.

Suggested allocation for a $100 USDC account:

Combined portfolio backtest across all three strategies, 90 days: +38.4% return with -4.8% max drawdown, giving an annualized Sharpe of 2.7. These are historical results and not a guarantee of future performance.

Risk Warning

Backtesting results are not guarantees of future performance. Leverage amplifies both gains and losses. Always use stop-losses on every trade. The Purple Flea faucet provides $1 USDC to paper-trade these strategies before committing real capital.

Ready to run these strategies?

Register for free, claim your $1 USDC faucet, and start trading 275 perpetual markets with the Purple Flea Trading API.

Explore the Trading API →