Skip to content

Cookbook

kingchenc edited this page May 23, 2026 · 2 revisions

Cookbook

Practical strategy recipes built on Wickra's streaming indicators. Each recipe is a small, runnable snippet you can drop into a backtest loop or a live trading bot. Both paths share the same indicator state, so the same recipe works in either mode — see Streaming vs Batch.

1. RSI mean reversion

Enter when RSI crosses out of an extreme; flatten when it returns to neutral.

import wickra as ta

rsi = ta.RSI(14)
position = 0  # 0 flat, +1 long, −1 short
for price in price_feed:
    v = rsi.update(price)
    if v is None:
        continue
    if position == 0 and v < 30:
        position = 1
        print(f"BUY at {price:.2f}")
    elif position == 1 and v > 50:
        position = 0
        print(f"EXIT long at {price:.2f}")
    elif position == 0 and v > 70:
        position = -1
        print(f"SHORT at {price:.2f}")
    elif position == -1 and v < 50:
        position = 0
        print(f"COVER short at {price:.2f}")

2. MACD histogram crossover

Trade in the direction of a MACD-histogram sign change. Zero-crossings of the histogram (MACD − signal) are the canonical trigger and lead the slower MACD-vs-signal line cross.

use wickra::{Indicator, MacdIndicator};

let mut macd = MacdIndicator::classic(); // (12, 26, 9)
let mut last_hist: Option<f64> = None;
for &price in &prices {
    if let Some(v) = macd.update(price) {
        if let Some(prev) = last_hist {
            if prev <= 0.0 && v.histogram > 0.0 {
                println!("BUY: MACD histogram turned positive at {price:.2}");
            } else if prev >= 0.0 && v.histogram < 0.0 {
                println!("SELL: MACD histogram turned negative at {price:.2}");
            }
        }
        last_hist = Some(v.histogram);
    }
}

3. Bollinger band breakout

Trade in the direction of a band-piercing close, taking the bands as a dynamic support / resistance.

import wickra as ta

bb = ta.BollingerBands(20, 2.0)
for price in price_feed:
    out = bb.update(price)
    if out is None:
        continue
    upper, _middle, lower, _stddev = out
    if price > upper:
        print(f"BREAKOUT (long): {price:.2f} > upper {upper:.2f}")
    elif price < lower:
        print(f"BREAKOUT (short): {price:.2f} < lower {lower:.2f}")

4. ADX-gated trend filter

Take EMA-crossover signals only when ADX confirms a trend is in place. This is a textbook way to silence whipsaws in a ranging market.

import wickra as ta

ema_fast = ta.EMA(20)
ema_slow = ta.EMA(50)
adx = ta.ADX(14)

for high, low, close in candle_feed:
    f = ema_fast.update(close)
    s = ema_slow.update(close)
    a = adx.update(high, low, close)   # (plus_di, minus_di, adx) or None
    if f is None or s is None or a is None:
        continue
    _, _, adx_v = a
    if adx_v < 25:
        continue  # ranging market — skip
    if f > s:
        print(f"LONG: EMA20 > EMA50, ADX={adx_v:.1f}")
    elif f < s:
        print(f"SHORT: EMA20 < EMA50, ADX={adx_v:.1f}")

5. Multi-timeframe confirmation

Only take a 1-minute entry when the 1-hour trend agrees. With Wickra you keep one streaming indicator per timeframe and feed each only the candles that belong to it. wickra-data's Resampler rolls one candle stream up into a coarser one; the canonical example is examples/rust/src/bin/multi_timeframe.rs.

use wickra::{Indicator, Rsi};

let mut rsi_1m = Rsi::new(14)?;
let mut rsi_1h = Rsi::new(14)?;

for candle in one_min_candles {
    let fast = rsi_1m.update(candle.close);

    if candle.is_hour_close {
        let slow = rsi_1h.update(candle.close);
        if let (Some(f), Some(s)) = (fast, slow) {
            if f > 70.0 && s > 50.0 {
                println!("strong overbought (1m {f:.1} / 1h {s:.1})");
            } else if f < 30.0 && s < 50.0 {
                println!("strong oversold (1m {f:.1} / 1h {s:.1})");
            }
        }
    }
}

6. SuperTrend trailing stop

SuperTrend is a single-line ATR-banded trailing stop with explicit flip logic — drop it into a long-only loop to manage exits:

import wickra as ta

st = ta.SuperTrend(10, 3.0)
position = 0  # 0 flat, +1 long
for high, low, close in candle_feed:
    out = st.update(high, low, close)
    if out is None:
        continue
    value, direction = out
    if direction > 0 and position == 0:
        position = 1
        print(f"BUY at {close:.2f}, stop={value:.2f}")
    elif direction < 0 and position == 1:
        position = 0
        print(f"EXIT at {close:.2f} (SuperTrend flipped)")

7. Chained indicators

When you want an indicator computed over the output of another, use the Rust Chain combinator. The chain itself implements Indicator, so you can nest, stack, and feed it into anything that takes an indicator.

use wickra::{BatchExt, Chain, Ema, Rsi};

// RSI(7) of EMA(14)-smoothed closes.
let mut chain = Chain::new(Ema::new(14)?, Rsi::new(7)?);
let out: Vec<Option<f64>> = chain.batch(&prices);

See Indicator Chaining for the chained-warmup rule and three-stage examples.

See also

  • Indicators Overview — pick the right indicator for the question you are asking.
  • Streaming vs Batch — why these recipes work bit-identically in both modes.
  • Data LayerResampler and the bundled BTCUSDT datasets for live multi-timeframe work.

Wickra Wiki

Home

Getting started

Reference

Guides

Moving Averages

Price Oscillators

Momentum Oscillators

Trend & Directional

Price Oscillators

Volatility & Bands

Bands & Channels

Trailing Stops

Volume

Price Statistics

Clone this wiki locally