8b4a847d24
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".
186 lines
5.4 KiB
Markdown
186 lines
5.4 KiB
Markdown
# 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](Streaming-vs-Batch.md).
|
||
|
||
## 1. RSI mean reversion
|
||
|
||
Enter when RSI crosses out of an extreme; flatten when it returns to
|
||
neutral.
|
||
|
||
```python
|
||
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.
|
||
|
||
```rust
|
||
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.
|
||
|
||
```python
|
||
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.
|
||
|
||
```python
|
||
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`](Data-Layer.md) rolls one
|
||
candle stream up into a coarser one; the canonical example is
|
||
`examples/rust/src/bin/multi_timeframe.rs`.
|
||
|
||
```rust
|
||
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:
|
||
|
||
```python
|
||
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.
|
||
|
||
```rust
|
||
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](Indicator-Chaining.md) for the chained-warmup rule
|
||
and three-stage examples.
|
||
|
||
## See also
|
||
|
||
- [Indicators Overview](Indicators-Overview.md) — pick the right indicator
|
||
for the question you are asking.
|
||
- [Streaming vs Batch](Streaming-vs-Batch.md) — why these recipes work
|
||
bit-identically in both modes.
|
||
- [Data Layer](Data-Layer.md) — `Resampler` and the bundled BTCUSDT
|
||
datasets for live multi-timeframe work.
|