Docs · Strategy Patterns

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

  1. Momentum / Trend following
  2. Mean reversion
  3. Market making
  4. Arbitrage
  5. Dollar-cost averaging (DCA)
  6. News / event-driven

01Momentum / Trend following

Directional Beginner-friendly Medium edge

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

Directional Medium difficulty High edge in chop

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

Non-directional Advanced High edge in illiquid venues

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

Non-directional Advanced Edge eroded by HFT

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)

Directional (long-only) Trivial difficulty Genuinely effective

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

Directional Advanced Edge fading

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:

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 →

More reading