feat: TA-Lib candlestick patterns — Doji family (part 3 of 9) (#134)
* feat: add Doji-family candlestick patterns Five single-/two-bar Doji patterns, all `Input = Candle`, `Output = f64`: - Doji Star (CDLDOJISTAR) — a long body followed by a doji gapping away in the trend direction; bullish +1 (after a black bar), bearish -1 (after a white bar). - Dragonfly Doji (CDLDRAGONFLYDOJI) — a doji opening and closing at the high with a long lower shadow; bullish +1. - Gravestone Doji (CDLGRAVESTONEDOJI) — a doji opening and closing at the low with a long upper shadow; bearish -1. - Long-Legged Doji (CDLLONGLEGGEDDOJI) — a doji with long shadows on both sides; a non-directional indecision flag, +1 on detection. - Rickshaw Man (CDLRICKSHAWMAN) — a long-legged doji with the body centred in the range; a non-directional indecision flag, +1 on detection. Counter 254 -> 259 (mod-count == lib counted block; FAMILIES total 249 -> 254). * chore: sync indicator count to 259 --------- Co-authored-by: wickra-bot <wickra-bot@users.noreply.github.com>
This commit is contained in:
@@ -37,6 +37,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- **Belt-hold** — a single-bar reversal that opens at one extreme of its range and runs the other way; bullish +1, bearish -1 (`CDLBELTHOLD`).
|
- **Belt-hold** — a single-bar reversal that opens at one extreme of its range and runs the other way; bullish +1, bearish -1 (`CDLBELTHOLD`).
|
||||||
- **Breakaway** — a 5-bar reversal that gaps with the trend, drifts two more bars, then snaps back into the bar1/bar2 body gap; bullish +1, bearish -1 (`CDLBREAKAWAY`).
|
- **Breakaway** — a 5-bar reversal that gaps with the trend, drifts two more bars, then snaps back into the bar1/bar2 body gap; bullish +1, bearish -1 (`CDLBREAKAWAY`).
|
||||||
- **Counterattack** — a 2-bar reversal where an opposite-coloured second bar closes level with the first (the counterattack line); bullish +1, bearish -1 (`CDLCOUNTERATTACK`).
|
- **Counterattack** — a 2-bar reversal where an opposite-coloured second bar closes level with the first (the counterattack line); bullish +1, bearish -1 (`CDLCOUNTERATTACK`).
|
||||||
|
- **Doji Star** — a long body followed by a doji gapping away in the trend direction; bullish +1, bearish -1 (`CDLDOJISTAR`).
|
||||||
|
- **Dragonfly Doji** — a doji opening and closing at the high with a long lower shadow, a bullish reversal; +1 (`CDLDRAGONFLYDOJI`).
|
||||||
|
- **Gravestone Doji** — a doji opening and closing at the low with a long upper shadow, a bearish reversal; -1 (`CDLGRAVESTONEDOJI`).
|
||||||
|
- **Long-Legged Doji** — a doji with long shadows on both sides, an indecision signal; +1 detection (`CDLLONGLEGGEDDOJI`).
|
||||||
|
- **Rickshaw Man** — a long-legged doji with the body centred in the range, an indecision signal; +1 detection (`CDLRICKSHAWMAN`).
|
||||||
- **Derivatives family — funding & open interest (part 1).** A new family of
|
- **Derivatives family — funding & open interest (part 1).** A new family of
|
||||||
indicators that consume a perpetual / futures tick (`DerivativesTick`,
|
indicators that consume a perpetual / futures tick (`DerivativesTick`,
|
||||||
bundling funding rate, mark / index / futures price, open interest,
|
bundling funding rate, mark / index / futures price, open interest,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://wickra.org"><img src="https://raw.githubusercontent.com/wickra-lib/.github/main/profile/wickra-banner.webp?v=254" alt="Wickra — streaming-first technical indicators" width="100%"></a>
|
<a href="https://wickra.org"><img src="https://raw.githubusercontent.com/wickra-lib/.github/main/profile/wickra-banner.webp?v=259" alt="Wickra — streaming-first technical indicators" width="100%"></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
[](https://github.com/wickra-lib/wickra/actions/workflows/ci.yml)
|
[](https://github.com/wickra-lib/wickra/actions/workflows/ci.yml)
|
||||||
@@ -47,7 +47,7 @@ Full documentation lives at **[docs.wickra.org](https://docs.wickra.org)**:
|
|||||||
[Node](https://docs.wickra.org/Quickstart-Node),
|
[Node](https://docs.wickra.org/Quickstart-Node),
|
||||||
[WASM](https://docs.wickra.org/Quickstart-WASM).
|
[WASM](https://docs.wickra.org/Quickstart-WASM).
|
||||||
- **Indicators** — a per-indicator deep dive (formula, parameters, warmup) for
|
- **Indicators** — a per-indicator deep dive (formula, parameters, warmup) for
|
||||||
every one of the 254 indicators; start at the
|
every one of the 259 indicators; start at the
|
||||||
[indicators overview](https://docs.wickra.org/Indicators-Overview).
|
[indicators overview](https://docs.wickra.org/Indicators-Overview).
|
||||||
- **Reference** — [warmup periods](https://docs.wickra.org/Warmup-Periods),
|
- **Reference** — [warmup periods](https://docs.wickra.org/Warmup-Periods),
|
||||||
[streaming vs batch](https://docs.wickra.org/Streaming-vs-Batch),
|
[streaming vs batch](https://docs.wickra.org/Streaming-vs-Batch),
|
||||||
@@ -135,7 +135,7 @@ python -m benchmarks.compare_libraries
|
|||||||
|
|
||||||
## Indicators
|
## Indicators
|
||||||
|
|
||||||
254 streaming-first indicators across eighteen families. Every one passes the
|
259 streaming-first indicators across eighteen families. Every one passes the
|
||||||
`batch == streaming` equivalence test, reference-value tests, and reset
|
`batch == streaming` equivalence test, reference-value tests, and reset
|
||||||
semantics tests. Each has a per-indicator deep dive (formula, parameters,
|
semantics tests. Each has a per-indicator deep dive (formula, parameters,
|
||||||
warmup) at [docs.wickra.org](https://docs.wickra.org/Indicators-Overview).
|
warmup) at [docs.wickra.org](https://docs.wickra.org/Indicators-Overview).
|
||||||
@@ -155,7 +155,7 @@ warmup) at [docs.wickra.org](https://docs.wickra.org/Indicators-Overview).
|
|||||||
| Pivots & S/R | Classic Pivots, Fibonacci Pivots, Camarilla, Woodie Pivots, DeMark Pivots, Williams Fractals, ZigZag |
|
| Pivots & S/R | Classic Pivots, Fibonacci Pivots, Camarilla, Woodie Pivots, DeMark Pivots, Williams Fractals, ZigZag |
|
||||||
| DeMark | TD Setup, TD Sequential, TD DeMarker, TD REI, TD Pressure, TD Combo, TD Countdown, TD Lines, TD Range Projection, TD Differential, TD Open, TD Risk Level |
|
| DeMark | TD Setup, TD Sequential, TD DeMarker, TD REI, TD Pressure, TD Combo, TD Countdown, TD Lines, TD Range Projection, TD Differential, TD Open, TD Risk Level |
|
||||||
| Ichimoku & Charts | Ichimoku Kinko Hyo (Tenkan, Kijun, Senkou A/B, Chikou), Heikin-Ashi |
|
| Ichimoku & Charts | Ichimoku Kinko Hyo (Tenkan, Kijun, Senkou A/B, Chikou), Heikin-Ashi |
|
||||||
| Candlestick Patterns | Doji, Hammer, Inverted Hammer, Hanging Man, Shooting Star, Engulfing, Harami, Morning/Evening Star, Three White Soldiers/Black Crows, Piercing Line/Dark Cloud Cover, Marubozu, Tweezer, Spinning Top, Three Inside Up/Down, Three Outside Up/Down, Two Crows, Upside Gap Two Crows, Identical Three Crows, Three Line Strike, Three Stars in the South, Abandoned Baby, Advance Block, Belt-hold, Breakaway, Counterattack |
|
| Candlestick Patterns | Doji, Hammer, Inverted Hammer, Hanging Man, Shooting Star, Engulfing, Harami, Morning/Evening Star, Three White Soldiers/Black Crows, Piercing Line/Dark Cloud Cover, Marubozu, Tweezer, Spinning Top, Three Inside Up/Down, Three Outside Up/Down, Two Crows, Upside Gap Two Crows, Identical Three Crows, Three Line Strike, Three Stars in the South, Abandoned Baby, Advance Block, Belt-hold, Breakaway, Counterattack, Doji Star, Dragonfly Doji, Gravestone Doji, Long-Legged Doji, Rickshaw Man |
|
||||||
| Microstructure | Order-Book Imbalance (Top-1 / Top-N / Full), Microprice, Quoted Spread, Depth Slope, Signed Volume, Cumulative Volume Delta, Trade Imbalance, Effective Spread, Realized Spread, Kyle's Lambda, Footprint |
|
| Microstructure | Order-Book Imbalance (Top-1 / Top-N / Full), Microprice, Quoted Spread, Depth Slope, Signed Volume, Cumulative Volume Delta, Trade Imbalance, Effective Spread, Realized Spread, Kyle's Lambda, Footprint |
|
||||||
| Derivatives | Funding Rate, Funding Rate Mean, Funding Rate Z-Score, Funding Basis, Open-Interest Delta, OI / Price Divergence, OI-Weighted Price, Long/Short Ratio, Taker Buy/Sell Ratio, Liquidation Features, Term-Structure Basis, Calendar Spread |
|
| Derivatives | Funding Rate, Funding Rate Mean, Funding Rate Z-Score, Funding Basis, Open-Interest Delta, OI / Price Divergence, OI-Weighted Price, Long/Short Ratio, Taker Buy/Sell Ratio, Liquidation Features, Term-Structure Basis, Calendar Spread |
|
||||||
| Market Profile | Value Area (POC / VAH / VAL), Initial Balance, Opening Range |
|
| Market Profile | Value Area (POC / VAH / VAL), Initial Balance, Opening Range |
|
||||||
@@ -238,7 +238,7 @@ A Python live-trading example using the public `websockets` package lives at
|
|||||||
```
|
```
|
||||||
wickra/
|
wickra/
|
||||||
├── crates/
|
├── crates/
|
||||||
│ ├── wickra-core/ core engine + all 254 indicators
|
│ ├── wickra-core/ core engine + all 259 indicators
|
||||||
│ ├── wickra/ top-level facade crate (publishes on crates.io) + benches/
|
│ ├── wickra/ top-level facade crate (publishes on crates.io) + benches/
|
||||||
│ └── wickra-data/ CSV reader, tick aggregator, live exchange feeds
|
│ └── wickra-data/ CSV reader, tick aggregator, live exchange feeds
|
||||||
├── bindings/
|
├── bindings/
|
||||||
|
|||||||
@@ -245,6 +245,11 @@ const candleScalar = {
|
|||||||
BeltHold: { make: () => new wickra.BeltHold(), step: (ind, i) => ind.update(open[i], high[i], low[i], close[i]), batch: (ind) => ind.batch(open, high, low, close) },
|
BeltHold: { make: () => new wickra.BeltHold(), step: (ind, i) => ind.update(open[i], high[i], low[i], close[i]), batch: (ind) => ind.batch(open, high, low, close) },
|
||||||
Breakaway: { make: () => new wickra.Breakaway(), step: (ind, i) => ind.update(open[i], high[i], low[i], close[i]), batch: (ind) => ind.batch(open, high, low, close) },
|
Breakaway: { make: () => new wickra.Breakaway(), step: (ind, i) => ind.update(open[i], high[i], low[i], close[i]), batch: (ind) => ind.batch(open, high, low, close) },
|
||||||
Counterattack: { make: () => new wickra.Counterattack(), step: (ind, i) => ind.update(open[i], high[i], low[i], close[i]), batch: (ind) => ind.batch(open, high, low, close) },
|
Counterattack: { make: () => new wickra.Counterattack(), step: (ind, i) => ind.update(open[i], high[i], low[i], close[i]), batch: (ind) => ind.batch(open, high, low, close) },
|
||||||
|
DojiStar: { make: () => new wickra.DojiStar(), step: (ind, i) => ind.update(open[i], high[i], low[i], close[i]), batch: (ind) => ind.batch(open, high, low, close) },
|
||||||
|
DragonflyDoji: { make: () => new wickra.DragonflyDoji(), step: (ind, i) => ind.update(open[i], high[i], low[i], close[i]), batch: (ind) => ind.batch(open, high, low, close) },
|
||||||
|
GravestoneDoji: { make: () => new wickra.GravestoneDoji(), step: (ind, i) => ind.update(open[i], high[i], low[i], close[i]), batch: (ind) => ind.batch(open, high, low, close) },
|
||||||
|
LongLeggedDoji: { make: () => new wickra.LongLeggedDoji(), step: (ind, i) => ind.update(open[i], high[i], low[i], close[i]), batch: (ind) => ind.batch(open, high, low, close) },
|
||||||
|
RickshawMan: { make: () => new wickra.RickshawMan(), 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)) {
|
for (const [name, d] of Object.entries(candleScalar)) {
|
||||||
|
|||||||
Vendored
+45
@@ -2300,6 +2300,51 @@ export declare class Counterattack {
|
|||||||
isReady(): boolean
|
isReady(): boolean
|
||||||
warmupPeriod(): number
|
warmupPeriod(): number
|
||||||
}
|
}
|
||||||
|
export type DojiStarNode = DojiStar
|
||||||
|
export declare class DojiStar {
|
||||||
|
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 DragonflyDojiNode = DragonflyDoji
|
||||||
|
export declare class DragonflyDoji {
|
||||||
|
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 GravestoneDojiNode = GravestoneDoji
|
||||||
|
export declare class GravestoneDoji {
|
||||||
|
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 LongLeggedDojiNode = LongLeggedDoji
|
||||||
|
export declare class LongLeggedDoji {
|
||||||
|
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 RickshawManNode = RickshawMan
|
||||||
|
export declare class RickshawMan {
|
||||||
|
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 type OrderBookImbalanceTop1Node = OrderBookImbalanceTop1
|
||||||
export declare class OrderBookImbalanceTop1 {
|
export declare class OrderBookImbalanceTop1 {
|
||||||
constructor()
|
constructor()
|
||||||
|
|||||||
@@ -310,7 +310,7 @@ if (!nativeBinding) {
|
|||||||
throw new Error(`Failed to load native binding`)
|
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, 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, 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.version = version
|
||||||
module.exports.SMA = SMA
|
module.exports.SMA = SMA
|
||||||
@@ -525,6 +525,11 @@ module.exports.AdvanceBlock = AdvanceBlock
|
|||||||
module.exports.BeltHold = BeltHold
|
module.exports.BeltHold = BeltHold
|
||||||
module.exports.Breakaway = Breakaway
|
module.exports.Breakaway = Breakaway
|
||||||
module.exports.Counterattack = Counterattack
|
module.exports.Counterattack = Counterattack
|
||||||
|
module.exports.DojiStar = DojiStar
|
||||||
|
module.exports.DragonflyDoji = DragonflyDoji
|
||||||
|
module.exports.GravestoneDoji = GravestoneDoji
|
||||||
|
module.exports.LongLeggedDoji = LongLeggedDoji
|
||||||
|
module.exports.RickshawMan = RickshawMan
|
||||||
module.exports.OrderBookImbalanceTop1 = OrderBookImbalanceTop1
|
module.exports.OrderBookImbalanceTop1 = OrderBookImbalanceTop1
|
||||||
module.exports.OrderBookImbalanceFull = OrderBookImbalanceFull
|
module.exports.OrderBookImbalanceFull = OrderBookImbalanceFull
|
||||||
module.exports.Microprice = Microprice
|
module.exports.Microprice = Microprice
|
||||||
|
|||||||
@@ -8775,6 +8775,11 @@ node_candle_pattern!(AdvanceBlockNode, wc::AdvanceBlock, "AdvanceBlock");
|
|||||||
node_candle_pattern!(BeltHoldNode, wc::BeltHold, "BeltHold");
|
node_candle_pattern!(BeltHoldNode, wc::BeltHold, "BeltHold");
|
||||||
node_candle_pattern!(BreakawayNode, wc::Breakaway, "Breakaway");
|
node_candle_pattern!(BreakawayNode, wc::Breakaway, "Breakaway");
|
||||||
node_candle_pattern!(CounterattackNode, wc::Counterattack, "Counterattack");
|
node_candle_pattern!(CounterattackNode, wc::Counterattack, "Counterattack");
|
||||||
|
node_candle_pattern!(DojiStarNode, wc::DojiStar, "DojiStar");
|
||||||
|
node_candle_pattern!(DragonflyDojiNode, wc::DragonflyDoji, "DragonflyDoji");
|
||||||
|
node_candle_pattern!(GravestoneDojiNode, wc::GravestoneDoji, "GravestoneDoji");
|
||||||
|
node_candle_pattern!(LongLeggedDojiNode, wc::LongLeggedDoji, "LongLeggedDoji");
|
||||||
|
node_candle_pattern!(RickshawManNode, wc::RickshawMan, "RickshawMan");
|
||||||
|
|
||||||
// ============================== Microstructure: Order Book ==============================
|
// ============================== Microstructure: Order Book ==============================
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -250,6 +250,11 @@ from ._wickra import (
|
|||||||
BeltHold,
|
BeltHold,
|
||||||
Breakaway,
|
Breakaway,
|
||||||
Counterattack,
|
Counterattack,
|
||||||
|
DojiStar,
|
||||||
|
DragonflyDoji,
|
||||||
|
GravestoneDoji,
|
||||||
|
LongLeggedDoji,
|
||||||
|
RickshawMan,
|
||||||
# Microstructure: order book
|
# Microstructure: order book
|
||||||
OrderBookImbalanceTop1,
|
OrderBookImbalanceTop1,
|
||||||
OrderBookImbalanceTopN,
|
OrderBookImbalanceTopN,
|
||||||
@@ -527,6 +532,11 @@ __all__ = [
|
|||||||
"BeltHold",
|
"BeltHold",
|
||||||
"Breakaway",
|
"Breakaway",
|
||||||
"Counterattack",
|
"Counterattack",
|
||||||
|
"DojiStar",
|
||||||
|
"DragonflyDoji",
|
||||||
|
"GravestoneDoji",
|
||||||
|
"LongLeggedDoji",
|
||||||
|
"RickshawMan",
|
||||||
# Microstructure: order book
|
# Microstructure: order book
|
||||||
"OrderBookImbalanceTop1",
|
"OrderBookImbalanceTop1",
|
||||||
"OrderBookImbalanceTopN",
|
"OrderBookImbalanceTopN",
|
||||||
|
|||||||
@@ -11638,6 +11638,11 @@ candle_pattern_no_param!(PyAdvanceBlock, wc::AdvanceBlock, "AdvanceBlock");
|
|||||||
candle_pattern_no_param!(PyBeltHold, wc::BeltHold, "BeltHold");
|
candle_pattern_no_param!(PyBeltHold, wc::BeltHold, "BeltHold");
|
||||||
candle_pattern_no_param!(PyBreakaway, wc::Breakaway, "Breakaway");
|
candle_pattern_no_param!(PyBreakaway, wc::Breakaway, "Breakaway");
|
||||||
candle_pattern_no_param!(PyCounterattack, wc::Counterattack, "Counterattack");
|
candle_pattern_no_param!(PyCounterattack, wc::Counterattack, "Counterattack");
|
||||||
|
candle_pattern_no_param!(PyDojiStar, wc::DojiStar, "DojiStar");
|
||||||
|
candle_pattern_no_param!(PyDragonflyDoji, wc::DragonflyDoji, "DragonflyDoji");
|
||||||
|
candle_pattern_no_param!(PyGravestoneDoji, wc::GravestoneDoji, "GravestoneDoji");
|
||||||
|
candle_pattern_no_param!(PyLongLeggedDoji, wc::LongLeggedDoji, "LongLeggedDoji");
|
||||||
|
candle_pattern_no_param!(PyRickshawMan, wc::RickshawMan, "RickshawMan");
|
||||||
// ============================== Microstructure: Order Book ==============================
|
// ============================== Microstructure: Order Book ==============================
|
||||||
//
|
//
|
||||||
// Order-book indicators consume a depth snapshot rather than OHLCV. Streaming
|
// Order-book indicators consume a depth snapshot rather than OHLCV. Streaming
|
||||||
@@ -14144,6 +14149,11 @@ fn _wickra(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|||||||
m.add_class::<PyBeltHold>()?;
|
m.add_class::<PyBeltHold>()?;
|
||||||
m.add_class::<PyBreakaway>()?;
|
m.add_class::<PyBreakaway>()?;
|
||||||
m.add_class::<PyCounterattack>()?;
|
m.add_class::<PyCounterattack>()?;
|
||||||
|
m.add_class::<PyDojiStar>()?;
|
||||||
|
m.add_class::<PyDragonflyDoji>()?;
|
||||||
|
m.add_class::<PyGravestoneDoji>()?;
|
||||||
|
m.add_class::<PyLongLeggedDoji>()?;
|
||||||
|
m.add_class::<PyRickshawMan>()?;
|
||||||
// Microstructure: order book.
|
// Microstructure: order book.
|
||||||
m.add_class::<PyOrderBookImbalanceTop1>()?;
|
m.add_class::<PyOrderBookImbalanceTop1>()?;
|
||||||
m.add_class::<PyOrderBookImbalanceTopN>()?;
|
m.add_class::<PyOrderBookImbalanceTopN>()?;
|
||||||
|
|||||||
@@ -563,6 +563,26 @@ CANDLE_SCALAR = {
|
|||||||
lambda: ta.Counterattack(),
|
lambda: ta.Counterattack(),
|
||||||
lambda ind, h, l, c, v: ind.batch(c, h, l, c),
|
lambda ind, h, l, c, v: ind.batch(c, h, l, c),
|
||||||
),
|
),
|
||||||
|
"DojiStar": (
|
||||||
|
lambda: ta.DojiStar(),
|
||||||
|
lambda ind, h, l, c, v: ind.batch(c, h, l, c),
|
||||||
|
),
|
||||||
|
"DragonflyDoji": (
|
||||||
|
lambda: ta.DragonflyDoji(),
|
||||||
|
lambda ind, h, l, c, v: ind.batch(c, h, l, c),
|
||||||
|
),
|
||||||
|
"GravestoneDoji": (
|
||||||
|
lambda: ta.GravestoneDoji(),
|
||||||
|
lambda ind, h, l, c, v: ind.batch(c, h, l, c),
|
||||||
|
),
|
||||||
|
"LongLeggedDoji": (
|
||||||
|
lambda: ta.LongLeggedDoji(),
|
||||||
|
lambda ind, h, l, c, v: ind.batch(c, h, l, c),
|
||||||
|
),
|
||||||
|
"RickshawMan": (
|
||||||
|
lambda: ta.RickshawMan(),
|
||||||
|
lambda ind, h, l, c, v: ind.batch(c, h, l, c),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1685,6 +1705,32 @@ def test_counterattack_reference():
|
|||||||
assert t.update((20.0, 20.1, 14.9, 15.0, 1.0, 0)) == pytest.approx(0.0)
|
assert t.update((20.0, 20.1, 14.9, 15.0, 1.0, 0)) == pytest.approx(0.0)
|
||||||
assert t.update((10.0, 15.1, 9.9, 15.0, 1.0, 1)) == pytest.approx(1.0)
|
assert t.update((10.0, 15.1, 9.9, 15.0, 1.0, 1)) == pytest.approx(1.0)
|
||||||
|
|
||||||
|
|
||||||
|
def test_doji_star_reference():
|
||||||
|
t = ta.DojiStar()
|
||||||
|
assert t.update((20.0, 20.2, 14.8, 15.0, 1.0, 0)) == pytest.approx(0.0)
|
||||||
|
assert t.update((13.0, 13.1, 12.9, 13.0, 1.0, 1)) == pytest.approx(1.0)
|
||||||
|
|
||||||
|
|
||||||
|
def test_dragonfly_doji_reference():
|
||||||
|
t = ta.DragonflyDoji()
|
||||||
|
assert t.update((10.0, 10.05, 6.0, 10.0, 1.0, 0)) == pytest.approx(1.0)
|
||||||
|
|
||||||
|
|
||||||
|
def test_gravestone_doji_reference():
|
||||||
|
t = ta.GravestoneDoji()
|
||||||
|
assert t.update((10.0, 14.0, 9.95, 10.0, 1.0, 0)) == pytest.approx(-1.0)
|
||||||
|
|
||||||
|
|
||||||
|
def test_long_legged_doji_reference():
|
||||||
|
t = ta.LongLeggedDoji()
|
||||||
|
assert t.update((10.0, 12.0, 8.0, 10.05, 1.0, 0)) == pytest.approx(1.0)
|
||||||
|
|
||||||
|
|
||||||
|
def test_rickshaw_man_reference():
|
||||||
|
t = ta.RickshawMan()
|
||||||
|
assert t.update((10.0, 12.0, 8.0, 10.0, 1.0, 0)) == pytest.approx(1.0)
|
||||||
|
|
||||||
# --- Lifecycle ------------------------------------------------------------
|
# --- Lifecycle ------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6352,6 +6352,11 @@ wasm_candle_pattern!(WasmAdvanceBlock, wc::AdvanceBlock, AdvanceBlock);
|
|||||||
wasm_candle_pattern!(WasmBeltHold, wc::BeltHold, BeltHold);
|
wasm_candle_pattern!(WasmBeltHold, wc::BeltHold, BeltHold);
|
||||||
wasm_candle_pattern!(WasmBreakaway, wc::Breakaway, Breakaway);
|
wasm_candle_pattern!(WasmBreakaway, wc::Breakaway, Breakaway);
|
||||||
wasm_candle_pattern!(WasmCounterattack, wc::Counterattack, Counterattack);
|
wasm_candle_pattern!(WasmCounterattack, wc::Counterattack, Counterattack);
|
||||||
|
wasm_candle_pattern!(WasmDojiStar, wc::DojiStar, DojiStar);
|
||||||
|
wasm_candle_pattern!(WasmDragonflyDoji, wc::DragonflyDoji, DragonflyDoji);
|
||||||
|
wasm_candle_pattern!(WasmGravestoneDoji, wc::GravestoneDoji, GravestoneDoji);
|
||||||
|
wasm_candle_pattern!(WasmLongLeggedDoji, wc::LongLeggedDoji, LongLeggedDoji);
|
||||||
|
wasm_candle_pattern!(WasmRickshawMan, wc::RickshawMan, RickshawMan);
|
||||||
|
|
||||||
// ============================== Microstructure: Order Book ==============================
|
// ============================== Microstructure: Order Book ==============================
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -0,0 +1,205 @@
|
|||||||
|
//! Doji Star candlestick pattern.
|
||||||
|
|
||||||
|
use crate::ohlcv::Candle;
|
||||||
|
use crate::traits::Indicator;
|
||||||
|
|
||||||
|
/// Doji Star — a 2-bar reversal warning. A long trending body is followed by a
|
||||||
|
/// doji whose tiny body gaps away in the direction of the trend, the indecision
|
||||||
|
/// hinting the move is about to turn.
|
||||||
|
///
|
||||||
|
/// ```text
|
||||||
|
/// long body = |close − open| >= 0.5 * (high − low) (bar1)
|
||||||
|
/// doji = |close − open| <= 0.1 * (high − low) (bar2)
|
||||||
|
/// bullish (+1.0): bar1 black, doji body gaps DOWN below it (max(o2,c2) < close1)
|
||||||
|
/// bearish (−1.0): bar1 white, doji body gaps UP above it (min(o2,c2) > close1)
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Output is `+1.0` (bullish star, after a black bar) or `−1.0` (bearish star,
|
||||||
|
/// after a white bar) when the pattern completes, and `0.0` otherwise. The first
|
||||||
|
/// bar always returns `0.0` because the two-bar window is not yet filled. Doji
|
||||||
|
/// thresholds follow the geometric house style (fixed half-range body for the
|
||||||
|
/// long bar, tenth-range body for the doji) rather than TA-Lib's rolling
|
||||||
|
/// averages. Pattern-shape check only — no trend filter is applied; combine with
|
||||||
|
/// a trend indicator for actionable signals.
|
||||||
|
///
|
||||||
|
/// # Signed ±1 encoding
|
||||||
|
///
|
||||||
|
/// This detector emits the uniform candlestick sign convention shared across the
|
||||||
|
/// pattern family — `+1.0` bullish, `−1.0` bearish, `0.0` no pattern — so it
|
||||||
|
/// drops straight into a machine-learning feature matrix where the bullish and
|
||||||
|
/// bearish variants occupy a single dimension.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use wickra_core::{Candle, DojiStar, Indicator};
|
||||||
|
///
|
||||||
|
/// let mut indicator = DojiStar::new();
|
||||||
|
/// // Long black bar, then a doji gapping down -> bullish star.
|
||||||
|
/// indicator.update(Candle::new(20.0, 20.2, 14.8, 15.0, 1.0, 0).unwrap());
|
||||||
|
/// let out = indicator
|
||||||
|
/// .update(Candle::new(13.0, 13.1, 12.9, 13.0, 1.0, 1).unwrap());
|
||||||
|
/// assert_eq!(out, Some(1.0));
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct DojiStar {
|
||||||
|
prev: Option<Candle>,
|
||||||
|
has_emitted: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DojiStar {
|
||||||
|
/// Construct a new Doji Star detector.
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
prev: None,
|
||||||
|
has_emitted: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Indicator for DojiStar {
|
||||||
|
type Input = Candle;
|
||||||
|
type Output = f64;
|
||||||
|
|
||||||
|
fn update(&mut self, candle: Candle) -> Option<f64> {
|
||||||
|
self.has_emitted = true;
|
||||||
|
let prev = self.prev;
|
||||||
|
self.prev = Some(candle);
|
||||||
|
let Some(bar1) = prev else {
|
||||||
|
return Some(0.0);
|
||||||
|
};
|
||||||
|
let range1 = bar1.high - bar1.low;
|
||||||
|
let range2 = candle.high - candle.low;
|
||||||
|
if range1 <= 0.0 || range2 <= 0.0 {
|
||||||
|
return Some(0.0);
|
||||||
|
}
|
||||||
|
let body1 = bar1.close - bar1.open;
|
||||||
|
if body1.abs() < 0.5 * range1 {
|
||||||
|
return Some(0.0);
|
||||||
|
}
|
||||||
|
if (candle.close - candle.open).abs() > 0.1 * range2 {
|
||||||
|
return Some(0.0);
|
||||||
|
}
|
||||||
|
let doji_top = candle.open.max(candle.close);
|
||||||
|
let doji_bottom = candle.open.min(candle.close);
|
||||||
|
// Bullish: long black bar, doji body gaps down below it.
|
||||||
|
if body1 < 0.0 && doji_top < bar1.close {
|
||||||
|
return Some(1.0);
|
||||||
|
}
|
||||||
|
// Bearish: long white bar, doji body gaps up above it.
|
||||||
|
if body1 > 0.0 && doji_bottom > bar1.close {
|
||||||
|
return Some(-1.0);
|
||||||
|
}
|
||||||
|
Some(0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) {
|
||||||
|
self.prev = None;
|
||||||
|
self.has_emitted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn warmup_period(&self) -> usize {
|
||||||
|
2
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_ready(&self) -> bool {
|
||||||
|
self.has_emitted
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"DojiStar"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::traits::BatchExt;
|
||||||
|
|
||||||
|
fn c(open: f64, high: f64, low: f64, close: f64, ts: i64) -> Candle {
|
||||||
|
Candle::new(open, high, low, close, 1.0, ts).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn accessors_and_metadata() {
|
||||||
|
let t = DojiStar::new();
|
||||||
|
assert_eq!(t.name(), "DojiStar");
|
||||||
|
assert_eq!(t.warmup_period(), 2);
|
||||||
|
assert!(!t.is_ready());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bullish_doji_star_is_plus_one() {
|
||||||
|
let mut t = DojiStar::new();
|
||||||
|
assert_eq!(t.update(c(20.0, 20.2, 14.8, 15.0, 0)), Some(0.0));
|
||||||
|
assert_eq!(t.update(c(13.0, 13.1, 12.9, 13.0, 1)), Some(1.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bearish_doji_star_is_minus_one() {
|
||||||
|
let mut t = DojiStar::new();
|
||||||
|
assert_eq!(t.update(c(15.0, 20.2, 14.8, 20.0, 0)), Some(0.0));
|
||||||
|
assert_eq!(t.update(c(22.0, 22.1, 21.9, 22.0, 1)), Some(-1.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn second_bar_not_doji_yields_zero() {
|
||||||
|
let mut t = DojiStar::new();
|
||||||
|
t.update(c(20.0, 20.2, 14.8, 15.0, 0));
|
||||||
|
// Wide body, not a doji.
|
||||||
|
assert_eq!(t.update(c(13.0, 13.2, 11.0, 11.5, 1)), Some(0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_gap_yields_zero() {
|
||||||
|
let mut t = DojiStar::new();
|
||||||
|
t.update(c(20.0, 20.2, 14.8, 15.0, 0));
|
||||||
|
// Doji overlaps bar1's body (no gap down).
|
||||||
|
assert_eq!(t.update(c(16.0, 16.1, 15.9, 16.0, 1)), Some(0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn short_first_body_yields_zero() {
|
||||||
|
let mut t = DojiStar::new();
|
||||||
|
// First bar body too short to be the "long" leg.
|
||||||
|
t.update(c(20.0, 24.0, 16.0, 19.5, 0));
|
||||||
|
assert_eq!(t.update(c(13.0, 13.1, 12.9, 13.0, 1)), Some(0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn first_bar_returns_zero() {
|
||||||
|
let mut t = DojiStar::new();
|
||||||
|
assert_eq!(t.update(c(20.0, 20.2, 14.8, 15.0, 0)), Some(0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn batch_equals_streaming() {
|
||||||
|
let candles: Vec<Candle> = (0..40)
|
||||||
|
.map(|i| {
|
||||||
|
let base = 100.0 + i as f64;
|
||||||
|
if i % 2 == 0 {
|
||||||
|
c(base + 5.0, base + 5.2, base - 0.2, base, i)
|
||||||
|
} else {
|
||||||
|
c(base - 3.0, base - 2.9, base - 3.1, base - 3.0, i)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let mut a = DojiStar::new();
|
||||||
|
let mut b = DojiStar::new();
|
||||||
|
assert_eq!(
|
||||||
|
a.batch(&candles),
|
||||||
|
candles.iter().map(|x| b.update(*x)).collect::<Vec<_>>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reset_clears_state() {
|
||||||
|
let mut t = DojiStar::new();
|
||||||
|
t.update(c(20.0, 20.2, 14.8, 15.0, 0));
|
||||||
|
t.update(c(13.0, 13.1, 12.9, 13.0, 1));
|
||||||
|
assert!(t.is_ready());
|
||||||
|
t.reset();
|
||||||
|
assert!(!t.is_ready());
|
||||||
|
assert_eq!(t.update(c(20.0, 20.2, 14.8, 15.0, 0)), Some(0.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
//! Dragonfly Doji candlestick pattern.
|
||||||
|
|
||||||
|
use crate::ohlcv::Candle;
|
||||||
|
use crate::traits::Indicator;
|
||||||
|
|
||||||
|
/// Dragonfly Doji — a single-bar bullish reversal. Open, close, and high sit at
|
||||||
|
/// the top of the bar while a long lower shadow shows price was driven down hard
|
||||||
|
/// and then bid all the way back to the open — buyers rejecting the lows.
|
||||||
|
///
|
||||||
|
/// ```text
|
||||||
|
/// range = high − low
|
||||||
|
/// doji = |close − open| <= 0.1 * range
|
||||||
|
/// no upper wick = high − max(open, close) <= 0.1 * range
|
||||||
|
/// long lower = min(open, close) − low >= 0.5 * range
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Output is `+1.0` when the dragonfly prints and `0.0` otherwise. Dragonfly Doji
|
||||||
|
/// is a single-direction (bullish-only) shape, so it never emits `−1.0`. Body and
|
||||||
|
/// shadow thresholds follow the geometric house style (fixed fractions of the bar
|
||||||
|
/// range) rather than TA-Lib's rolling averages. Pattern-shape check only — no
|
||||||
|
/// trend filter is applied; combine with a trend indicator for actionable
|
||||||
|
/// signals.
|
||||||
|
///
|
||||||
|
/// # Signed ±1 encoding
|
||||||
|
///
|
||||||
|
/// This detector emits the uniform candlestick sign convention shared across the
|
||||||
|
/// pattern family — `+1.0` bullish, `0.0` no pattern — so it drops straight into
|
||||||
|
/// a machine-learning feature matrix as a single dimension.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use wickra_core::{Candle, DragonflyDoji, Indicator};
|
||||||
|
///
|
||||||
|
/// let mut indicator = DragonflyDoji::new();
|
||||||
|
/// // Body at the top, long lower shadow.
|
||||||
|
/// let candle = Candle::new(10.0, 10.05, 6.0, 10.0, 1.0, 0).unwrap();
|
||||||
|
/// assert_eq!(indicator.update(candle), Some(1.0));
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct DragonflyDoji {
|
||||||
|
has_emitted: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DragonflyDoji {
|
||||||
|
/// Construct a new Dragonfly Doji detector.
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self { has_emitted: false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Indicator for DragonflyDoji {
|
||||||
|
type Input = Candle;
|
||||||
|
type Output = f64;
|
||||||
|
|
||||||
|
fn update(&mut self, candle: Candle) -> Option<f64> {
|
||||||
|
self.has_emitted = true;
|
||||||
|
let range = candle.high - candle.low;
|
||||||
|
if range <= 0.0 {
|
||||||
|
return Some(0.0);
|
||||||
|
}
|
||||||
|
if (candle.close - candle.open).abs() > 0.1 * range {
|
||||||
|
return Some(0.0);
|
||||||
|
}
|
||||||
|
let upper = candle.high - candle.open.max(candle.close);
|
||||||
|
let lower = candle.open.min(candle.close) - candle.low;
|
||||||
|
if upper <= 0.1 * range && lower >= 0.5 * range {
|
||||||
|
return Some(1.0);
|
||||||
|
}
|
||||||
|
Some(0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) {
|
||||||
|
self.has_emitted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn warmup_period(&self) -> usize {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_ready(&self) -> bool {
|
||||||
|
self.has_emitted
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"DragonflyDoji"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::traits::BatchExt;
|
||||||
|
|
||||||
|
fn c(open: f64, high: f64, low: f64, close: f64, ts: i64) -> Candle {
|
||||||
|
Candle::new(open, high, low, close, 1.0, ts).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn accessors_and_metadata() {
|
||||||
|
let t = DragonflyDoji::new();
|
||||||
|
assert_eq!(t.name(), "DragonflyDoji");
|
||||||
|
assert_eq!(t.warmup_period(), 1);
|
||||||
|
assert!(!t.is_ready());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dragonfly_is_plus_one() {
|
||||||
|
let mut t = DragonflyDoji::new();
|
||||||
|
assert_eq!(t.update(c(10.0, 10.05, 6.0, 10.0, 0)), Some(1.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn upper_shadow_yields_zero() {
|
||||||
|
let mut t = DragonflyDoji::new();
|
||||||
|
// Long upper shadow -> not a dragonfly (this is a gravestone shape).
|
||||||
|
assert_eq!(t.update(c(10.0, 14.0, 9.95, 10.0, 0)), Some(0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn short_lower_shadow_yields_zero() {
|
||||||
|
let mut t = DragonflyDoji::new();
|
||||||
|
// Body at the top but the lower shadow is too short.
|
||||||
|
assert_eq!(t.update(c(10.0, 10.05, 9.6, 10.0, 0)), Some(0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn non_doji_yields_zero() {
|
||||||
|
let mut t = DragonflyDoji::new();
|
||||||
|
assert_eq!(t.update(c(10.0, 12.0, 6.0, 11.5, 0)), Some(0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn zero_range_yields_zero() {
|
||||||
|
let mut t = DragonflyDoji::new();
|
||||||
|
assert_eq!(t.update(c(10.0, 10.0, 10.0, 10.0, 0)), Some(0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn batch_equals_streaming() {
|
||||||
|
let candles: Vec<Candle> = (0..40)
|
||||||
|
.map(|i| {
|
||||||
|
let base = 100.0 + i as f64;
|
||||||
|
c(base, base + 0.05, base - 4.0, base, i)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let mut a = DragonflyDoji::new();
|
||||||
|
let mut b = DragonflyDoji::new();
|
||||||
|
assert_eq!(
|
||||||
|
a.batch(&candles),
|
||||||
|
candles.iter().map(|x| b.update(*x)).collect::<Vec<_>>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reset_clears_state() {
|
||||||
|
let mut t = DragonflyDoji::new();
|
||||||
|
t.update(c(10.0, 10.05, 6.0, 10.0, 0));
|
||||||
|
assert!(t.is_ready());
|
||||||
|
t.reset();
|
||||||
|
assert!(!t.is_ready());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
//! Gravestone Doji candlestick pattern.
|
||||||
|
|
||||||
|
use crate::ohlcv::Candle;
|
||||||
|
use crate::traits::Indicator;
|
||||||
|
|
||||||
|
/// Gravestone Doji — a single-bar bearish reversal. Open, close, and low sit at
|
||||||
|
/// the bottom of the bar while a long upper shadow shows price was pushed up hard
|
||||||
|
/// and then sold all the way back to the open — sellers rejecting the highs.
|
||||||
|
///
|
||||||
|
/// ```text
|
||||||
|
/// range = high − low
|
||||||
|
/// doji = |close − open| <= 0.1 * range
|
||||||
|
/// no lower wick = min(open, close) − low <= 0.1 * range
|
||||||
|
/// long upper = high − max(open, close) >= 0.5 * range
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Output is `−1.0` when the gravestone prints and `0.0` otherwise. Gravestone
|
||||||
|
/// Doji is a single-direction (bearish-only) shape, so it never emits `+1.0`.
|
||||||
|
/// Body and shadow thresholds follow the geometric house style (fixed fractions
|
||||||
|
/// of the bar range) rather than TA-Lib's rolling averages. Pattern-shape check
|
||||||
|
/// only — no trend filter is applied; combine with a trend indicator for
|
||||||
|
/// actionable signals.
|
||||||
|
///
|
||||||
|
/// # Signed ±1 encoding
|
||||||
|
///
|
||||||
|
/// This detector emits the uniform candlestick sign convention shared across the
|
||||||
|
/// pattern family — `−1.0` bearish, `0.0` no pattern — so it drops straight into
|
||||||
|
/// a machine-learning feature matrix as a single dimension.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use wickra_core::{Candle, GravestoneDoji, Indicator};
|
||||||
|
///
|
||||||
|
/// let mut indicator = GravestoneDoji::new();
|
||||||
|
/// // Body at the bottom, long upper shadow.
|
||||||
|
/// let candle = Candle::new(10.0, 14.0, 9.95, 10.0, 1.0, 0).unwrap();
|
||||||
|
/// assert_eq!(indicator.update(candle), Some(-1.0));
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct GravestoneDoji {
|
||||||
|
has_emitted: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GravestoneDoji {
|
||||||
|
/// Construct a new Gravestone Doji detector.
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self { has_emitted: false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Indicator for GravestoneDoji {
|
||||||
|
type Input = Candle;
|
||||||
|
type Output = f64;
|
||||||
|
|
||||||
|
fn update(&mut self, candle: Candle) -> Option<f64> {
|
||||||
|
self.has_emitted = true;
|
||||||
|
let range = candle.high - candle.low;
|
||||||
|
if range <= 0.0 {
|
||||||
|
return Some(0.0);
|
||||||
|
}
|
||||||
|
if (candle.close - candle.open).abs() > 0.1 * range {
|
||||||
|
return Some(0.0);
|
||||||
|
}
|
||||||
|
let upper = candle.high - candle.open.max(candle.close);
|
||||||
|
let lower = candle.open.min(candle.close) - candle.low;
|
||||||
|
if lower <= 0.1 * range && upper >= 0.5 * range {
|
||||||
|
return Some(-1.0);
|
||||||
|
}
|
||||||
|
Some(0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) {
|
||||||
|
self.has_emitted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn warmup_period(&self) -> usize {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_ready(&self) -> bool {
|
||||||
|
self.has_emitted
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"GravestoneDoji"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::traits::BatchExt;
|
||||||
|
|
||||||
|
fn c(open: f64, high: f64, low: f64, close: f64, ts: i64) -> Candle {
|
||||||
|
Candle::new(open, high, low, close, 1.0, ts).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn accessors_and_metadata() {
|
||||||
|
let t = GravestoneDoji::new();
|
||||||
|
assert_eq!(t.name(), "GravestoneDoji");
|
||||||
|
assert_eq!(t.warmup_period(), 1);
|
||||||
|
assert!(!t.is_ready());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn gravestone_is_minus_one() {
|
||||||
|
let mut t = GravestoneDoji::new();
|
||||||
|
assert_eq!(t.update(c(10.0, 14.0, 9.95, 10.0, 0)), Some(-1.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lower_shadow_yields_zero() {
|
||||||
|
let mut t = GravestoneDoji::new();
|
||||||
|
// Long lower shadow -> not a gravestone (this is a dragonfly shape).
|
||||||
|
assert_eq!(t.update(c(10.0, 10.05, 6.0, 10.0, 0)), Some(0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn short_upper_shadow_yields_zero() {
|
||||||
|
let mut t = GravestoneDoji::new();
|
||||||
|
// Body at the bottom but the upper shadow is too short.
|
||||||
|
assert_eq!(t.update(c(10.0, 10.4, 9.95, 10.0, 0)), Some(0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn non_doji_yields_zero() {
|
||||||
|
let mut t = GravestoneDoji::new();
|
||||||
|
assert_eq!(t.update(c(10.0, 14.0, 9.5, 13.5, 0)), Some(0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn zero_range_yields_zero() {
|
||||||
|
let mut t = GravestoneDoji::new();
|
||||||
|
assert_eq!(t.update(c(10.0, 10.0, 10.0, 10.0, 0)), Some(0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn batch_equals_streaming() {
|
||||||
|
let candles: Vec<Candle> = (0..40)
|
||||||
|
.map(|i| {
|
||||||
|
let base = 100.0 + i as f64;
|
||||||
|
c(base, base + 4.0, base - 0.05, base, i)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let mut a = GravestoneDoji::new();
|
||||||
|
let mut b = GravestoneDoji::new();
|
||||||
|
assert_eq!(
|
||||||
|
a.batch(&candles),
|
||||||
|
candles.iter().map(|x| b.update(*x)).collect::<Vec<_>>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reset_clears_state() {
|
||||||
|
let mut t = GravestoneDoji::new();
|
||||||
|
t.update(c(10.0, 14.0, 9.95, 10.0, 0));
|
||||||
|
assert!(t.is_ready());
|
||||||
|
t.reset();
|
||||||
|
assert!(!t.is_ready());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
//! Long-Legged Doji candlestick pattern.
|
||||||
|
|
||||||
|
use crate::ohlcv::Candle;
|
||||||
|
use crate::traits::Indicator;
|
||||||
|
|
||||||
|
/// Long-Legged Doji — a single-bar indecision signal. A doji with long shadows on
|
||||||
|
/// *both* sides: price ranged widely up and down yet closed essentially where it
|
||||||
|
/// opened, a tug-of-war that often precedes a turn.
|
||||||
|
///
|
||||||
|
/// ```text
|
||||||
|
/// range = high − low
|
||||||
|
/// doji = |close − open| <= 0.1 * range
|
||||||
|
/// long upper = high − max(open, close) >= 0.3 * range
|
||||||
|
/// long lower = min(open, close) − low >= 0.3 * range
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Output is `+1.0` when the long-legged doji prints and `0.0` otherwise. This is
|
||||||
|
/// a non-directional indecision flag — it never emits `−1.0` (use
|
||||||
|
/// `DragonflyDoji` / `GravestoneDoji` for the directional single-shadow variants).
|
||||||
|
/// Body and shadow thresholds follow the geometric house style (fixed fractions
|
||||||
|
/// of the bar range) rather than TA-Lib's rolling averages. Pattern-shape check
|
||||||
|
/// only — no trend filter is applied; combine with a trend indicator for
|
||||||
|
/// actionable signals.
|
||||||
|
///
|
||||||
|
/// # Signed ±1 encoding
|
||||||
|
///
|
||||||
|
/// This detector emits the uniform candlestick sign convention shared across the
|
||||||
|
/// pattern family — `+1.0` detected, `0.0` no pattern — so it drops straight into
|
||||||
|
/// a machine-learning feature matrix as a single dimension.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use wickra_core::{Candle, LongLeggedDoji, Indicator};
|
||||||
|
///
|
||||||
|
/// let mut indicator = LongLeggedDoji::new();
|
||||||
|
/// // Tiny body, long shadows on both sides.
|
||||||
|
/// let candle = Candle::new(10.0, 12.0, 8.0, 10.05, 1.0, 0).unwrap();
|
||||||
|
/// assert_eq!(indicator.update(candle), Some(1.0));
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct LongLeggedDoji {
|
||||||
|
has_emitted: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LongLeggedDoji {
|
||||||
|
/// Construct a new Long-Legged Doji detector.
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self { has_emitted: false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Indicator for LongLeggedDoji {
|
||||||
|
type Input = Candle;
|
||||||
|
type Output = f64;
|
||||||
|
|
||||||
|
fn update(&mut self, candle: Candle) -> Option<f64> {
|
||||||
|
self.has_emitted = true;
|
||||||
|
let range = candle.high - candle.low;
|
||||||
|
if range <= 0.0 {
|
||||||
|
return Some(0.0);
|
||||||
|
}
|
||||||
|
if (candle.close - candle.open).abs() > 0.1 * range {
|
||||||
|
return Some(0.0);
|
||||||
|
}
|
||||||
|
let upper = candle.high - candle.open.max(candle.close);
|
||||||
|
let lower = candle.open.min(candle.close) - candle.low;
|
||||||
|
if upper >= 0.3 * range && lower >= 0.3 * range {
|
||||||
|
return Some(1.0);
|
||||||
|
}
|
||||||
|
Some(0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) {
|
||||||
|
self.has_emitted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn warmup_period(&self) -> usize {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_ready(&self) -> bool {
|
||||||
|
self.has_emitted
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"LongLeggedDoji"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::traits::BatchExt;
|
||||||
|
|
||||||
|
fn c(open: f64, high: f64, low: f64, close: f64, ts: i64) -> Candle {
|
||||||
|
Candle::new(open, high, low, close, 1.0, ts).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn accessors_and_metadata() {
|
||||||
|
let t = LongLeggedDoji::new();
|
||||||
|
assert_eq!(t.name(), "LongLeggedDoji");
|
||||||
|
assert_eq!(t.warmup_period(), 1);
|
||||||
|
assert!(!t.is_ready());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn long_legged_is_plus_one() {
|
||||||
|
let mut t = LongLeggedDoji::new();
|
||||||
|
assert_eq!(t.update(c(10.0, 12.0, 8.0, 10.05, 0)), Some(1.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn one_sided_shadow_yields_zero() {
|
||||||
|
let mut t = LongLeggedDoji::new();
|
||||||
|
// Dragonfly shape: long lower shadow but no upper -> not long-legged.
|
||||||
|
assert_eq!(t.update(c(10.0, 10.05, 6.0, 10.0, 0)), Some(0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn non_doji_yields_zero() {
|
||||||
|
let mut t = LongLeggedDoji::new();
|
||||||
|
assert_eq!(t.update(c(10.0, 12.0, 8.0, 11.5, 0)), Some(0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn zero_range_yields_zero() {
|
||||||
|
let mut t = LongLeggedDoji::new();
|
||||||
|
assert_eq!(t.update(c(10.0, 10.0, 10.0, 10.0, 0)), Some(0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn batch_equals_streaming() {
|
||||||
|
let candles: Vec<Candle> = (0..40)
|
||||||
|
.map(|i| {
|
||||||
|
let base = 100.0 + i as f64;
|
||||||
|
c(base, base + 3.0, base - 3.0, base + 0.05, i)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let mut a = LongLeggedDoji::new();
|
||||||
|
let mut b = LongLeggedDoji::new();
|
||||||
|
assert_eq!(
|
||||||
|
a.batch(&candles),
|
||||||
|
candles.iter().map(|x| b.update(*x)).collect::<Vec<_>>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reset_clears_state() {
|
||||||
|
let mut t = LongLeggedDoji::new();
|
||||||
|
t.update(c(10.0, 12.0, 8.0, 10.05, 0));
|
||||||
|
assert!(t.is_ready());
|
||||||
|
t.reset();
|
||||||
|
assert!(!t.is_ready());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -63,10 +63,12 @@ mod demark_pivots;
|
|||||||
mod depth_slope;
|
mod depth_slope;
|
||||||
mod detrended_std_dev;
|
mod detrended_std_dev;
|
||||||
mod doji;
|
mod doji;
|
||||||
|
mod doji_star;
|
||||||
mod donchian;
|
mod donchian;
|
||||||
mod donchian_stop;
|
mod donchian_stop;
|
||||||
mod double_bollinger;
|
mod double_bollinger;
|
||||||
mod dpo;
|
mod dpo;
|
||||||
|
mod dragonfly_doji;
|
||||||
mod drawdown_duration;
|
mod drawdown_duration;
|
||||||
mod ease_of_movement;
|
mod ease_of_movement;
|
||||||
mod effective_spread;
|
mod effective_spread;
|
||||||
@@ -89,6 +91,7 @@ mod funding_rate_mean;
|
|||||||
mod funding_rate_zscore;
|
mod funding_rate_zscore;
|
||||||
mod gain_loss_ratio;
|
mod gain_loss_ratio;
|
||||||
mod garman_klass;
|
mod garman_klass;
|
||||||
|
mod gravestone_doji;
|
||||||
mod hammer;
|
mod hammer;
|
||||||
mod hanging_man;
|
mod hanging_man;
|
||||||
mod harami;
|
mod harami;
|
||||||
@@ -122,6 +125,7 @@ mod linreg_angle;
|
|||||||
mod linreg_channel;
|
mod linreg_channel;
|
||||||
mod linreg_slope;
|
mod linreg_slope;
|
||||||
mod liquidation_features;
|
mod liquidation_features;
|
||||||
|
mod long_legged_doji;
|
||||||
mod long_short_ratio;
|
mod long_short_ratio;
|
||||||
mod ma_envelope;
|
mod ma_envelope;
|
||||||
mod macd;
|
mod macd;
|
||||||
@@ -168,6 +172,7 @@ mod realized_spread;
|
|||||||
mod recovery_factor;
|
mod recovery_factor;
|
||||||
mod relative_strength_ab;
|
mod relative_strength_ab;
|
||||||
mod renko_trailing_stop;
|
mod renko_trailing_stop;
|
||||||
|
mod rickshaw_man;
|
||||||
mod roc;
|
mod roc;
|
||||||
mod rogers_satchell;
|
mod rogers_satchell;
|
||||||
mod roofing_filter;
|
mod roofing_filter;
|
||||||
@@ -317,10 +322,12 @@ pub use demark_pivots::{DemarkPivots, DemarkPivotsOutput};
|
|||||||
pub use depth_slope::DepthSlope;
|
pub use depth_slope::DepthSlope;
|
||||||
pub use detrended_std_dev::DetrendedStdDev;
|
pub use detrended_std_dev::DetrendedStdDev;
|
||||||
pub use doji::Doji;
|
pub use doji::Doji;
|
||||||
|
pub use doji_star::DojiStar;
|
||||||
pub use donchian::{Donchian, DonchianOutput};
|
pub use donchian::{Donchian, DonchianOutput};
|
||||||
pub use donchian_stop::{DonchianStop, DonchianStopOutput};
|
pub use donchian_stop::{DonchianStop, DonchianStopOutput};
|
||||||
pub use double_bollinger::{DoubleBollinger, DoubleBollingerOutput};
|
pub use double_bollinger::{DoubleBollinger, DoubleBollingerOutput};
|
||||||
pub use dpo::Dpo;
|
pub use dpo::Dpo;
|
||||||
|
pub use dragonfly_doji::DragonflyDoji;
|
||||||
pub use drawdown_duration::DrawdownDuration;
|
pub use drawdown_duration::DrawdownDuration;
|
||||||
pub use ease_of_movement::EaseOfMovement;
|
pub use ease_of_movement::EaseOfMovement;
|
||||||
pub use effective_spread::EffectiveSpread;
|
pub use effective_spread::EffectiveSpread;
|
||||||
@@ -343,6 +350,7 @@ pub use funding_rate_mean::FundingRateMean;
|
|||||||
pub use funding_rate_zscore::FundingRateZScore;
|
pub use funding_rate_zscore::FundingRateZScore;
|
||||||
pub use gain_loss_ratio::GainLossRatio;
|
pub use gain_loss_ratio::GainLossRatio;
|
||||||
pub use garman_klass::GarmanKlassVolatility;
|
pub use garman_klass::GarmanKlassVolatility;
|
||||||
|
pub use gravestone_doji::GravestoneDoji;
|
||||||
pub use hammer::Hammer;
|
pub use hammer::Hammer;
|
||||||
pub use hanging_man::HangingMan;
|
pub use hanging_man::HangingMan;
|
||||||
pub use harami::Harami;
|
pub use harami::Harami;
|
||||||
@@ -376,6 +384,7 @@ pub use linreg_angle::LinRegAngle;
|
|||||||
pub use linreg_channel::{LinRegChannel, LinRegChannelOutput};
|
pub use linreg_channel::{LinRegChannel, LinRegChannelOutput};
|
||||||
pub use linreg_slope::LinRegSlope;
|
pub use linreg_slope::LinRegSlope;
|
||||||
pub use liquidation_features::{LiquidationFeatures, LiquidationFeaturesOutput};
|
pub use liquidation_features::{LiquidationFeatures, LiquidationFeaturesOutput};
|
||||||
|
pub use long_legged_doji::LongLeggedDoji;
|
||||||
pub use long_short_ratio::LongShortRatio;
|
pub use long_short_ratio::LongShortRatio;
|
||||||
pub use ma_envelope::{MaEnvelope, MaEnvelopeOutput};
|
pub use ma_envelope::{MaEnvelope, MaEnvelopeOutput};
|
||||||
pub use macd::{MacdIndicator, MacdOutput};
|
pub use macd::{MacdIndicator, MacdOutput};
|
||||||
@@ -422,6 +431,7 @@ pub use realized_spread::RealizedSpread;
|
|||||||
pub use recovery_factor::RecoveryFactor;
|
pub use recovery_factor::RecoveryFactor;
|
||||||
pub use relative_strength_ab::{RelativeStrengthAB, RelativeStrengthOutput};
|
pub use relative_strength_ab::{RelativeStrengthAB, RelativeStrengthOutput};
|
||||||
pub use renko_trailing_stop::RenkoTrailingStop;
|
pub use renko_trailing_stop::RenkoTrailingStop;
|
||||||
|
pub use rickshaw_man::RickshawMan;
|
||||||
pub use roc::Roc;
|
pub use roc::Roc;
|
||||||
pub use rogers_satchell::RogersSatchellVolatility;
|
pub use rogers_satchell::RogersSatchellVolatility;
|
||||||
pub use roofing_filter::RoofingFilter;
|
pub use roofing_filter::RoofingFilter;
|
||||||
@@ -785,6 +795,11 @@ pub const FAMILIES: &[(&str, &[&str])] = &[
|
|||||||
"BeltHold",
|
"BeltHold",
|
||||||
"Breakaway",
|
"Breakaway",
|
||||||
"Counterattack",
|
"Counterattack",
|
||||||
|
"DojiStar",
|
||||||
|
"DragonflyDoji",
|
||||||
|
"GravestoneDoji",
|
||||||
|
"LongLeggedDoji",
|
||||||
|
"RickshawMan",
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -876,6 +891,6 @@ mod family_tests {
|
|||||||
// the actual indicator count is the early-warning signal that an
|
// the actual indicator count is the early-warning signal that an
|
||||||
// indicator was added without being assigned a family.
|
// indicator was added without being assigned a family.
|
||||||
let total: usize = FAMILIES.iter().map(|(_, ns)| ns.len()).sum();
|
let total: usize = FAMILIES.iter().map(|(_, ns)| ns.len()).sum();
|
||||||
assert_eq!(total, 249, "FAMILIES total drifted from indicator count");
|
assert_eq!(total, 254, "FAMILIES total drifted from indicator count");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,167 @@
|
|||||||
|
//! Rickshaw Man candlestick pattern.
|
||||||
|
|
||||||
|
use crate::ohlcv::Candle;
|
||||||
|
use crate::traits::Indicator;
|
||||||
|
|
||||||
|
/// Rickshaw Man — a single-bar indecision signal. A long-legged doji whose tiny
|
||||||
|
/// body sits near the *middle* of a wide range, the most balanced form of
|
||||||
|
/// indecision: neither side controlled the close and the midpoint pins it.
|
||||||
|
///
|
||||||
|
/// ```text
|
||||||
|
/// range = high − low
|
||||||
|
/// doji = |close − open| <= 0.1 * range
|
||||||
|
/// long upper = high − max(open, close) >= 0.3 * range
|
||||||
|
/// long lower = min(open, close) − low >= 0.3 * range
|
||||||
|
/// centred body = body midpoint within the central 40–60 % of the range
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Output is `+1.0` when the rickshaw man prints and `0.0` otherwise. This is a
|
||||||
|
/// non-directional indecision flag — it never emits `−1.0`. A rickshaw man is a
|
||||||
|
/// special case of a long-legged doji (the body additionally sits at the centre),
|
||||||
|
/// so both detectors may flag the same bar. Body and shadow thresholds follow the
|
||||||
|
/// geometric house style (fixed fractions of the bar range) rather than TA-Lib's
|
||||||
|
/// rolling averages. Pattern-shape check only — no trend filter is applied;
|
||||||
|
/// combine with a trend indicator for actionable signals.
|
||||||
|
///
|
||||||
|
/// # Signed ±1 encoding
|
||||||
|
///
|
||||||
|
/// This detector emits the uniform candlestick sign convention shared across the
|
||||||
|
/// pattern family — `+1.0` detected, `0.0` no pattern — so it drops straight into
|
||||||
|
/// a machine-learning feature matrix as a single dimension.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use wickra_core::{Candle, Indicator, RickshawMan};
|
||||||
|
///
|
||||||
|
/// let mut indicator = RickshawMan::new();
|
||||||
|
/// // Tiny body centred in a wide range, long shadows both sides.
|
||||||
|
/// let candle = Candle::new(10.0, 12.0, 8.0, 10.0, 1.0, 0).unwrap();
|
||||||
|
/// assert_eq!(indicator.update(candle), Some(1.0));
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct RickshawMan {
|
||||||
|
has_emitted: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RickshawMan {
|
||||||
|
/// Construct a new Rickshaw Man detector.
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self { has_emitted: false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Indicator for RickshawMan {
|
||||||
|
type Input = Candle;
|
||||||
|
type Output = f64;
|
||||||
|
|
||||||
|
fn update(&mut self, candle: Candle) -> Option<f64> {
|
||||||
|
self.has_emitted = true;
|
||||||
|
let range = candle.high - candle.low;
|
||||||
|
if range <= 0.0 {
|
||||||
|
return Some(0.0);
|
||||||
|
}
|
||||||
|
if (candle.close - candle.open).abs() > 0.1 * range {
|
||||||
|
return Some(0.0);
|
||||||
|
}
|
||||||
|
let upper = candle.high - candle.open.max(candle.close);
|
||||||
|
let lower = candle.open.min(candle.close) - candle.low;
|
||||||
|
let body_mid = 0.5 * (candle.open + candle.close);
|
||||||
|
let pos = (body_mid - candle.low) / range;
|
||||||
|
if upper >= 0.3 * range && lower >= 0.3 * range && (0.4..=0.6).contains(&pos) {
|
||||||
|
return Some(1.0);
|
||||||
|
}
|
||||||
|
Some(0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) {
|
||||||
|
self.has_emitted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn warmup_period(&self) -> usize {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_ready(&self) -> bool {
|
||||||
|
self.has_emitted
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"RickshawMan"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::traits::BatchExt;
|
||||||
|
|
||||||
|
fn c(open: f64, high: f64, low: f64, close: f64, ts: i64) -> Candle {
|
||||||
|
Candle::new(open, high, low, close, 1.0, ts).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn accessors_and_metadata() {
|
||||||
|
let t = RickshawMan::new();
|
||||||
|
assert_eq!(t.name(), "RickshawMan");
|
||||||
|
assert_eq!(t.warmup_period(), 1);
|
||||||
|
assert!(!t.is_ready());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rickshaw_is_plus_one() {
|
||||||
|
let mut t = RickshawMan::new();
|
||||||
|
assert_eq!(t.update(c(10.0, 12.0, 8.0, 10.0, 0)), Some(1.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn off_centre_body_yields_zero() {
|
||||||
|
let mut t = RickshawMan::new();
|
||||||
|
// Long-legged but the body sits near the top, not the middle.
|
||||||
|
assert_eq!(t.update(c(11.4, 12.0, 8.0, 11.45, 0)), Some(0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn one_sided_shadow_yields_zero() {
|
||||||
|
let mut t = RickshawMan::new();
|
||||||
|
// Dragonfly shape: no upper shadow -> not a rickshaw man.
|
||||||
|
assert_eq!(t.update(c(10.0, 10.05, 6.0, 10.0, 0)), Some(0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn non_doji_yields_zero() {
|
||||||
|
let mut t = RickshawMan::new();
|
||||||
|
assert_eq!(t.update(c(9.0, 12.0, 8.0, 11.0, 0)), Some(0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn zero_range_yields_zero() {
|
||||||
|
let mut t = RickshawMan::new();
|
||||||
|
assert_eq!(t.update(c(10.0, 10.0, 10.0, 10.0, 0)), Some(0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn batch_equals_streaming() {
|
||||||
|
let candles: Vec<Candle> = (0..40)
|
||||||
|
.map(|i| {
|
||||||
|
let base = 100.0 + i as f64;
|
||||||
|
c(base, base + 2.0, base - 2.0, base + 0.05, i)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let mut a = RickshawMan::new();
|
||||||
|
let mut b = RickshawMan::new();
|
||||||
|
assert_eq!(
|
||||||
|
a.batch(&candles),
|
||||||
|
candles.iter().map(|x| b.update(*x)).collect::<Vec<_>>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reset_clears_state() {
|
||||||
|
let mut t = RickshawMan::new();
|
||||||
|
t.update(c(10.0, 12.0, 8.0, 10.0, 0));
|
||||||
|
assert!(t.is_ready());
|
||||||
|
t.reset();
|
||||||
|
assert!(!t.is_ready());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -64,44 +64,45 @@ pub use indicators::{
|
|||||||
ClassicPivotsOutput, Cmo, CoefficientOfVariation, Cointegration, CointegrationOutput,
|
ClassicPivotsOutput, Cmo, CoefficientOfVariation, Cointegration, CointegrationOutput,
|
||||||
ConditionalValueAtRisk, ConnorsRsi, Coppock, Counterattack, CumulativeVolumeDelta,
|
ConditionalValueAtRisk, ConnorsRsi, Coppock, Counterattack, CumulativeVolumeDelta,
|
||||||
CyberneticCycle, Decycler, DecyclerOscillator, Dema, DemandIndex, DemarkPivots,
|
CyberneticCycle, Decycler, DecyclerOscillator, Dema, DemandIndex, DemarkPivots,
|
||||||
DemarkPivotsOutput, DepthSlope, DetrendedStdDev, Doji, Donchian, DonchianOutput, DonchianStop,
|
DemarkPivotsOutput, DepthSlope, DetrendedStdDev, Doji, DojiStar, Donchian, DonchianOutput,
|
||||||
DonchianStopOutput, DoubleBollinger, DoubleBollingerOutput, Dpo, DrawdownDuration,
|
DonchianStop, DonchianStopOutput, DoubleBollinger, DoubleBollingerOutput, Dpo, DragonflyDoji,
|
||||||
EaseOfMovement, EffectiveSpread, EhlersStochastic, ElderImpulse, Ema,
|
DrawdownDuration, EaseOfMovement, EffectiveSpread, EhlersStochastic, ElderImpulse, Ema,
|
||||||
EmpiricalModeDecomposition, Engulfing, Evwma, Fama, FibonacciPivots, FibonacciPivotsOutput,
|
EmpiricalModeDecomposition, Engulfing, Evwma, Fama, FibonacciPivots, FibonacciPivotsOutput,
|
||||||
FisherTransform, Footprint, FootprintOutput, ForceIndex, FractalChaosBands,
|
FisherTransform, Footprint, FootprintOutput, ForceIndex, FractalChaosBands,
|
||||||
FractalChaosBandsOutput, Frama, FundingBasis, FundingRate, FundingRateMean, FundingRateZScore,
|
FractalChaosBandsOutput, Frama, FundingBasis, FundingRate, FundingRateMean, FundingRateZScore,
|
||||||
GainLossRatio, GarmanKlassVolatility, Hammer, HangingMan, Harami, HeikinAshi, HeikinAshiOutput,
|
GainLossRatio, GarmanKlassVolatility, GravestoneDoji, Hammer, HangingMan, Harami, HeikinAshi,
|
||||||
HiLoActivator, HilbertDominantCycle, HistoricalVolatility, Hma, HurstChannel,
|
HeikinAshiOutput, HiLoActivator, HilbertDominantCycle, HistoricalVolatility, Hma, HurstChannel,
|
||||||
HurstChannelOutput, HurstExponent, Ichimoku, IchimokuOutput, IdenticalThreeCrows, Inertia,
|
HurstChannelOutput, HurstExponent, Ichimoku, IchimokuOutput, IdenticalThreeCrows, Inertia,
|
||||||
InformationRatio, InitialBalance, InitialBalanceOutput, InstantaneousTrendline,
|
InformationRatio, InitialBalance, InitialBalanceOutput, InstantaneousTrendline,
|
||||||
InverseFisherTransform, InvertedHammer, Jma, Kama, KellyCriterion, Keltner, KeltnerOutput, Kst,
|
InverseFisherTransform, InvertedHammer, Jma, Kama, KellyCriterion, Keltner, KeltnerOutput, Kst,
|
||||||
KstOutput, Kurtosis, Kvo, KylesLambda, LaguerreRsi, LeadLagCrossCorrelation,
|
KstOutput, Kurtosis, Kvo, KylesLambda, LaguerreRsi, LeadLagCrossCorrelation,
|
||||||
LeadLagCrossCorrelationOutput, LinRegAngle, LinRegChannel, LinRegChannelOutput, LinRegSlope,
|
LeadLagCrossCorrelationOutput, LinRegAngle, LinRegChannel, LinRegChannelOutput, LinRegSlope,
|
||||||
LinearRegression, LiquidationFeatures, LiquidationFeaturesOutput, LongShortRatio, MaEnvelope,
|
LinearRegression, LiquidationFeatures, LiquidationFeaturesOutput, LongLeggedDoji,
|
||||||
MaEnvelopeOutput, MacdIndicator, MacdOutput, Mama, MamaOutput, MarketFacilitationIndex,
|
LongShortRatio, MaEnvelope, MaEnvelopeOutput, MacdIndicator, MacdOutput, Mama, MamaOutput,
|
||||||
Marubozu, MassIndex, MaxDrawdown, McGinleyDynamic, MedianAbsoluteDeviation, MedianPrice, Mfi,
|
MarketFacilitationIndex, Marubozu, MassIndex, MaxDrawdown, McGinleyDynamic,
|
||||||
Microprice, Mom, MorningEveningStar, Natr, Nvi, OIPriceDivergence, OIWeighted, Obv, OmegaRatio,
|
MedianAbsoluteDeviation, MedianPrice, Mfi, Microprice, Mom, MorningEveningStar, Natr, Nvi,
|
||||||
OpenInterestDelta, OpeningRange, OpeningRangeOutput, OrderBookImbalanceFull,
|
OIPriceDivergence, OIWeighted, Obv, OmegaRatio, OpenInterestDelta, OpeningRange,
|
||||||
OrderBookImbalanceTop1, OrderBookImbalanceTopN, PainIndex, PairSpreadZScore, PairwiseBeta,
|
OpeningRangeOutput, OrderBookImbalanceFull, OrderBookImbalanceTop1, OrderBookImbalanceTopN,
|
||||||
ParkinsonVolatility, PearsonCorrelation, PercentB, PercentageTrailingStop, Pgo,
|
PainIndex, PairSpreadZScore, PairwiseBeta, ParkinsonVolatility, PearsonCorrelation, PercentB,
|
||||||
PiercingDarkCloud, Pmo, Ppo, ProfitFactor, Psar, Pvi, QuotedSpread, RSquared, RealizedSpread,
|
PercentageTrailingStop, Pgo, PiercingDarkCloud, Pmo, Ppo, ProfitFactor, Psar, Pvi,
|
||||||
RecoveryFactor, RelativeStrengthAB, RelativeStrengthOutput, RenkoTrailingStop, Roc,
|
QuotedSpread, RSquared, RealizedSpread, RecoveryFactor, RelativeStrengthAB,
|
||||||
RogersSatchellVolatility, RollingVwap, RoofingFilter, Rsi, Rvi, RviVolatility, Rwi, RwiOutput,
|
RelativeStrengthOutput, RenkoTrailingStop, RickshawMan, Roc, RogersSatchellVolatility,
|
||||||
SharpeRatio, ShootingStar, SignedVolume, SineWave, Skewness, Sma, Smi, Smma, SortinoRatio,
|
RollingVwap, RoofingFilter, Rsi, Rvi, RviVolatility, Rwi, RwiOutput, SharpeRatio, ShootingStar,
|
||||||
SpearmanCorrelation, SpinningTop, StandardError, StandardErrorBands, StandardErrorBandsOutput,
|
SignedVolume, SineWave, Skewness, Sma, Smi, Smma, SortinoRatio, SpearmanCorrelation,
|
||||||
StarcBands, StarcBandsOutput, Stc, StdDev, StepTrailingStop, StochRsi, Stochastic,
|
SpinningTop, StandardError, StandardErrorBands, StandardErrorBandsOutput, StarcBands,
|
||||||
StochasticOutput, SuperSmoother, SuperTrend, SuperTrendOutput, TakerBuySellRatio, TdCombo,
|
StarcBandsOutput, Stc, StdDev, StepTrailingStop, StochRsi, Stochastic, StochasticOutput,
|
||||||
TdCountdown, TdDeMarker, TdDifferential, TdLines, TdLinesOutput, TdOpen, TdPressure,
|
SuperSmoother, SuperTrend, SuperTrendOutput, TakerBuySellRatio, TdCombo, TdCountdown,
|
||||||
TdRangeProjection, TdRangeProjectionOutput, TdRei, TdRiskLevel, TdRiskLevelOutput,
|
TdDeMarker, TdDifferential, TdLines, TdLinesOutput, TdOpen, TdPressure, TdRangeProjection,
|
||||||
TdSequential, TdSequentialOutput, TdSetup, Tema, TermStructureBasis, ThreeInside,
|
TdRangeProjectionOutput, TdRei, TdRiskLevel, TdRiskLevelOutput, TdSequential,
|
||||||
ThreeLineStrike, ThreeOutside, ThreeSoldiersOrCrows, ThreeStarsInSouth, Tii, TradeImbalance,
|
TdSequentialOutput, TdSetup, Tema, TermStructureBasis, ThreeInside, ThreeLineStrike,
|
||||||
TreynorRatio, Trima, Trix, TrueRange, Tsi, Tsv, TtmSqueeze, TtmSqueezeOutput, Tweezer,
|
ThreeOutside, ThreeSoldiersOrCrows, ThreeStarsInSouth, Tii, TradeImbalance, TreynorRatio,
|
||||||
TwoCrows, TypicalPrice, UlcerIndex, UltimateOscillator, UpsideGapTwoCrows, ValueArea,
|
Trima, Trix, TrueRange, Tsi, Tsv, TtmSqueeze, TtmSqueezeOutput, Tweezer, TwoCrows,
|
||||||
ValueAreaOutput, ValueAtRisk, Variance, VerticalHorizontalFilter, Vidya, VoltyStop,
|
TypicalPrice, UlcerIndex, UltimateOscillator, UpsideGapTwoCrows, ValueArea, ValueAreaOutput,
|
||||||
VolumeOscillator, VolumePriceTrend, Vortex, VortexOutput, Vwap, VwapStdDevBands,
|
ValueAtRisk, Variance, VerticalHorizontalFilter, Vidya, VoltyStop, VolumeOscillator,
|
||||||
VwapStdDevBandsOutput, Vwma, Vzo, WaveTrend, WaveTrendOutput, WeightedClose, WilliamsFractals,
|
VolumePriceTrend, Vortex, VortexOutput, Vwap, VwapStdDevBands, VwapStdDevBandsOutput, Vwma,
|
||||||
WilliamsFractalsOutput, WilliamsR, Wma, WoodiePivots, WoodiePivotsOutput, YangZhangVolatility,
|
Vzo, WaveTrend, WaveTrendOutput, WeightedClose, WilliamsFractals, WilliamsFractalsOutput,
|
||||||
YoyoExit, ZScore, ZeroLagMacd, ZeroLagMacdOutput, ZigZag, ZigZagOutput, Zlema, FAMILIES, T3,
|
WilliamsR, Wma, WoodiePivots, WoodiePivotsOutput, YangZhangVolatility, YoyoExit, ZScore,
|
||||||
|
ZeroLagMacd, ZeroLagMacdOutput, ZigZag, ZigZagOutput, Zlema, FAMILIES, T3,
|
||||||
};
|
};
|
||||||
// `FootprintLevel` is a row element of `FootprintOutput`, re-exported on its own
|
// `FootprintLevel` is a row element of `FootprintOutput`, re-exported on its own
|
||||||
// line so the indicator-count tooling (which scans the braced block above and
|
// line so the indicator-count tooling (which scans the braced block above and
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@ That includes:
|
|||||||
[Python](https://docs.wickra.org/Quickstart-Python),
|
[Python](https://docs.wickra.org/Quickstart-Python),
|
||||||
[Node](https://docs.wickra.org/Quickstart-Node), and
|
[Node](https://docs.wickra.org/Quickstart-Node), and
|
||||||
[WASM](https://docs.wickra.org/Quickstart-WASM).
|
[WASM](https://docs.wickra.org/Quickstart-WASM).
|
||||||
- A per-indicator deep dive for every one of the **254 indicators** across
|
- A per-indicator deep dive for every one of the **259 indicators** across
|
||||||
the sixteen families (Moving Averages, Momentum Oscillators, Trend &
|
the sixteen families (Moving Averages, Momentum Oscillators, Trend &
|
||||||
Directional, Price Oscillators, Volatility & Bands, Bands & Channels,
|
Directional, Price Oscillators, Volatility & Bands, Bands & Channels,
|
||||||
Trailing Stops, Volume, Price Statistics, Ehlers / Cycle DSP, Pivots &
|
Trailing Stops, Volume, Price Statistics, Ehlers / Cycle DSP, Pivots &
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
use libfuzzer_sys::fuzz_target;
|
use libfuzzer_sys::fuzz_target;
|
||||||
use wickra_core::{
|
use wickra_core::{
|
||||||
AbandonedBaby, AccelerationBands, AcceleratorOscillator, AdOscillator, Adl, AdvanceBlock, Adx, Adxr, Alligator, AnchoredVwap, Aroon, AroonOscillator, Atr, AtrBands, AtrTrailingStop, AwesomeOscillator, AwesomeOscillatorHistogram, BalanceOfPower, BatchExt, BeltHold, Breakaway, Camarilla, Candle, Cci, ChaikinMoneyFlow, ChaikinOscillator, ChaikinVolatility, ChandeKrollStop, ChandelierExit, ChoppinessIndex, ClassicPivots, Counterattack, DemandIndex, DemarkPivots, Doji, Donchian, DonchianStop, EaseOfMovement, Engulfing, Evwma, FibonacciPivots, ForceIndex, FractalChaosBands, GarmanKlassVolatility, Hammer, HangingMan, Harami, HeikinAshi, HiLoActivator, HurstChannel, Ichimoku, IdenticalThreeCrows, Indicator, Inertia, InitialBalance, InvertedHammer, Keltner, Kvo, MarketFacilitationIndex, Marubozu, MassIndex, MedianPrice, Mfi, MorningEveningStar, Natr, Nvi, Obv, OpeningRange, ParkinsonVolatility, Pgo, PiercingDarkCloud, Psar, Pvi, RogersSatchellVolatility, RollingVwap, Rvi, Rwi, ShootingStar, Smi, SpinningTop, StarcBands, Stochastic, SuperTrend, TdCombo, TdCountdown, TdDeMarker, TdDifferential, TdLines, TdOpen, TdPressure, TdRangeProjection, TdRei, TdRiskLevel, TdSequential, TdSetup, ThreeInside, ThreeLineStrike, ThreeOutside, ThreeSoldiersOrCrows, ThreeStarsInSouth, TrueRange, Tsv, TtmSqueeze, Tweezer, TwoCrows, TypicalPrice, UltimateOscillator, UpsideGapTwoCrows, ValueArea, VoltyStop, VolumeOscillator, VolumePriceTrend, Vortex, Vwap, VwapStdDevBands, Vwma, Vzo, WaveTrend, WeightedClose, WilliamsFractals, WilliamsR, WoodiePivots, YangZhangVolatility, YoyoExit, ZigZag
|
AbandonedBaby, AccelerationBands, AcceleratorOscillator, AdOscillator, Adl, AdvanceBlock, Adx, Adxr, Alligator, AnchoredVwap, Aroon, AroonOscillator, Atr, AtrBands, AtrTrailingStop, AwesomeOscillator, AwesomeOscillatorHistogram, BalanceOfPower, BatchExt, BeltHold, Breakaway, Camarilla, Candle, Cci, ChaikinMoneyFlow, ChaikinOscillator, ChaikinVolatility, ChandeKrollStop, ChandelierExit, ChoppinessIndex, ClassicPivots, Counterattack, DemandIndex, DemarkPivots, Doji, DojiStar, Donchian, DonchianStop, DragonflyDoji, EaseOfMovement, Engulfing, Evwma, FibonacciPivots, ForceIndex, FractalChaosBands, GarmanKlassVolatility, GravestoneDoji, Hammer, HangingMan, Harami, HeikinAshi, HiLoActivator, HurstChannel, Ichimoku, IdenticalThreeCrows, Indicator, Inertia, InitialBalance, InvertedHammer, Keltner, Kvo, LongLeggedDoji, MarketFacilitationIndex, Marubozu, MassIndex, MedianPrice, Mfi, MorningEveningStar, Natr, Nvi, Obv, OpeningRange, ParkinsonVolatility, Pgo, PiercingDarkCloud, Psar, Pvi, RickshawMan, RogersSatchellVolatility, RollingVwap, Rvi, Rwi, ShootingStar, Smi, SpinningTop, StarcBands, Stochastic, SuperTrend, TdCombo, TdCountdown, TdDeMarker, TdDifferential, TdLines, TdOpen, TdPressure, TdRangeProjection, TdRei, TdRiskLevel, TdSequential, TdSetup, ThreeInside, ThreeLineStrike, ThreeOutside, ThreeSoldiersOrCrows, ThreeStarsInSouth, TrueRange, Tsv, TtmSqueeze, Tweezer, TwoCrows, TypicalPrice, UltimateOscillator, UpsideGapTwoCrows, ValueArea, VoltyStop, VolumeOscillator, VolumePriceTrend, Vortex, Vwap, VwapStdDevBands, Vwma, Vzo, WaveTrend, WeightedClose, WilliamsFractals, WilliamsR, WoodiePivots, YangZhangVolatility, YoyoExit, ZigZag
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Convert a flat `f64` stream into a `Vec<Candle>` by chunking it into
|
/// Convert a flat `f64` stream into a `Vec<Candle>` by chunking it into
|
||||||
@@ -278,6 +278,11 @@ fuzz_target!(|data: Vec<f64>| {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- Candlestick Patterns (family 14) ---
|
// --- Candlestick Patterns (family 14) ---
|
||||||
|
drive(RickshawMan::new, &candles);
|
||||||
|
drive(LongLeggedDoji::new, &candles);
|
||||||
|
drive(GravestoneDoji::new, &candles);
|
||||||
|
drive(DragonflyDoji::new, &candles);
|
||||||
|
drive(DojiStar::new, &candles);
|
||||||
drive(Counterattack::new, &candles);
|
drive(Counterattack::new, &candles);
|
||||||
drive(Breakaway::new, &candles);
|
drive(Breakaway::new, &candles);
|
||||||
drive(BeltHold::new, &candles);
|
drive(BeltHold::new, &candles);
|
||||||
|
|||||||
Reference in New Issue
Block a user