docs(wiki): add Cookbook, TA-Lib migration table and FAQ
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".
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user