feat: TA-Lib candlestick patterns — hikkake-mod/pigeon/neck-lines (part 5 of 9) (#137)

* feat: add hikkake-modified, homing-pigeon and neck-line candlestick patterns

Five patterns, all `Input = Candle`, `Output = f64`:

- Modified Hikkake (CDLHIKKAKEMOD) — a close-confirmed Hikkake: an inside bar
  then a breakout that closes back inside the inside-bar range; bullish +1,
  bearish -1.
- Homing Pigeon (CDLHOMINGPIGEON) — two black candles, the second a small body
  inside the first, a bullish reversal; +1.
- On-Neck (CDLONNECK) — long black bar then a white bar closing at its low (the
  neckline), a bearish continuation; -1.
- In-Neck (CDLINNECK) — long black bar then a white bar closing just into its
  body, a bearish continuation; -1.
- Thrusting (CDLTHRUSTING) — long black bar then a white bar closing well into
  but below the midpoint of its body, a bearish continuation; -1.

Counter 264 -> 269 (mod-count == lib counted block; FAMILIES total 259 -> 264).

* chore: sync indicator count to 269

---------

Co-authored-by: wickra-bot <wickra-bot@users.noreply.github.com>
This commit is contained in:
kingchenc
2026-06-02 17:03:49 +02:00
committed by GitHub
parent d43bc9ddf3
commit e4ca9c3f8f
19 changed files with 1106 additions and 28 deletions
+10
View File
@@ -260,6 +260,11 @@ from ._wickra import (
GapSideBySideWhite,
HighWave,
Hikkake,
HikkakeModified,
HomingPigeon,
OnNeck,
InNeck,
Thrusting,
# Microstructure: order book
OrderBookImbalanceTop1,
OrderBookImbalanceTopN,
@@ -547,6 +552,11 @@ __all__ = [
"GapSideBySideWhite",
"HighWave",
"Hikkake",
"HikkakeModified",
"HomingPigeon",
"OnNeck",
"InNeck",
"Thrusting",
# Microstructure: order book
"OrderBookImbalanceTop1",
"OrderBookImbalanceTopN",
+10
View File
@@ -11652,6 +11652,11 @@ candle_pattern_no_param!(
);
candle_pattern_no_param!(PyHighWave, wc::HighWave, "HighWave");
candle_pattern_no_param!(PyHikkake, wc::Hikkake, "Hikkake");
candle_pattern_no_param!(PyHikkakeModified, wc::HikkakeModified, "HikkakeModified");
candle_pattern_no_param!(PyHomingPigeon, wc::HomingPigeon, "HomingPigeon");
candle_pattern_no_param!(PyOnNeck, wc::OnNeck, "OnNeck");
candle_pattern_no_param!(PyInNeck, wc::InNeck, "InNeck");
candle_pattern_no_param!(PyThrusting, wc::Thrusting, "Thrusting");
// ============================== Microstructure: Order Book ==============================
//
// Order-book indicators consume a depth snapshot rather than OHLCV. Streaming
@@ -14168,6 +14173,11 @@ fn _wickra(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<PyGapSideBySideWhite>()?;
m.add_class::<PyHighWave>()?;
m.add_class::<PyHikkake>()?;
m.add_class::<PyHikkakeModified>()?;
m.add_class::<PyHomingPigeon>()?;
m.add_class::<PyOnNeck>()?;
m.add_class::<PyInNeck>()?;
m.add_class::<PyThrusting>()?;
// Microstructure: order book.
m.add_class::<PyOrderBookImbalanceTop1>()?;
m.add_class::<PyOrderBookImbalanceTopN>()?;
@@ -603,6 +603,26 @@ CANDLE_SCALAR = {
lambda: ta.Hikkake(),
lambda ind, h, l, c, v: ind.batch(c, h, l, c),
),
"HikkakeModified": (
lambda: ta.HikkakeModified(),
lambda ind, h, l, c, v: ind.batch(c, h, l, c),
),
"HomingPigeon": (
lambda: ta.HomingPigeon(),
lambda ind, h, l, c, v: ind.batch(c, h, l, c),
),
"OnNeck": (
lambda: ta.OnNeck(),
lambda ind, h, l, c, v: ind.batch(c, h, l, c),
),
"InNeck": (
lambda: ta.InNeck(),
lambda ind, h, l, c, v: ind.batch(c, h, l, c),
),
"Thrusting": (
lambda: ta.Thrusting(),
lambda ind, h, l, c, v: ind.batch(c, h, l, c),
),
}
@@ -1784,6 +1804,37 @@ def test_hikkake_reference():
assert t.update((11.0, 13.0, 8.0, 12.0, 1.0, 1)) == pytest.approx(0.0)
assert t.update((9.0, 12.0, 6.0, 7.0, 1.0, 2)) == pytest.approx(1.0)
def test_hikkake_modified_reference():
t = ta.HikkakeModified()
assert t.update((10.0, 15.0, 5.0, 12.0, 1.0, 0)) == pytest.approx(0.0)
assert t.update((11.0, 13.0, 8.0, 12.0, 1.0, 1)) == pytest.approx(0.0)
assert t.update((9.0, 12.0, 6.0, 9.0, 1.0, 2)) == pytest.approx(1.0)
def test_homing_pigeon_reference():
t = ta.HomingPigeon()
assert t.update((15.0, 15.1, 9.9, 10.0, 1.0, 0)) == pytest.approx(0.0)
assert t.update((14.0, 14.1, 10.9, 11.0, 1.0, 1)) == pytest.approx(1.0)
def test_on_neck_reference():
t = ta.OnNeck()
assert t.update((15.0, 15.1, 9.0, 10.0, 1.0, 0)) == pytest.approx(0.0)
assert t.update((7.0, 9.1, 6.9, 9.0, 1.0, 1)) == pytest.approx(-1.0)
def test_in_neck_reference():
t = ta.InNeck()
assert t.update((15.0, 15.1, 9.0, 10.0, 1.0, 0)) == pytest.approx(0.0)
assert t.update((7.0, 10.3, 6.9, 10.2, 1.0, 1)) == pytest.approx(-1.0)
def test_thrusting_reference():
t = ta.Thrusting()
assert t.update((15.0, 15.1, 9.0, 10.0, 1.0, 0)) == pytest.approx(0.0)
assert t.update((7.0, 11.6, 6.9, 11.5, 1.0, 1)) == pytest.approx(-1.0)
# --- Lifecycle ------------------------------------------------------------