Three content gaps in the wiki: there was no migration story for users porting from TA-Lib, no strategy cookbook, and no FAQ. Add all three as self-contained pages and link them from Home.md's "Wiki contents". * docs/wiki/TA-Lib-Migration.md — full one-to-one mapping table from every common talib.X(...) call to the equivalent Wickra expression, plus a "what Wickra has that TA-Lib does not" / "what TA-Lib has that Wickra does not (yet)" delta. * docs/wiki/Cookbook.md — seven concrete strategy recipes (RSI mean reversion, MACD histogram crossover, Bollinger breakout, ADX-gated trend, multi-timeframe confirmation, SuperTrend trailing stop, Chain<EMA, RSI>) with Rust or Python snippets. * docs/wiki/FAQ.md — common questions on warmup, NaN handling, thread safety, installation, performance and comparing Wickra to TA-Lib / pandas-ta / talipp / finta. Also extend the [Unreleased] CHANGELOG entry that records the examples/<lang>/ restructure with the wiki additions; Home.md gains three new bullets under "Wiki contents".
5.4 KiB
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 Layer —
Resamplerand the bundled BTCUSDT datasets for live multi-timeframe work.