Six strategy patterns that actually work
There are infinite trading strategies. Most of them are variations on six core patterns. This guide walks through each — when it works, when it fails, and a code skeleton you can paste into WatchDog Bot and adapt.
The six patterns
01Momentum / Trend following
The bet: when prices have been moving in one direction, they'll keep moving in that direction long enough for you to capture some of it. Buy strength, sell weakness.
Classic implementations: moving average crossover, breakout from N-day high, RSI > 70 / < 30 signals.
When it works
- Strong directional regimes (post-news, post-Fed)
- Multi-day or multi-week trends in liquid assets
- Combined with a volatility filter (only trade when ATR > threshold)
- Diversified across many assets (some will catch trends)
When it fails
- Sideways / chop markets (most of the time, actually)
- Without stop losses — one bad trend reversal wipes weeks of profit
- Over-fit parameters (5/20 MA looked great on 2023 BTC, useless on 2025)
- Without diversification (concentrated in one asset)
Skeleton
import time
from collections import deque
import wd, ccxt
conn = wd.connection("Binance")
ex = ccxt.binance({"apiKey": conn.api_key, "secret": conn.api_secret, "enableRateLimit": True})
short_window, long_window = 12, 26
short = deque(maxlen=short_window)
long = deque(maxlen=long_window)
position = 0
while True:
try:
price = float(ex.fetch_ticker("BTC/USDT")["last"])
short.append(price); long.append(price)
if len(long) < long_window:
time.sleep(300); continue
s, l = sum(short)/len(short), sum(long)/len(long)
wd.log.info("price=%.2f short=%.2f long=%.2f position=%d", price, s, l, position)
if s > l * 1.001 and position == 0:
wd.log.info("BUY signal")
if not wd.is_demo(): ex.create_market_buy_order("BTC/USDT", 0.001)
position = 1
elif s < l * 0.999 and position == 1:
wd.log.info("SELL signal")
if not wd.is_demo(): ex.create_market_sell_order("BTC/USDT", 0.001)
position = 0
time.sleep(300)
except Exception as e:
wd.log.error("tick failed: %s", e); time.sleep(60)
Key parameters to tune: the two MA windows, the crossover threshold (1.001 above to reduce noise), the polling interval, and the position size.
02Mean reversion
The bet: opposite of momentum — when prices stretch far from their recent average, they tend to snap back. Buy weakness, sell strength.
Classic implementations: Bollinger Band re-entry, z-score of price relative to N-period mean, pairs trading on correlated assets.
When it works
- Range-bound markets (most of the time)
- Stablecoin / yield-bearing pairs (USDC vs USDT, stETH vs ETH)
- Highly correlated pairs (XAU/EUR vs XAU/USD basis)
- With strict stop losses — if mean-reversion fails, it fails fast
When it fails
- Trending regimes (you catch a falling knife)
- "Black swan" events that break the relationship
- Without position limits — you double down forever
- If your "mean" is calculated over too short a window
Skeleton (z-score reversion)
import time
import numpy as np
from collections import deque
import wd, ccxt
conn = wd.connection("Coinbase")
ex = ccxt.coinbase({"apiKey": conn.api_key, "secret": conn.api_secret})
window = 60
prices = deque(maxlen=window)
position = 0 # +1 long, -1 short, 0 flat
Z_ENTER, Z_EXIT = 2.0, 0.5
while True:
try:
p = float(ex.fetch_ticker("ETH/USD")["last"])
prices.append(p)
if len(prices) < window:
time.sleep(60); continue
mean = np.mean(prices); std = np.std(prices)
z = (p - mean) / std if std > 0 else 0
wd.log.info("p=%.2f mean=%.2f z=%+.2f pos=%+d", p, mean, z, position)
# Enter when z is extreme; exit when it returns to mean
if z < -Z_ENTER and position == 0:
wd.log.info("BUY — price %.1f std below mean", abs(z))
position = 1
elif z > Z_ENTER and position == 0:
wd.log.info("SELL — price %.1f std above mean", z)
position = -1
elif abs(z) < Z_EXIT and position != 0:
wd.log.info("EXIT — z returned to %.2f", z)
position = 0
time.sleep(60)
except Exception as e:
wd.log.error("tick failed: %s", e); time.sleep(60)
03Market making
The bet: not on price direction at all. You profit by being the counterparty to impatient traders, earning the bid-ask spread on every round trip.
Classic implementations: simple quote-skew (covered in our Kalshi MM guide), Avellaneda-Stoikov, more complex inventory models.
When it works
- Illiquid markets with wide spreads (10–50 bps in crypto, 5–15¢ in prediction markets)
- Markets dominated by impatient directional traders (Kalshi, low-cap altcoins)
- Stable underlying — no major directional moves
- You have an inventory limit and skew quotes to enforce it
When it fails
- Tight-spread liquid markets (BTC/USDT — HFT firms own it)
- Major directional moves (you get adverse-selected on one side)
- News events you don't see fast enough
- Without inventory limits — you accumulate one-sided positions and blow up
Full implementation with inventory skew + adverse selection defenses: Kalshi Market Making Strategies.
04Arbitrage
The bet: the same asset trades at different prices in two places. Buy the cheap one, sell the expensive one, lock in the spread. Risk-free in theory; latency-bound in practice.
Classic implementations: cross-exchange spot arbitrage, triangular arb across three pairs, basis trading (spot vs futures), funding-rate harvesting.
When it works
- Cross-exchange spreads in volatile periods (Coinbase often diverges from Binance during US news)
- Basis trades when funding rates are extreme
- Triangular arb on illiquid altcoin triplets
- With co-located infrastructure (AWS regions near the exchanges)
When it fails
- "Easy" arbitrage in liquid pairs is already gone — HFT firms close it in milliseconds
- If withdrawal/deposit times prevent you from rebalancing inventory
- If fees + slippage eat the spread (most retail "arbs" net negative)
- Counterparty risk — one of your exchanges goes down mid-trade
Skeleton (spread scanner)
import time
import wd, ccxt
binance = ccxt.binance({"enableRateLimit": True})
coinbase = ccxt.coinbase({"enableRateLimit": True})
THRESHOLD_BPS = 30
while True:
try:
a = float(binance.fetch_ticker("BTC/USDT")["last"])
b = float(coinbase.fetch_ticker("BTC/USD")["last"])
spread = abs(a - b) / min(a, b) * 10000
wd.log.info("Binance %.2f Coinbase %.2f spread %.1f bps", a, b, spread)
if spread > THRESHOLD_BPS:
wd.log.warning("ARB OPPORTUNITY — would buy %s, sell %s",
"Binance" if a < b else "Coinbase",
"Coinbase" if a < b else "Binance")
# Execution: buy on lower, sell on higher.
# CAUTION: account for fees, slippage, and latency before going live.
time.sleep(5)
except Exception as e:
wd.log.error(e); time.sleep(15)
05Dollar-cost averaging (DCA)
The bet: you don't try to time the market. You buy a fixed dollar amount on a schedule and let cost averaging do the work. Boring. Often beats active traders.
When it works
- Assets in long-term uptrend (BTC/ETH historically)
- When you can't predict tops/bottoms (i.e., almost always)
- Counters emotional buying-the-top / selling-the-bottom
- Tax-efficient — each lot has a defined cost basis
When it fails
- Assets in long-term downtrend (you average down forever)
- Without a position limit (one bad asset becomes 80% of your portfolio)
- If "buy the dip" overrides the schedule (psychology defeats the rule)
Full skeleton in the examples library.
06News / event-driven
The bet: markets react to news. If you can read news faster than humans (or interpret it better with an LLM), you can take positions before the price moves.
Classic implementations: headline scanners with LLM classifiers, sentiment scoring, economic calendar bots, social media signal extraction.
When it works
- Scheduled events with known outcomes (Fed meetings, CPI prints)
- Prediction markets where you have an information edge
- Social-driven assets where sentiment correlates with price (memecoins, crypto)
- With high confidence thresholds (only trade when the LLM is very sure)
When it fails
- "News" priced in before you see it (markets are efficient at known events)
- LLM hallucinations on ambiguous headlines
- Latency between headline drop and your trade (Bloomberg + Reuters customers see it first)
- Acting on low-confidence signals — you'll lose to noise
Full skeleton (with LLM classifier) in the examples library.
Choosing a pattern
A rough decision tree:
- You want to start simple and learn: DCA. Boring, useful, hard to mess up.
- You believe in the long-term direction of an asset: DCA + occasional momentum entry when conviction is high.
- You have an information edge (news, signals, expertise): News-driven, but be honest about latency.
- You like math and have patience: Market making — especially on Kalshi where retail edge exists.
- You enjoy chasing micro-edges: Arbitrage — be ready to invest in infrastructure.
- You're somewhere between novice and serious quant: Mean reversion on highly correlated pairs.
The strategy you can stick to for 12 months beats the strategy you'll abandon after a 20% drawdown.
Build any of these in WatchDog Bot
All six patterns shown above run as native Python bots. Free trial, no credit card.
Start Free Trial →
WatchDog Bot