feat: TA-Lib candlestick patterns — matching-low/lines/three-methods (part 7 of 9) (#139)

Adds five TA-Lib candlestick patterns, all `Input = Candle`, `Output = f64`
(`+1.0` bullish / `-1.0` bearish / `0.0` no pattern), wired across core,
Python/Node/WASM bindings, fuzz, and tests.

- **Matching Low** (`CDLMATCHINGLOW`) — 2-bar bullish reversal: two black candles in a decline share the same close, signalling selling pressure is exhausting; bullish +1.
- **Long Line** (`CDLLONGLINE`) — a candle whose range beats a rolling average of recent ranges with a body-dominated range; bullish +1 (white) / bearish -1 (black).
- **Short Line** (`CDLSHORTLINE`) — a compact candle whose range falls below the rolling average with a body-dominated range; bullish +1 (white) / bearish -1 (black).
- **Rising Three Methods** (`CDLRISEFALL3METHODS`) — 5-bar bullish continuation: a long white candle, three small bars holding within its range, then a white breakout to new highs; bullish +1.
- **Falling Three Methods** (`CDLRISEFALL3METHODS`) — the bearish mirror: a long black candle, three small bars within its range, then a black breakdown to new lows; bearish -1.

Counter 274 → 279 (mod-count == lib counted block; FAMILIES total 269 → 274).

Stacked on #138 (part 6 of 9); base retargets to `main` as the chain merges.
This commit is contained in:
kingchenc
2026-06-02 17:16:15 +02:00
committed by GitHub
parent 04ae145126
commit c2c85c7ecf
19 changed files with 1285 additions and 41 deletions
@@ -265,6 +265,11 @@ const candleScalar = {
KickingByLength: { make: () => new wickra.KickingByLength(), step: (ind, i) => ind.update(open[i], high[i], low[i], close[i]), batch: (ind) => ind.batch(open, high, low, close) },
LadderBottom: { make: () => new wickra.LadderBottom(), step: (ind, i) => ind.update(open[i], high[i], low[i], close[i]), batch: (ind) => ind.batch(open, high, low, close) },
MatHold: { make: () => new wickra.MatHold(), step: (ind, i) => ind.update(open[i], high[i], low[i], close[i]), batch: (ind) => ind.batch(open, high, low, close) },
MatchingLow: { make: () => new wickra.MatchingLow(), step: (ind, i) => ind.update(open[i], high[i], low[i], close[i]), batch: (ind) => ind.batch(open, high, low, close) },
LongLine: { make: () => new wickra.LongLine(), step: (ind, i) => ind.update(open[i], high[i], low[i], close[i]), batch: (ind) => ind.batch(open, high, low, close) },
ShortLine: { make: () => new wickra.ShortLine(), step: (ind, i) => ind.update(open[i], high[i], low[i], close[i]), batch: (ind) => ind.batch(open, high, low, close) },
RisingThreeMethods: { make: () => new wickra.RisingThreeMethods(), step: (ind, i) => ind.update(open[i], high[i], low[i], close[i]), batch: (ind) => ind.batch(open, high, low, close) },
FallingThreeMethods: { make: () => new wickra.FallingThreeMethods(), step: (ind, i) => ind.update(open[i], high[i], low[i], close[i]), batch: (ind) => ind.batch(open, high, low, close) },
};
for (const [name, d] of Object.entries(candleScalar)) {
+45
View File
@@ -2480,6 +2480,51 @@ export declare class MatHold {
isReady(): boolean
warmupPeriod(): number
}
export type MatchingLowNode = MatchingLow
export declare class MatchingLow {
constructor()
update(open: number, high: number, low: number, close: number): number | null
batch(open: Array<number>, high: Array<number>, low: Array<number>, close: Array<number>): Array<number>
reset(): void
isReady(): boolean
warmupPeriod(): number
}
export type LongLineNode = LongLine
export declare class LongLine {
constructor()
update(open: number, high: number, low: number, close: number): number | null
batch(open: Array<number>, high: Array<number>, low: Array<number>, close: Array<number>): Array<number>
reset(): void
isReady(): boolean
warmupPeriod(): number
}
export type ShortLineNode = ShortLine
export declare class ShortLine {
constructor()
update(open: number, high: number, low: number, close: number): number | null
batch(open: Array<number>, high: Array<number>, low: Array<number>, close: Array<number>): Array<number>
reset(): void
isReady(): boolean
warmupPeriod(): number
}
export type RisingThreeMethodsNode = RisingThreeMethods
export declare class RisingThreeMethods {
constructor()
update(open: number, high: number, low: number, close: number): number | null
batch(open: Array<number>, high: Array<number>, low: Array<number>, close: Array<number>): Array<number>
reset(): void
isReady(): boolean
warmupPeriod(): number
}
export type FallingThreeMethodsNode = FallingThreeMethods
export declare class FallingThreeMethods {
constructor()
update(open: number, high: number, low: number, close: number): number | null
batch(open: Array<number>, high: Array<number>, low: Array<number>, close: Array<number>): Array<number>
reset(): void
isReady(): boolean
warmupPeriod(): number
}
export type OrderBookImbalanceTop1Node = OrderBookImbalanceTop1
export declare class OrderBookImbalanceTop1 {
constructor()
+6 -1
View File
@@ -310,7 +310,7 @@ if (!nativeBinding) {
throw new Error(`Failed to load native binding`)
}
const { version, SMA, EMA, WMA, RSI, DEMA, TEMA, HMA, ROC, TRIX, SMMA, TRIMA, ZLEMA, MOM, CMO, DPO, StdDev, UlcerIndex, VerticalHorizontalFilter, ZScore, McGinleyDynamic, FRAMA, SuperSmoother, FisherTransform, Decycler, CenterOfGravity, CyberneticCycle, InstantaneousTrendline, EhlersStochastic, RVIVolatility, Variance, CoefficientOfVariation, Skewness, Kurtosis, StandardError, DetrendedStdDev, RSquared, MedianAbsoluteDeviation, Autocorrelation, HurstExponent, PearsonCorrelation, Beta, PairwiseBeta, SpearmanCorrelation, PairSpreadZScore, LeadLagCrossCorrelation, Cointegration, RelativeStrengthAB, MACD, BollingerBands, ATR, Stochastic, OBV, ADX, ADXR, CCI, WilliamsR, MFI, PSAR, Keltner, Donchian, VWAP, RollingVWAP, AwesomeOscillator, Aroon, Inertia, ConnorsRSI, LaguerreRSI, SMI, KST, PGO, RVI, AwesomeOscillatorHistogram, STC, ElderImpulse, ZeroLagMACD, CFO, APO, KAMA, EVWMA, Alligator, JMA, VIDYA, ALMA, T3, TSI, PMO, TII, ADL, VolumePriceTrend, ChaikinMoneyFlow, ChaikinOscillator, ForceIndex, NVI, PVI, VolumeOscillator, KVO, WilliamsAD, AnchoredVWAP, DemandIndex, TSV, VZO, MarketFacilitationIndex, EaseOfMovement, SuperTrend, ChandelierExit, ChandeKrollStop, AtrTrailingStop, HiLoActivator, VoltyStop, YoyoExit, DonchianStop, PercentageTrailingStop, StepTrailingStop, RenkoTrailingStop, TypicalPrice, MedianPrice, WeightedClose, LinearRegression, LinRegSlope, AcceleratorOscillator, BalanceOfPower, ChoppinessIndex, TrueRange, ChaikinVolatility, YangZhangVolatility, RogersSatchellVolatility, GarmanKlassVolatility, ParkinsonVolatility, LinRegAngle, BollingerBandwidth, PercentB, NATR, HistoricalVolatility, AroonOscillator, WaveTrend, RWI, Vortex, MassIndex, StochRSI, UltimateOscillator, PPO, Coppock, VWMA, MaEnvelope, AccelerationBands, StarcBands, AtrBands, HurstChannel, LinRegChannel, StandardErrorBands, DoubleBollinger, TtmSqueeze, FractalChaosBands, VwapStdDevBands, ClassicPivots, FibonacciPivots, Camarilla, WoodiePivots, DemarkPivots, WilliamsFractals, ZigZag, TDSetup, TDSequential, TDDeMarker, TDREI, TDPressure, TDCombo, TDCountdown, TDLines, TDRangeProjection, TDDifferential, TDOpen, TDRiskLevel, InverseFisherTransform, DecyclerOscillator, RoofingFilter, EmpiricalModeDecomposition, HilbertDominantCycle, AdaptiveCycle, SineWave, MAMA, FAMA, Ichimoku, HeikinAshi, ValueArea, InitialBalance, OpeningRange, Doji, Hammer, InvertedHammer, HangingMan, ShootingStar, Engulfing, Harami, MorningEveningStar, ThreeSoldiersOrCrows, PiercingDarkCloud, Marubozu, Tweezer, SpinningTop, ThreeInside, ThreeOutside, TwoCrows, UpsideGapTwoCrows, IdenticalThreeCrows, ThreeLineStrike, ThreeStarsInSouth, AbandonedBaby, AdvanceBlock, BeltHold, Breakaway, Counterattack, DojiStar, DragonflyDoji, GravestoneDoji, LongLeggedDoji, RickshawMan, EveningDojiStar, MorningDojiStar, GapSideBySideWhite, HighWave, Hikkake, HikkakeModified, HomingPigeon, OnNeck, InNeck, Thrusting, SeparatingLines, Kicking, KickingByLength, LadderBottom, MatHold, OrderBookImbalanceTop1, OrderBookImbalanceFull, Microprice, QuotedSpread, DepthSlope, OrderBookImbalanceTopN, SignedVolume, CumulativeVolumeDelta, TradeImbalance, EffectiveSpread, RealizedSpread, KylesLambda, Footprint, FundingRate, FundingRateMean, FundingRateZScore, FundingBasis, OpenInterestDelta, OIPriceDivergence, OIWeighted, LongShortRatio, TakerBuySellRatio, LiquidationFeatures, TermStructureBasis, CalendarSpread, SharpeRatio, SortinoRatio, CalmarRatio, OmegaRatio, MaxDrawdown, AverageDrawdown, DrawdownDuration, PainIndex, ValueAtRisk, ConditionalValueAtRisk, ProfitFactor, GainLossRatio, RecoveryFactor, KellyCriterion, TreynorRatio, InformationRatio, Alpha } = nativeBinding
const { version, SMA, EMA, WMA, RSI, DEMA, TEMA, HMA, ROC, TRIX, SMMA, TRIMA, ZLEMA, MOM, CMO, DPO, StdDev, UlcerIndex, VerticalHorizontalFilter, ZScore, McGinleyDynamic, FRAMA, SuperSmoother, FisherTransform, Decycler, CenterOfGravity, CyberneticCycle, InstantaneousTrendline, EhlersStochastic, RVIVolatility, Variance, CoefficientOfVariation, Skewness, Kurtosis, StandardError, DetrendedStdDev, RSquared, MedianAbsoluteDeviation, Autocorrelation, HurstExponent, PearsonCorrelation, Beta, PairwiseBeta, SpearmanCorrelation, PairSpreadZScore, LeadLagCrossCorrelation, Cointegration, RelativeStrengthAB, MACD, BollingerBands, ATR, Stochastic, OBV, ADX, ADXR, CCI, WilliamsR, MFI, PSAR, Keltner, Donchian, VWAP, RollingVWAP, AwesomeOscillator, Aroon, Inertia, ConnorsRSI, LaguerreRSI, SMI, KST, PGO, RVI, AwesomeOscillatorHistogram, STC, ElderImpulse, ZeroLagMACD, CFO, APO, KAMA, EVWMA, Alligator, JMA, VIDYA, ALMA, T3, TSI, PMO, TII, ADL, VolumePriceTrend, ChaikinMoneyFlow, ChaikinOscillator, ForceIndex, NVI, PVI, VolumeOscillator, KVO, WilliamsAD, AnchoredVWAP, DemandIndex, TSV, VZO, MarketFacilitationIndex, EaseOfMovement, SuperTrend, ChandelierExit, ChandeKrollStop, AtrTrailingStop, HiLoActivator, VoltyStop, YoyoExit, DonchianStop, PercentageTrailingStop, StepTrailingStop, RenkoTrailingStop, TypicalPrice, MedianPrice, WeightedClose, LinearRegression, LinRegSlope, AcceleratorOscillator, BalanceOfPower, ChoppinessIndex, TrueRange, ChaikinVolatility, YangZhangVolatility, RogersSatchellVolatility, GarmanKlassVolatility, ParkinsonVolatility, LinRegAngle, BollingerBandwidth, PercentB, NATR, HistoricalVolatility, AroonOscillator, WaveTrend, RWI, Vortex, MassIndex, StochRSI, UltimateOscillator, PPO, Coppock, VWMA, MaEnvelope, AccelerationBands, StarcBands, AtrBands, HurstChannel, LinRegChannel, StandardErrorBands, DoubleBollinger, TtmSqueeze, FractalChaosBands, VwapStdDevBands, ClassicPivots, FibonacciPivots, Camarilla, WoodiePivots, DemarkPivots, WilliamsFractals, ZigZag, TDSetup, TDSequential, TDDeMarker, TDREI, TDPressure, TDCombo, TDCountdown, TDLines, TDRangeProjection, TDDifferential, TDOpen, TDRiskLevel, InverseFisherTransform, DecyclerOscillator, RoofingFilter, EmpiricalModeDecomposition, HilbertDominantCycle, AdaptiveCycle, SineWave, MAMA, FAMA, Ichimoku, HeikinAshi, ValueArea, InitialBalance, OpeningRange, Doji, Hammer, InvertedHammer, HangingMan, ShootingStar, Engulfing, Harami, MorningEveningStar, ThreeSoldiersOrCrows, PiercingDarkCloud, Marubozu, Tweezer, SpinningTop, ThreeInside, ThreeOutside, TwoCrows, UpsideGapTwoCrows, IdenticalThreeCrows, ThreeLineStrike, ThreeStarsInSouth, AbandonedBaby, AdvanceBlock, BeltHold, Breakaway, Counterattack, DojiStar, DragonflyDoji, GravestoneDoji, LongLeggedDoji, RickshawMan, EveningDojiStar, MorningDojiStar, GapSideBySideWhite, HighWave, Hikkake, HikkakeModified, HomingPigeon, OnNeck, InNeck, Thrusting, SeparatingLines, Kicking, KickingByLength, LadderBottom, MatHold, MatchingLow, LongLine, ShortLine, RisingThreeMethods, FallingThreeMethods, OrderBookImbalanceTop1, OrderBookImbalanceFull, Microprice, QuotedSpread, DepthSlope, OrderBookImbalanceTopN, SignedVolume, CumulativeVolumeDelta, TradeImbalance, EffectiveSpread, RealizedSpread, KylesLambda, Footprint, FundingRate, FundingRateMean, FundingRateZScore, FundingBasis, OpenInterestDelta, OIPriceDivergence, OIWeighted, LongShortRatio, TakerBuySellRatio, LiquidationFeatures, TermStructureBasis, CalendarSpread, SharpeRatio, SortinoRatio, CalmarRatio, OmegaRatio, MaxDrawdown, AverageDrawdown, DrawdownDuration, PainIndex, ValueAtRisk, ConditionalValueAtRisk, ProfitFactor, GainLossRatio, RecoveryFactor, KellyCriterion, TreynorRatio, InformationRatio, Alpha } = nativeBinding
module.exports.version = version
module.exports.SMA = SMA
@@ -545,6 +545,11 @@ module.exports.Kicking = Kicking
module.exports.KickingByLength = KickingByLength
module.exports.LadderBottom = LadderBottom
module.exports.MatHold = MatHold
module.exports.MatchingLow = MatchingLow
module.exports.LongLine = LongLine
module.exports.ShortLine = ShortLine
module.exports.RisingThreeMethods = RisingThreeMethods
module.exports.FallingThreeMethods = FallingThreeMethods
module.exports.OrderBookImbalanceTop1 = OrderBookImbalanceTop1
module.exports.OrderBookImbalanceFull = OrderBookImbalanceFull
module.exports.Microprice = Microprice
+13
View File
@@ -8799,6 +8799,19 @@ node_candle_pattern!(KickingNode, wc::Kicking, "Kicking");
node_candle_pattern!(KickingByLengthNode, wc::KickingByLength, "KickingByLength");
node_candle_pattern!(LadderBottomNode, wc::LadderBottom, "LadderBottom");
node_candle_pattern!(MatHoldNode, wc::MatHold, "MatHold");
node_candle_pattern!(MatchingLowNode, wc::MatchingLow, "MatchingLow");
node_candle_pattern!(LongLineNode, wc::LongLine, "LongLine");
node_candle_pattern!(ShortLineNode, wc::ShortLine, "ShortLine");
node_candle_pattern!(
RisingThreeMethodsNode,
wc::RisingThreeMethods,
"RisingThreeMethods"
);
node_candle_pattern!(
FallingThreeMethodsNode,
wc::FallingThreeMethods,
"FallingThreeMethods"
);
// ============================== Microstructure: Order Book ==============================
//
+10
View File
@@ -270,6 +270,11 @@ from ._wickra import (
KickingByLength,
LadderBottom,
MatHold,
MatchingLow,
LongLine,
ShortLine,
RisingThreeMethods,
FallingThreeMethods,
# Microstructure: order book
OrderBookImbalanceTop1,
OrderBookImbalanceTopN,
@@ -567,6 +572,11 @@ __all__ = [
"KickingByLength",
"LadderBottom",
"MatHold",
"MatchingLow",
"LongLine",
"ShortLine",
"RisingThreeMethods",
"FallingThreeMethods",
# Microstructure: order book
"OrderBookImbalanceTop1",
"OrderBookImbalanceTopN",
+18
View File
@@ -11662,6 +11662,19 @@ candle_pattern_no_param!(PyKicking, wc::Kicking, "Kicking");
candle_pattern_no_param!(PyKickingByLength, wc::KickingByLength, "KickingByLength");
candle_pattern_no_param!(PyLadderBottom, wc::LadderBottom, "LadderBottom");
candle_pattern_no_param!(PyMatHold, wc::MatHold, "MatHold");
candle_pattern_no_param!(PyMatchingLow, wc::MatchingLow, "MatchingLow");
candle_pattern_no_param!(PyLongLine, wc::LongLine, "LongLine");
candle_pattern_no_param!(PyShortLine, wc::ShortLine, "ShortLine");
candle_pattern_no_param!(
PyRisingThreeMethods,
wc::RisingThreeMethods,
"RisingThreeMethods"
);
candle_pattern_no_param!(
PyFallingThreeMethods,
wc::FallingThreeMethods,
"FallingThreeMethods"
);
// ============================== Microstructure: Order Book ==============================
//
// Order-book indicators consume a depth snapshot rather than OHLCV. Streaming
@@ -14188,6 +14201,11 @@ fn _wickra(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<PyKickingByLength>()?;
m.add_class::<PyLadderBottom>()?;
m.add_class::<PyMatHold>()?;
m.add_class::<PyMatchingLow>()?;
m.add_class::<PyLongLine>()?;
m.add_class::<PyShortLine>()?;
m.add_class::<PyRisingThreeMethods>()?;
m.add_class::<PyFallingThreeMethods>()?;
// Microstructure: order book.
m.add_class::<PyOrderBookImbalanceTop1>()?;
m.add_class::<PyOrderBookImbalanceTopN>()?;
@@ -643,6 +643,26 @@ CANDLE_SCALAR = {
lambda: ta.MatHold(),
lambda ind, h, l, c, v: ind.batch(c, h, l, c),
),
"MatchingLow": (
lambda: ta.MatchingLow(),
lambda ind, h, l, c, v: ind.batch(c, h, l, c),
),
"LongLine": (
lambda: ta.LongLine(),
lambda ind, h, l, c, v: ind.batch(c, h, l, c),
),
"ShortLine": (
lambda: ta.ShortLine(),
lambda ind, h, l, c, v: ind.batch(c, h, l, c),
),
"RisingThreeMethods": (
lambda: ta.RisingThreeMethods(),
lambda ind, h, l, c, v: ind.batch(c, h, l, c),
),
"FallingThreeMethods": (
lambda: ta.FallingThreeMethods(),
lambda ind, h, l, c, v: ind.batch(c, h, l, c),
),
}
@@ -1891,6 +1911,46 @@ def test_mat_hold_reference():
assert t.update((15.0, 15.1, 14.4, 14.5, 1.0, 3)) == pytest.approx(0.0)
assert t.update((14.5, 17.1, 14.4, 17.0, 1.0, 4)) == pytest.approx(1.0)
def test_matching_low_reference():
t = ta.MatchingLow()
assert t.update((15.0, 15.1, 9.9, 10.0, 1.0, 0)) == pytest.approx(0.0)
assert t.update((13.0, 13.1, 9.9, 10.0, 1.0, 1)) == pytest.approx(1.0)
def test_long_line_reference():
t = ta.LongLine()
# Five quiet bars fill the rolling range average, then a wide solid white bar.
for ts in range(5):
assert t.update((10.0, 10.5, 9.5, 10.2, 1.0, ts)) == pytest.approx(0.0)
assert t.update((10.0, 13.0, 9.9, 12.9, 1.0, 5)) == pytest.approx(1.0)
def test_short_line_reference():
t = ta.ShortLine()
# Five wide bars fill the rolling range average, then a compact solid white bar.
for ts in range(5):
assert t.update((10.0, 13.0, 9.5, 12.9, 1.0, ts)) == pytest.approx(0.0)
assert t.update((10.0, 11.0, 9.9, 10.9, 1.0, 5)) == pytest.approx(1.0)
def test_rising_three_methods_reference():
t = ta.RisingThreeMethods()
assert t.update((10.0, 15.1, 9.9, 15.0, 1.0, 0)) == pytest.approx(0.0)
assert t.update((14.0, 14.1, 12.9, 13.0, 1.0, 1)) == pytest.approx(0.0)
assert t.update((13.5, 13.6, 12.4, 12.5, 1.0, 2)) == pytest.approx(0.0)
assert t.update((13.0, 13.1, 11.9, 12.0, 1.0, 3)) == pytest.approx(0.0)
assert t.update((12.5, 16.1, 12.4, 16.0, 1.0, 4)) == pytest.approx(1.0)
def test_falling_three_methods_reference():
t = ta.FallingThreeMethods()
assert t.update((15.0, 15.1, 9.9, 10.0, 1.0, 0)) == pytest.approx(0.0)
assert t.update((11.0, 12.1, 10.9, 12.0, 1.0, 1)) == pytest.approx(0.0)
assert t.update((11.5, 12.6, 11.4, 12.5, 1.0, 2)) == pytest.approx(0.0)
assert t.update((12.0, 13.1, 11.9, 13.0, 1.0, 3)) == pytest.approx(0.0)
assert t.update((12.5, 12.6, 8.9, 9.0, 1.0, 4)) == pytest.approx(-1.0)
# --- Lifecycle ------------------------------------------------------------
+13
View File
@@ -6376,6 +6376,19 @@ wasm_candle_pattern!(WasmKicking, wc::Kicking, Kicking);
wasm_candle_pattern!(WasmKickingByLength, wc::KickingByLength, KickingByLength);
wasm_candle_pattern!(WasmLadderBottom, wc::LadderBottom, LadderBottom);
wasm_candle_pattern!(WasmMatHold, wc::MatHold, MatHold);
wasm_candle_pattern!(WasmMatchingLow, wc::MatchingLow, MatchingLow);
wasm_candle_pattern!(WasmLongLine, wc::LongLine, LongLine);
wasm_candle_pattern!(WasmShortLine, wc::ShortLine, ShortLine);
wasm_candle_pattern!(
WasmRisingThreeMethods,
wc::RisingThreeMethods,
RisingThreeMethods
);
wasm_candle_pattern!(
WasmFallingThreeMethods,
wc::FallingThreeMethods,
FallingThreeMethods
);
// ============================== Microstructure: Order Book ==============================
//