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.
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)
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.
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:
| Metric | Value |
|---|---|
| Total trades | 47 |
| Win rate | 53.2% |
| Average win | +2.4% |
| Average loss | -1.6% |
| Profit factor | 1.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
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.
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%:
| Metric | Value |
|---|---|
| Total trades | 29 |
| Win rate | 58.6% |
| Average win | +2.1% |
| Average loss | -1.4% |
| Profit factor | 2.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
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.
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:
| Metric | Value |
|---|---|
| Total trades | 14 |
| Win rate | 64.3% |
| Average win | +5.8% |
| Average loss | -2.3% |
| Profit factor | 4.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:
- MACD crossover: $25 collateral per trade, 3x leverage, 1-hour candles
- RSI strategy: $20 collateral per trade, 2x leverage, 4-hour candles
- Breakout strategy: $30 collateral per trade, 2x leverage, daily candles
- Reserve: $25 in USDC for margin maintenance and new opportunities
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.
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 →