feat: TA-Lib candlestick patterns — tasuki-gap/unique-three-river/marubozu-pair/concealing-baby-swallow (part 9 of 9) (#141)
The final batch of the TA-Lib candlestick roadmap. Adds five patterns, each a streaming `Indicator<Input = Candle, Output = f64>` emitting the family's uniform `±1.0 / 0.0` sign convention, fully wired across the Rust core, Python / Node / WASM bindings, fuzz target and reference tests. - **Tasuki Gap** (`CDLTASUKIGAP`) — a 3-bar continuation: two same-coloured candles gap in the trend direction, then an opposite candle opens within the second body and closes back into the gap without filling it; upside +1, downside -1. - **Unique Three River** (`CDLUNIQUE3RIVER`) — a 3-bar bullish reversal: a long black candle, a black candle probing a new low with its body inside the first, then a small white candle held below it; bullish +1. - **Closing Marubozu** (`CDLCLOSINGMARUBOZU`) — a single long-bodied candle with no shadow on the close end; +1 (white, closes at the high) or -1 (black, closes at the low). - **Opening Marubozu** — a single long-bodied candle with no shadow on the open end; +1 (white, opens at the low) or -1 (black, opens at the high). No direct TA-Lib equivalent — completes the pair with the closing marubozu. - **Concealing Baby Swallow** (`CDLCONCEALBABYSWALL`) — a rare 4-bar bullish capitulation: two black marubozu, a black candle gapping down with an upper shadow into the second, then a large black candle engulfing it entirely; bullish +1. Body and shadow thresholds follow the geometric house style (fixed fractions of the bar range) rather than TA-Lib's rolling averages. Counter 284 → 289 (mod-count == lib counted block; FAMILIES total 279 → 284). Stacked on #140 (`feat/cdl-gap-methods`); base retargets to `main` as the stack merges down.
This commit is contained in:
@@ -280,6 +280,11 @@ from ._wickra import (
|
||||
StalledPattern,
|
||||
StickSandwich,
|
||||
Takuri,
|
||||
ClosingMarubozu,
|
||||
OpeningMarubozu,
|
||||
TasukiGap,
|
||||
UniqueThreeRiver,
|
||||
ConcealingBabySwallow,
|
||||
# Microstructure: order book
|
||||
OrderBookImbalanceTop1,
|
||||
OrderBookImbalanceTopN,
|
||||
@@ -587,6 +592,11 @@ __all__ = [
|
||||
"StalledPattern",
|
||||
"StickSandwich",
|
||||
"Takuri",
|
||||
"ClosingMarubozu",
|
||||
"OpeningMarubozu",
|
||||
"TasukiGap",
|
||||
"UniqueThreeRiver",
|
||||
"ConcealingBabySwallow",
|
||||
# Microstructure: order book
|
||||
"OrderBookImbalanceTop1",
|
||||
"OrderBookImbalanceTopN",
|
||||
|
||||
@@ -11688,6 +11688,15 @@ candle_pattern_no_param!(
|
||||
candle_pattern_no_param!(PyStalledPattern, wc::StalledPattern, "StalledPattern");
|
||||
candle_pattern_no_param!(PyStickSandwich, wc::StickSandwich, "StickSandwich");
|
||||
candle_pattern_no_param!(PyTakuri, wc::Takuri, "Takuri");
|
||||
candle_pattern_no_param!(PyClosingMarubozu, wc::ClosingMarubozu, "ClosingMarubozu");
|
||||
candle_pattern_no_param!(PyOpeningMarubozu, wc::OpeningMarubozu, "OpeningMarubozu");
|
||||
candle_pattern_no_param!(PyTasukiGap, wc::TasukiGap, "TasukiGap");
|
||||
candle_pattern_no_param!(PyUniqueThreeRiver, wc::UniqueThreeRiver, "UniqueThreeRiver");
|
||||
candle_pattern_no_param!(
|
||||
PyConcealingBabySwallow,
|
||||
wc::ConcealingBabySwallow,
|
||||
"ConcealingBabySwallow"
|
||||
);
|
||||
// ============================== Microstructure: Order Book ==============================
|
||||
//
|
||||
// Order-book indicators consume a depth snapshot rather than OHLCV. Streaming
|
||||
@@ -14224,6 +14233,11 @@ fn _wickra(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
m.add_class::<PyStalledPattern>()?;
|
||||
m.add_class::<PyStickSandwich>()?;
|
||||
m.add_class::<PyTakuri>()?;
|
||||
m.add_class::<PyClosingMarubozu>()?;
|
||||
m.add_class::<PyOpeningMarubozu>()?;
|
||||
m.add_class::<PyTasukiGap>()?;
|
||||
m.add_class::<PyUniqueThreeRiver>()?;
|
||||
m.add_class::<PyConcealingBabySwallow>()?;
|
||||
// Microstructure: order book.
|
||||
m.add_class::<PyOrderBookImbalanceTop1>()?;
|
||||
m.add_class::<PyOrderBookImbalanceTopN>()?;
|
||||
|
||||
@@ -683,6 +683,26 @@ CANDLE_SCALAR = {
|
||||
lambda: ta.Takuri(),
|
||||
lambda ind, h, l, c, v: ind.batch(c, h, l, c),
|
||||
),
|
||||
"ClosingMarubozu": (
|
||||
lambda: ta.ClosingMarubozu(),
|
||||
lambda ind, h, l, c, v: ind.batch(c, h, l, c),
|
||||
),
|
||||
"OpeningMarubozu": (
|
||||
lambda: ta.OpeningMarubozu(),
|
||||
lambda ind, h, l, c, v: ind.batch(c, h, l, c),
|
||||
),
|
||||
"TasukiGap": (
|
||||
lambda: ta.TasukiGap(),
|
||||
lambda ind, h, l, c, v: ind.batch(c, h, l, c),
|
||||
),
|
||||
"UniqueThreeRiver": (
|
||||
lambda: ta.UniqueThreeRiver(),
|
||||
lambda ind, h, l, c, v: ind.batch(c, h, l, c),
|
||||
),
|
||||
"ConcealingBabySwallow": (
|
||||
lambda: ta.ConcealingBabySwallow(),
|
||||
lambda ind, h, l, c, v: ind.batch(c, h, l, c),
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
@@ -2004,6 +2024,38 @@ def test_takuri_reference():
|
||||
t = ta.Takuri()
|
||||
assert t.update((10.0, 10.05, 7.0, 10.0, 1.0, 0)) == pytest.approx(1.0)
|
||||
|
||||
|
||||
def test_closing_marubozu_reference():
|
||||
t = ta.ClosingMarubozu()
|
||||
assert t.update((10.5, 15.0, 10.0, 15.0, 1.0, 0)) == pytest.approx(1.0)
|
||||
|
||||
|
||||
def test_opening_marubozu_reference():
|
||||
t = ta.OpeningMarubozu()
|
||||
assert t.update((10.0, 15.0, 10.0, 14.5, 1.0, 0)) == pytest.approx(1.0)
|
||||
|
||||
|
||||
def test_tasuki_gap_reference():
|
||||
t = ta.TasukiGap()
|
||||
assert t.update((10.0, 11.2, 9.8, 11.0, 1.0, 0)) == pytest.approx(0.0)
|
||||
assert t.update((12.0, 14.0, 11.9, 13.5, 1.0, 1)) == pytest.approx(0.0)
|
||||
assert t.update((13.0, 13.1, 11.4, 11.5, 1.0, 2)) == pytest.approx(1.0)
|
||||
|
||||
|
||||
def test_unique_three_river_reference():
|
||||
t = ta.UniqueThreeRiver()
|
||||
assert t.update((15.0, 15.1, 10.0, 10.5, 1.0, 0)) == pytest.approx(0.0)
|
||||
assert t.update((14.0, 14.1, 9.0, 11.0, 1.0, 1)) == pytest.approx(0.0)
|
||||
assert t.update((10.2, 10.9, 9.5, 10.4, 1.0, 2)) == pytest.approx(1.0)
|
||||
|
||||
|
||||
def test_concealing_baby_swallow_reference():
|
||||
t = ta.ConcealingBabySwallow()
|
||||
assert t.update((20.0, 20.1, 14.9, 15.0, 1.0, 0)) == pytest.approx(0.0)
|
||||
assert t.update((16.0, 16.1, 11.9, 12.0, 1.0, 1)) == pytest.approx(0.0)
|
||||
assert t.update((11.0, 13.0, 9.9, 10.0, 1.0, 2)) == pytest.approx(0.0)
|
||||
assert t.update((14.0, 14.1, 8.9, 9.0, 1.0, 3)) == pytest.approx(1.0)
|
||||
|
||||
# --- Lifecycle ------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user