feat(breadth): complete the Market Breadth family (14 indicators) (#157)
Completes expansion-roadmap block **A2 — Market Breadth**: the 14 indicators that remained after the `AdvanceDecline` bootstrap, all built on the existing `CrossSection` input. ## Indicators (all scalar `Indicator<Input = CrossSection, Output = f64>`) | Indicator | Reading | |-----------|---------| | `AdvanceDeclineRatio` | advancers / decliners | | `AdVolumeLine` | cumulative net advancing volume | | `McClellanOscillator` | 19/39 EMAs of ratio-adjusted net advances | | `McClellanSummationIndex` | running total of the oscillator | | `Trin` (Arms Index) | A/D ratio over up/down volume ratio | | `BreadthThrust` (Zweig) | SMA of the advancing-issues share | | `NewHighsNewLows` | new highs − new lows | | `HighLowIndex` | SMA of the record-high percent | | `PercentAboveMa` | % of the universe above its MA | | `UpDownVolumeRatio` | advancing / declining volume | | `BullishPercentIndex` | % on a point-and-figure buy signal | | `CumulativeVolumeIndex` | volume-normalised cumulative net advancing volume | | `AbsoluteBreadthIndex` | \|advancers − decliners\| | | `TickIndex` | instantaneous net advancers − decliners | ## Input model `AdVolumeLine` and `CumulativeVolumeIndex` are kept distinct (the latter normalises each tick's net advancing volume by total volume, so it stays comparable across volume regimes). `PercentAboveMa` and `BullishPercentIndex` need a per-symbol state signal that `Member` did not carry, so `Member` gains two additive flags (`above_ma`, `on_buy_signal`) via a new `Member::with_signals` constructor; the 4-arg `Member::new` leaves both cleared, so every existing caller and binding is unchanged. `CrossSection` gains volume / new-extreme / state aggregation helpers. ## Wiring Fully wired across the Rust core, the python/node/wasm bindings, the cross-section fuzz target, the README + docs indicator counters (325 → 339), and dedicated python/node streaming-vs-batch tests. `fmt` / `test --workspace --all-features` / `clippy --workspace -D warnings` / node build+test / pytest all green locally.
This commit is contained in:
@@ -1284,6 +1284,112 @@ test('market breadth: AdvanceDecline rejects ragged universe', () => {
|
||||
);
|
||||
});
|
||||
|
||||
test('market breadth: 14 indicators reference values + batch parity', () => {
|
||||
const flags4 = [false, false, false, false];
|
||||
|
||||
// Advance/Decline Ratio: 3/1 = 3 ; 0 advancers -> 0.
|
||||
const adr = new wickra.AdvanceDeclineRatio();
|
||||
assert.equal(adr.update([1, 1, 1, -1], [10, 10, 10, 10], flags4, flags4), 3.0);
|
||||
assert.equal(adr.update([-1, -1, -1, -1], [10, 10, 10, 10], flags4, flags4), 0.0);
|
||||
assert.deepEqual(
|
||||
Array.from(
|
||||
new wickra.AdvanceDeclineRatio().batch(
|
||||
[[1, 1, 1, -1], [-1, -1, -1, -1]],
|
||||
[[10, 10, 10, 10], [10, 10, 10, 10]],
|
||||
[flags4, flags4],
|
||||
[flags4, flags4],
|
||||
),
|
||||
),
|
||||
[3.0, 0.0],
|
||||
);
|
||||
|
||||
// AD Volume Line: cumulative net advancing volume.
|
||||
const adv = new wickra.AdVolumeLine();
|
||||
assert.equal(adv.update([1, -1], [150, 50], [false, false], [false, false]), 100.0);
|
||||
assert.equal(adv.update([1, -1], [60, 60], [false, false], [false, false]), 100.0);
|
||||
|
||||
// McClellan Oscillator + Summation: seed 0, then -50.
|
||||
const osc = new wickra.McClellanOscillator();
|
||||
assert.ok(Math.abs(osc.update([1, 1, 1, -1], [10, 10, 10, 10], flags4, flags4)) < 1e-9);
|
||||
assert.ok(Math.abs(osc.update([-1, -1, -1, 1], [10, 10, 10, 10], flags4, flags4) - -50.0) < 1e-9);
|
||||
const msi = new wickra.McClellanSummationIndex();
|
||||
assert.ok(Math.abs(msi.update([1, 1, 1, -1], [10, 10, 10, 10], flags4, flags4)) < 1e-9);
|
||||
assert.ok(Math.abs(msi.update([-1, -1, -1, 1], [10, 10, 10, 10], flags4, flags4) - -50.0) < 1e-9);
|
||||
|
||||
// TRIN: balanced breadth -> 1.
|
||||
assert.ok(
|
||||
Math.abs(new wickra.Trin().update([1, 1, 1, -1], [50, 50, 50, 50], flags4, flags4) - 1.0) < 1e-9,
|
||||
);
|
||||
|
||||
// Breadth Thrust(2): warmup null, then SMA(2) of [0.8, 0.6] = 0.7.
|
||||
const bt = new wickra.BreadthThrust(2);
|
||||
const up10 = Array(10).fill(false);
|
||||
assert.equal(bt.update([...Array(8).fill(1), -1, -1], Array(10).fill(10), up10, up10), null);
|
||||
assert.ok(
|
||||
Math.abs(bt.update([...Array(6).fill(1), -1, -1, -1, -1], Array(10).fill(10), up10, up10) - 0.7) < 1e-9,
|
||||
);
|
||||
|
||||
// New Highs - New Lows: 2 - 1 = 1.
|
||||
assert.equal(
|
||||
new wickra.NewHighsNewLows().update([1, 1, -1], [10, 10, 10], [true, true, false], [false, false, true]),
|
||||
1.0,
|
||||
);
|
||||
|
||||
// High-Low Index(2): warmup null, then SMA(2) of [80, 60] = 70.
|
||||
const hli = new wickra.HighLowIndex(2);
|
||||
assert.equal(
|
||||
hli.update(Array(10).fill(1), Array(10).fill(10), [...Array(8).fill(true), false, false], [...Array(8).fill(false), true, true]),
|
||||
null,
|
||||
);
|
||||
assert.ok(
|
||||
Math.abs(
|
||||
hli.update(Array(10).fill(1), Array(10).fill(10), [...Array(6).fill(true), false, false, false, false], [...Array(6).fill(false), true, true, true, true]) - 70.0,
|
||||
) < 1e-9,
|
||||
);
|
||||
|
||||
// Percent Above MA: 3/4 -> 75 (5-array update with aboveMa).
|
||||
assert.equal(
|
||||
new wickra.PercentAboveMa().update([1, 1, 1, -1], [10, 10, 10, 10], flags4, flags4, [true, true, true, false]),
|
||||
75.0,
|
||||
);
|
||||
|
||||
// Up/Down Volume Ratio: 150/50 = 3.
|
||||
assert.equal(
|
||||
new wickra.UpDownVolumeRatio().update([1, -1], [150, 50], [false, false], [false, false]),
|
||||
3.0,
|
||||
);
|
||||
|
||||
// Bullish Percent Index: 2/4 -> 50 (5-array update with onBuySignal).
|
||||
assert.equal(
|
||||
new wickra.BullishPercentIndex().update([1, 1, -1, -1], [10, 10, 10, 10], flags4, flags4, [true, true, false, false]),
|
||||
50.0,
|
||||
);
|
||||
|
||||
// Cumulative Volume Index: (100/200) -> 0.5.
|
||||
assert.ok(
|
||||
Math.abs(new wickra.CumulativeVolumeIndex().update([1, -1], [150, 50], [false, false], [false, false]) - 0.5) < 1e-9,
|
||||
);
|
||||
|
||||
// Absolute Breadth Index: |2 - 3| = 1.
|
||||
assert.equal(
|
||||
new wickra.AbsoluteBreadthIndex().update([1, 1, -1, -1, -1], Array(5).fill(10), Array(5).fill(false), Array(5).fill(false)),
|
||||
1.0,
|
||||
);
|
||||
|
||||
// TICK Index: 2 - 3 = -1.
|
||||
assert.equal(
|
||||
new wickra.TickIndex().update([1, 1, -1, -1, -1], Array(5).fill(10), Array(5).fill(false), Array(5).fill(false)),
|
||||
-1.0,
|
||||
);
|
||||
});
|
||||
|
||||
test('market breadth: rejects ragged universe', () => {
|
||||
assert.throws(() => new wickra.Trin().update([1, -1], [10], [false, false], [false, false]));
|
||||
assert.throws(() =>
|
||||
new wickra.PercentAboveMa().update([1, -1], [10, 10], [false, false], [false, false], [true]),
|
||||
);
|
||||
});
|
||||
|
||||
test('OI / flow / liquidation indicators reference values', () => {
|
||||
// OI +10% while price flat -> divergence +0.1.
|
||||
const div = new wickra.OIPriceDivergence(1);
|
||||
|
||||
Vendored
+126
@@ -3253,6 +3253,132 @@ export declare class AdvanceDecline {
|
||||
isReady(): boolean
|
||||
warmupPeriod(): number
|
||||
}
|
||||
export type AdvanceDeclineRatioNode = AdvanceDeclineRatio
|
||||
export declare class AdvanceDeclineRatio {
|
||||
constructor()
|
||||
update(change: Array<number>, volume: Array<number>, newHigh: Array<boolean>, newLow: Array<boolean>): number | null
|
||||
batch(change: Array<Array<number>>, volume: Array<Array<number>>, newHigh: Array<Array<boolean>>, newLow: Array<Array<boolean>>): Array<number>
|
||||
reset(): void
|
||||
isReady(): boolean
|
||||
warmupPeriod(): number
|
||||
}
|
||||
export type AdVolumeLineNode = AdVolumeLine
|
||||
export declare class AdVolumeLine {
|
||||
constructor()
|
||||
update(change: Array<number>, volume: Array<number>, newHigh: Array<boolean>, newLow: Array<boolean>): number | null
|
||||
batch(change: Array<Array<number>>, volume: Array<Array<number>>, newHigh: Array<Array<boolean>>, newLow: Array<Array<boolean>>): Array<number>
|
||||
reset(): void
|
||||
isReady(): boolean
|
||||
warmupPeriod(): number
|
||||
}
|
||||
export type McClellanOscillatorNode = McClellanOscillator
|
||||
export declare class McClellanOscillator {
|
||||
constructor()
|
||||
update(change: Array<number>, volume: Array<number>, newHigh: Array<boolean>, newLow: Array<boolean>): number | null
|
||||
batch(change: Array<Array<number>>, volume: Array<Array<number>>, newHigh: Array<Array<boolean>>, newLow: Array<Array<boolean>>): Array<number>
|
||||
reset(): void
|
||||
isReady(): boolean
|
||||
warmupPeriod(): number
|
||||
}
|
||||
export type McClellanSummationIndexNode = McClellanSummationIndex
|
||||
export declare class McClellanSummationIndex {
|
||||
constructor()
|
||||
update(change: Array<number>, volume: Array<number>, newHigh: Array<boolean>, newLow: Array<boolean>): number | null
|
||||
batch(change: Array<Array<number>>, volume: Array<Array<number>>, newHigh: Array<Array<boolean>>, newLow: Array<Array<boolean>>): Array<number>
|
||||
reset(): void
|
||||
isReady(): boolean
|
||||
warmupPeriod(): number
|
||||
}
|
||||
export type TrinNode = Trin
|
||||
export declare class Trin {
|
||||
constructor()
|
||||
update(change: Array<number>, volume: Array<number>, newHigh: Array<boolean>, newLow: Array<boolean>): number | null
|
||||
batch(change: Array<Array<number>>, volume: Array<Array<number>>, newHigh: Array<Array<boolean>>, newLow: Array<Array<boolean>>): Array<number>
|
||||
reset(): void
|
||||
isReady(): boolean
|
||||
warmupPeriod(): number
|
||||
}
|
||||
export type BreadthThrustNode = BreadthThrust
|
||||
export declare class BreadthThrust {
|
||||
constructor(period: number)
|
||||
update(change: Array<number>, volume: Array<number>, newHigh: Array<boolean>, newLow: Array<boolean>): number | null
|
||||
batch(change: Array<Array<number>>, volume: Array<Array<number>>, newHigh: Array<Array<boolean>>, newLow: Array<Array<boolean>>): Array<number>
|
||||
reset(): void
|
||||
isReady(): boolean
|
||||
warmupPeriod(): number
|
||||
}
|
||||
export type NewHighsNewLowsNode = NewHighsNewLows
|
||||
export declare class NewHighsNewLows {
|
||||
constructor()
|
||||
update(change: Array<number>, volume: Array<number>, newHigh: Array<boolean>, newLow: Array<boolean>): number | null
|
||||
batch(change: Array<Array<number>>, volume: Array<Array<number>>, newHigh: Array<Array<boolean>>, newLow: Array<Array<boolean>>): Array<number>
|
||||
reset(): void
|
||||
isReady(): boolean
|
||||
warmupPeriod(): number
|
||||
}
|
||||
export type HighLowIndexNode = HighLowIndex
|
||||
export declare class HighLowIndex {
|
||||
constructor(period: number)
|
||||
update(change: Array<number>, volume: Array<number>, newHigh: Array<boolean>, newLow: Array<boolean>): number | null
|
||||
batch(change: Array<Array<number>>, volume: Array<Array<number>>, newHigh: Array<Array<boolean>>, newLow: Array<Array<boolean>>): Array<number>
|
||||
reset(): void
|
||||
isReady(): boolean
|
||||
warmupPeriod(): number
|
||||
}
|
||||
export type PercentAboveMaNode = PercentAboveMa
|
||||
export declare class PercentAboveMa {
|
||||
constructor()
|
||||
update(change: Array<number>, volume: Array<number>, newHigh: Array<boolean>, newLow: Array<boolean>, aboveMa: Array<boolean>): number | null
|
||||
batch(change: Array<Array<number>>, volume: Array<Array<number>>, newHigh: Array<Array<boolean>>, newLow: Array<Array<boolean>>, aboveMa: Array<Array<boolean>>): Array<number>
|
||||
reset(): void
|
||||
isReady(): boolean
|
||||
warmupPeriod(): number
|
||||
}
|
||||
export type UpDownVolumeRatioNode = UpDownVolumeRatio
|
||||
export declare class UpDownVolumeRatio {
|
||||
constructor()
|
||||
update(change: Array<number>, volume: Array<number>, newHigh: Array<boolean>, newLow: Array<boolean>): number | null
|
||||
batch(change: Array<Array<number>>, volume: Array<Array<number>>, newHigh: Array<Array<boolean>>, newLow: Array<Array<boolean>>): Array<number>
|
||||
reset(): void
|
||||
isReady(): boolean
|
||||
warmupPeriod(): number
|
||||
}
|
||||
export type BullishPercentIndexNode = BullishPercentIndex
|
||||
export declare class BullishPercentIndex {
|
||||
constructor()
|
||||
update(change: Array<number>, volume: Array<number>, newHigh: Array<boolean>, newLow: Array<boolean>, onBuySignal: Array<boolean>): number | null
|
||||
batch(change: Array<Array<number>>, volume: Array<Array<number>>, newHigh: Array<Array<boolean>>, newLow: Array<Array<boolean>>, onBuySignal: Array<Array<boolean>>): Array<number>
|
||||
reset(): void
|
||||
isReady(): boolean
|
||||
warmupPeriod(): number
|
||||
}
|
||||
export type CumulativeVolumeIndexNode = CumulativeVolumeIndex
|
||||
export declare class CumulativeVolumeIndex {
|
||||
constructor()
|
||||
update(change: Array<number>, volume: Array<number>, newHigh: Array<boolean>, newLow: Array<boolean>): number | null
|
||||
batch(change: Array<Array<number>>, volume: Array<Array<number>>, newHigh: Array<Array<boolean>>, newLow: Array<Array<boolean>>): Array<number>
|
||||
reset(): void
|
||||
isReady(): boolean
|
||||
warmupPeriod(): number
|
||||
}
|
||||
export type AbsoluteBreadthIndexNode = AbsoluteBreadthIndex
|
||||
export declare class AbsoluteBreadthIndex {
|
||||
constructor()
|
||||
update(change: Array<number>, volume: Array<number>, newHigh: Array<boolean>, newLow: Array<boolean>): number | null
|
||||
batch(change: Array<Array<number>>, volume: Array<Array<number>>, newHigh: Array<Array<boolean>>, newLow: Array<Array<boolean>>): Array<number>
|
||||
reset(): void
|
||||
isReady(): boolean
|
||||
warmupPeriod(): number
|
||||
}
|
||||
export type TickIndexNode = TickIndex
|
||||
export declare class TickIndex {
|
||||
constructor()
|
||||
update(change: Array<number>, volume: Array<number>, newHigh: Array<boolean>, newLow: Array<boolean>): number | null
|
||||
batch(change: Array<Array<number>>, volume: Array<Array<number>>, newHigh: Array<Array<boolean>>, newLow: Array<Array<boolean>>): Array<number>
|
||||
reset(): void
|
||||
isReady(): boolean
|
||||
warmupPeriod(): number
|
||||
}
|
||||
export type SharpeRatioNode = SharpeRatio
|
||||
export declare class SharpeRatio {
|
||||
constructor(period: number, riskFree: number)
|
||||
|
||||
+15
-1
@@ -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, MIDPOINT, ROCP, ROCR, ROCR100, LINEARREG_INTERCEPT, TSF, Autocorrelation, HurstExponent, PearsonCorrelation, Beta, PairwiseBeta, SpearmanCorrelation, RollingCorrelation, RollingCovariance, OuHalfLife, SpreadHurst, DistanceSsd, BetaNeutralSpread, PairSpreadZScore, LeadLagCrossCorrelation, Cointegration, RelativeStrengthAB, VarianceRatio, GrangerCausality, KalmanHedgeRatio, SpreadBollingerBands, MACD, MACDFIX, MACDEXT, BollingerBands, ATR, PLUS_DM, MINUS_DM, PLUS_DI, MINUS_DI, DX, MIDPRICE, AVGPRICE, SAREXT, HT_PHASOR, 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, AnchoredRSI, 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, HT_DCPHASE, HT_TRENDMODE, HilbertDominantCycle, AdaptiveCycle, SineWave, MAMA, FAMA, Ichimoku, HeikinAshi, ValueArea, VolumeProfile, TpoProfile, 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, UpsideGapThreeMethods, DownsideGapThreeMethods, StalledPattern, StickSandwich, Takuri, ClosingMarubozu, OpeningMarubozu, TasukiGap, UniqueThreeRiver, ConcealingBabySwallow, 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, AdvanceDecline, SharpeRatio, SortinoRatio, CalmarRatio, OmegaRatio, MaxDrawdown, AverageDrawdown, DrawdownDuration, PainIndex, ValueAtRisk, ConditionalValueAtRisk, ProfitFactor, GainLossRatio, RecoveryFactor, KellyCriterion, TreynorRatio, InformationRatio, RenkoBars, KagiBars, PointAndFigureBars, 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, MIDPOINT, ROCP, ROCR, ROCR100, LINEARREG_INTERCEPT, TSF, Autocorrelation, HurstExponent, PearsonCorrelation, Beta, PairwiseBeta, SpearmanCorrelation, RollingCorrelation, RollingCovariance, OuHalfLife, SpreadHurst, DistanceSsd, BetaNeutralSpread, PairSpreadZScore, LeadLagCrossCorrelation, Cointegration, RelativeStrengthAB, VarianceRatio, GrangerCausality, KalmanHedgeRatio, SpreadBollingerBands, MACD, MACDFIX, MACDEXT, BollingerBands, ATR, PLUS_DM, MINUS_DM, PLUS_DI, MINUS_DI, DX, MIDPRICE, AVGPRICE, SAREXT, HT_PHASOR, 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, AnchoredRSI, 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, HT_DCPHASE, HT_TRENDMODE, HilbertDominantCycle, AdaptiveCycle, SineWave, MAMA, FAMA, Ichimoku, HeikinAshi, ValueArea, VolumeProfile, TpoProfile, 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, UpsideGapThreeMethods, DownsideGapThreeMethods, StalledPattern, StickSandwich, Takuri, ClosingMarubozu, OpeningMarubozu, TasukiGap, UniqueThreeRiver, ConcealingBabySwallow, 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, AdvanceDecline, AdvanceDeclineRatio, AdVolumeLine, McClellanOscillator, McClellanSummationIndex, Trin, BreadthThrust, NewHighsNewLows, HighLowIndex, PercentAboveMa, UpDownVolumeRatio, BullishPercentIndex, CumulativeVolumeIndex, AbsoluteBreadthIndex, TickIndex, SharpeRatio, SortinoRatio, CalmarRatio, OmegaRatio, MaxDrawdown, AverageDrawdown, DrawdownDuration, PainIndex, ValueAtRisk, ConditionalValueAtRisk, ProfitFactor, GainLossRatio, RecoveryFactor, KellyCriterion, TreynorRatio, InformationRatio, RenkoBars, KagiBars, PointAndFigureBars, Alpha } = nativeBinding
|
||||
|
||||
module.exports.version = version
|
||||
module.exports.SMA = SMA
|
||||
@@ -618,6 +618,20 @@ module.exports.LiquidationFeatures = LiquidationFeatures
|
||||
module.exports.TermStructureBasis = TermStructureBasis
|
||||
module.exports.CalendarSpread = CalendarSpread
|
||||
module.exports.AdvanceDecline = AdvanceDecline
|
||||
module.exports.AdvanceDeclineRatio = AdvanceDeclineRatio
|
||||
module.exports.AdVolumeLine = AdVolumeLine
|
||||
module.exports.McClellanOscillator = McClellanOscillator
|
||||
module.exports.McClellanSummationIndex = McClellanSummationIndex
|
||||
module.exports.Trin = Trin
|
||||
module.exports.BreadthThrust = BreadthThrust
|
||||
module.exports.NewHighsNewLows = NewHighsNewLows
|
||||
module.exports.HighLowIndex = HighLowIndex
|
||||
module.exports.PercentAboveMa = PercentAboveMa
|
||||
module.exports.UpDownVolumeRatio = UpDownVolumeRatio
|
||||
module.exports.BullishPercentIndex = BullishPercentIndex
|
||||
module.exports.CumulativeVolumeIndex = CumulativeVolumeIndex
|
||||
module.exports.AbsoluteBreadthIndex = AbsoluteBreadthIndex
|
||||
module.exports.TickIndex = TickIndex
|
||||
module.exports.SharpeRatio = SharpeRatio
|
||||
module.exports.SortinoRatio = SortinoRatio
|
||||
module.exports.CalmarRatio = CalmarRatio
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -352,6 +352,20 @@ from ._wickra import (
|
||||
TermStructureBasis,
|
||||
CalendarSpread,
|
||||
# Market Breadth
|
||||
TickIndex,
|
||||
AbsoluteBreadthIndex,
|
||||
CumulativeVolumeIndex,
|
||||
BullishPercentIndex,
|
||||
UpDownVolumeRatio,
|
||||
PercentAboveMa,
|
||||
HighLowIndex,
|
||||
NewHighsNewLows,
|
||||
BreadthThrust,
|
||||
Trin,
|
||||
McClellanSummationIndex,
|
||||
McClellanOscillator,
|
||||
AdVolumeLine,
|
||||
AdvanceDeclineRatio,
|
||||
AdvanceDecline,
|
||||
# Risk / Performance
|
||||
SharpeRatio,
|
||||
@@ -702,6 +716,20 @@ __all__ = [
|
||||
"TermStructureBasis",
|
||||
"CalendarSpread",
|
||||
# Market Breadth
|
||||
"TickIndex",
|
||||
"AbsoluteBreadthIndex",
|
||||
"CumulativeVolumeIndex",
|
||||
"BullishPercentIndex",
|
||||
"UpDownVolumeRatio",
|
||||
"PercentAboveMa",
|
||||
"HighLowIndex",
|
||||
"NewHighsNewLows",
|
||||
"BreadthThrust",
|
||||
"Trin",
|
||||
"McClellanSummationIndex",
|
||||
"McClellanOscillator",
|
||||
"AdVolumeLine",
|
||||
"AdvanceDeclineRatio",
|
||||
"AdvanceDecline",
|
||||
# Risk / Performance
|
||||
"SharpeRatio",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2783,6 +2783,190 @@ def test_advance_decline_rejects_ragged_universe():
|
||||
ad.update([1.0, -1.0], [10.0], [False, False], [False, False])
|
||||
|
||||
|
||||
def _breadth_streaming_equals_batch(indicator, change, volume, new_high, new_low):
|
||||
"""Assert a 4-array breadth indicator's batch matches its streaming output."""
|
||||
batch = indicator().batch(change, volume, new_high, new_low)
|
||||
streamer = indicator()
|
||||
streamed = np.array(
|
||||
[
|
||||
streamer.update(change[i], volume[i], new_high[i], new_low[i])
|
||||
for i in range(len(change))
|
||||
],
|
||||
dtype=np.float64,
|
||||
)
|
||||
assert batch.shape == (len(change),)
|
||||
assert _eq_nan(batch, streamed)
|
||||
return batch
|
||||
|
||||
|
||||
def test_advance_decline_ratio_breadth():
|
||||
change = [[1.0, 1.0, 1.0, -1.0], [1.0, 0.0, 0.0, 0.0], [-1.0, -1.0, -1.0, -1.0]]
|
||||
volume = [[10.0] * 4 for _ in range(3)]
|
||||
flags = [[False] * 4 for _ in range(3)]
|
||||
batch = _breadth_streaming_equals_batch(ta.AdvanceDeclineRatio, change, volume, flags, flags)
|
||||
# 3/1 = 3 ; 1/max(0,1) = 1 ; 0/3 = 0.
|
||||
assert list(batch) == [3.0, 1.0, 0.0]
|
||||
|
||||
|
||||
def test_ad_volume_line_breadth():
|
||||
change = [[1.0, -1.0], [1.0, -1.0], [1.0, 0.0]]
|
||||
volume = [[150.0, 50.0], [60.0, 60.0], [30.0, 0.0]]
|
||||
flags = [[False] * 2 for _ in range(3)]
|
||||
batch = _breadth_streaming_equals_batch(ta.AdVolumeLine, change, volume, flags, flags)
|
||||
# net +100 -> 100 ; net 0 -> 100 ; net +30 -> 130.
|
||||
assert list(batch) == [100.0, 100.0, 130.0]
|
||||
|
||||
|
||||
def test_mcclellan_oscillator_breadth():
|
||||
change = [[1.0, 1.0, 1.0, -1.0], [-1.0, -1.0, -1.0, 1.0], [1.0, 1.0, -1.0, -1.0]]
|
||||
volume = [[10.0] * 4 for _ in range(3)]
|
||||
flags = [[False] * 4 for _ in range(3)]
|
||||
batch = _breadth_streaming_equals_batch(ta.McClellanOscillator, change, volume, flags, flags)
|
||||
# seed 0 ; -50 ; -67.5.
|
||||
assert abs(batch[0]) < 1e-9
|
||||
assert abs(batch[1] - (-50.0)) < 1e-9
|
||||
assert abs(batch[2] - (-67.5)) < 1e-9
|
||||
|
||||
|
||||
def test_mcclellan_summation_index_breadth():
|
||||
change = [[1.0, 1.0, 1.0, -1.0], [-1.0, -1.0, -1.0, 1.0], [1.0, 1.0, -1.0, -1.0]]
|
||||
volume = [[10.0] * 4 for _ in range(3)]
|
||||
flags = [[False] * 4 for _ in range(3)]
|
||||
batch = _breadth_streaming_equals_batch(ta.McClellanSummationIndex, change, volume, flags, flags)
|
||||
# 0 ; -50 ; -117.5.
|
||||
assert abs(batch[0]) < 1e-9
|
||||
assert abs(batch[1] - (-50.0)) < 1e-9
|
||||
assert abs(batch[2] - (-117.5)) < 1e-9
|
||||
|
||||
|
||||
def test_trin_breadth():
|
||||
change = [[1.0, 1.0, 1.0, -1.0], [1.0, 1.0, -1.0, -1.0]]
|
||||
volume = [[50.0, 50.0, 50.0, 50.0], [10.0, 10.0, 40.0, 40.0]]
|
||||
flags = [[False] * 4 for _ in range(2)]
|
||||
batch = _breadth_streaming_equals_batch(ta.Trin, change, volume, flags, flags)
|
||||
# (3/1)/(150/50) = 1 ; (2/2)/(20/80) = 4.
|
||||
assert abs(batch[0] - 1.0) < 1e-9
|
||||
assert abs(batch[1] - 4.0) < 1e-9
|
||||
|
||||
|
||||
def test_breadth_thrust_breadth():
|
||||
change = [[1.0] * 8 + [-1.0] * 2, [1.0] * 6 + [-1.0] * 4]
|
||||
volume = [[10.0] * 10 for _ in range(2)]
|
||||
flags = [[False] * 10 for _ in range(2)]
|
||||
batch = ta.BreadthThrust(2).batch(change, volume, flags, flags)
|
||||
streamer = ta.BreadthThrust(2)
|
||||
streamed = np.array(
|
||||
[streamer.update(change[i], volume[i], flags[i], flags[i]) for i in range(2)],
|
||||
dtype=np.float64,
|
||||
)
|
||||
assert _eq_nan(batch, streamed)
|
||||
# 0.8 (warmup -> NaN) ; SMA(2) of [0.8, 0.6] = 0.7.
|
||||
assert math.isnan(batch[0])
|
||||
assert abs(batch[1] - 0.7) < 1e-9
|
||||
|
||||
|
||||
def test_new_highs_new_lows_breadth():
|
||||
change = [[1.0, 1.0, -1.0], [1.0, -1.0, -1.0]]
|
||||
volume = [[10.0] * 3 for _ in range(2)]
|
||||
new_high = [[True, True, False], [True, False, False]]
|
||||
new_low = [[False, False, True], [False, True, True]]
|
||||
batch = _breadth_streaming_equals_batch(ta.NewHighsNewLows, change, volume, new_high, new_low)
|
||||
# 2 - 1 = 1 ; 1 - 2 = -1.
|
||||
assert list(batch) == [1.0, -1.0]
|
||||
|
||||
|
||||
def test_high_low_index_breadth():
|
||||
change = [[1.0] * 10, [1.0] * 10]
|
||||
volume = [[10.0] * 10 for _ in range(2)]
|
||||
new_high = [[True] * 8 + [False] * 2, [True] * 6 + [False] * 4]
|
||||
new_low = [[False] * 8 + [True] * 2, [False] * 6 + [True] * 4]
|
||||
batch = ta.HighLowIndex(2).batch(change, volume, new_high, new_low)
|
||||
streamer = ta.HighLowIndex(2)
|
||||
streamed = np.array(
|
||||
[streamer.update(change[i], volume[i], new_high[i], new_low[i]) for i in range(2)],
|
||||
dtype=np.float64,
|
||||
)
|
||||
assert _eq_nan(batch, streamed)
|
||||
# 80% (warmup) ; SMA(2) of [80, 60] = 70.
|
||||
assert math.isnan(batch[0])
|
||||
assert abs(batch[1] - 70.0) < 1e-9
|
||||
|
||||
|
||||
def test_percent_above_ma_breadth():
|
||||
change = [[1.0, 1.0, 1.0, -1.0], [1.0, 1.0, -1.0, -1.0]]
|
||||
volume = [[10.0] * 4 for _ in range(2)]
|
||||
flags = [[False] * 4 for _ in range(2)]
|
||||
above_ma = [[True, True, True, False], [True, False, False, False]]
|
||||
batch = ta.PercentAboveMa().batch(change, volume, flags, flags, above_ma)
|
||||
streamer = ta.PercentAboveMa()
|
||||
streamed = np.array(
|
||||
[streamer.update(change[i], volume[i], flags[i], flags[i], above_ma[i]) for i in range(2)],
|
||||
dtype=np.float64,
|
||||
)
|
||||
assert _eq_nan(batch, streamed)
|
||||
# 3/4 -> 75 ; 1/4 -> 25.
|
||||
assert list(batch) == [75.0, 25.0]
|
||||
|
||||
|
||||
def test_up_down_volume_ratio_breadth():
|
||||
change = [[1.0, -1.0], [1.0, 0.0]]
|
||||
volume = [[150.0, 50.0], [100.0, 0.0]]
|
||||
flags = [[False] * 2 for _ in range(2)]
|
||||
batch = _breadth_streaming_equals_batch(ta.UpDownVolumeRatio, change, volume, flags, flags)
|
||||
# 150/50 = 3 ; 100/max(0,1) = 100.
|
||||
assert list(batch) == [3.0, 100.0]
|
||||
|
||||
|
||||
def test_bullish_percent_index_breadth():
|
||||
change = [[1.0, 1.0, -1.0, -1.0], [1.0, 1.0, 1.0, 1.0]]
|
||||
volume = [[10.0] * 4 for _ in range(2)]
|
||||
flags = [[False] * 4 for _ in range(2)]
|
||||
on_buy = [[True, True, False, False], [True, True, True, True]]
|
||||
batch = ta.BullishPercentIndex().batch(change, volume, flags, flags, on_buy)
|
||||
streamer = ta.BullishPercentIndex()
|
||||
streamed = np.array(
|
||||
[streamer.update(change[i], volume[i], flags[i], flags[i], on_buy[i]) for i in range(2)],
|
||||
dtype=np.float64,
|
||||
)
|
||||
assert _eq_nan(batch, streamed)
|
||||
# 2/4 -> 50 ; 4/4 -> 100.
|
||||
assert list(batch) == [50.0, 100.0]
|
||||
|
||||
|
||||
def test_cumulative_volume_index_breadth():
|
||||
change = [[1.0, -1.0], [1.0, -1.0], [0.0]]
|
||||
volume = [[150.0, 50.0], [60.0, 60.0], [0.0]]
|
||||
new_high = [[False, False], [False, False], [False]]
|
||||
new_low = [[False, False], [False, False], [False]]
|
||||
batch = ta.CumulativeVolumeIndex().batch(change, volume, new_high, new_low)
|
||||
streamer = ta.CumulativeVolumeIndex()
|
||||
streamed = np.array(
|
||||
[streamer.update(change[i], volume[i], new_high[i], new_low[i]) for i in range(3)],
|
||||
dtype=np.float64,
|
||||
)
|
||||
assert _eq_nan(batch, streamed)
|
||||
# (100/200) -> 0.5 ; net 0 -> 0.5 ; zero-volume tick -> 0.5.
|
||||
assert list(batch) == [0.5, 0.5, 0.5]
|
||||
|
||||
|
||||
def test_absolute_breadth_index_breadth():
|
||||
change = [[1.0, 1.0, -1.0, -1.0, -1.0], [1.0, 1.0, 1.0, -1.0, -1.0]]
|
||||
volume = [[10.0] * 5 for _ in range(2)]
|
||||
flags = [[False] * 5 for _ in range(2)]
|
||||
batch = _breadth_streaming_equals_batch(ta.AbsoluteBreadthIndex, change, volume, flags, flags)
|
||||
# |2 - 3| = 1 ; |3 - 2| = 1.
|
||||
assert list(batch) == [1.0, 1.0]
|
||||
|
||||
|
||||
def test_tick_index_breadth():
|
||||
change = [[1.0, 1.0, -1.0, -1.0, -1.0], [1.0, 1.0, 1.0, -1.0, -1.0]]
|
||||
volume = [[10.0] * 5 for _ in range(2)]
|
||||
flags = [[False] * 5 for _ in range(2)]
|
||||
batch = _breadth_streaming_equals_batch(ta.TickIndex, change, volume, flags, flags)
|
||||
# 2 - 3 = -1 ; 3 - 2 = 1.
|
||||
assert list(batch) == [-1.0, 1.0]
|
||||
|
||||
|
||||
def test_funding_basis_streaming_equals_batch():
|
||||
n = 40
|
||||
index = np.array([100.0 + 0.5 * math.sin(i * 0.2) for i in range(n)], dtype=np.float64)
|
||||
|
||||
@@ -8437,6 +8437,664 @@ impl WasmAdvanceDecline {
|
||||
}
|
||||
}
|
||||
|
||||
fn build_cross_section_above_ma(
|
||||
change: &[f64],
|
||||
volume: &[f64],
|
||||
new_high: &[f64],
|
||||
new_low: &[f64],
|
||||
above_ma: &[f64],
|
||||
) -> Result<wc::CrossSection, JsError> {
|
||||
if change.len() != volume.len()
|
||||
|| change.len() != new_high.len()
|
||||
|| change.len() != new_low.len()
|
||||
|| change.len() != above_ma.len()
|
||||
{
|
||||
return Err(JsError::new(
|
||||
"change, volume, newHigh, newLow and aboveMa must be equal length",
|
||||
));
|
||||
}
|
||||
let members = (0..change.len())
|
||||
.map(|i| {
|
||||
wc::Member::with_signals(
|
||||
change[i],
|
||||
volume[i],
|
||||
new_high[i] != 0.0,
|
||||
new_low[i] != 0.0,
|
||||
above_ma[i] != 0.0,
|
||||
false,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
wc::CrossSection::new(members, 0).map_err(map_err)
|
||||
}
|
||||
|
||||
fn build_cross_section_buy(
|
||||
change: &[f64],
|
||||
volume: &[f64],
|
||||
new_high: &[f64],
|
||||
new_low: &[f64],
|
||||
on_buy_signal: &[f64],
|
||||
) -> Result<wc::CrossSection, JsError> {
|
||||
if change.len() != volume.len()
|
||||
|| change.len() != new_high.len()
|
||||
|| change.len() != new_low.len()
|
||||
|| change.len() != on_buy_signal.len()
|
||||
{
|
||||
return Err(JsError::new(
|
||||
"change, volume, newHigh, newLow and onBuySignal must be equal length",
|
||||
));
|
||||
}
|
||||
let members = (0..change.len())
|
||||
.map(|i| {
|
||||
wc::Member::with_signals(
|
||||
change[i],
|
||||
volume[i],
|
||||
new_high[i] != 0.0,
|
||||
new_low[i] != 0.0,
|
||||
false,
|
||||
on_buy_signal[i] != 0.0,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
wc::CrossSection::new(members, 0).map_err(map_err)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = AdvanceDeclineRatio)]
|
||||
pub struct WasmAdvanceDeclineRatio {
|
||||
inner: wc::AdvanceDeclineRatio,
|
||||
}
|
||||
|
||||
impl Default for WasmAdvanceDeclineRatio {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_class = AdvanceDeclineRatio)]
|
||||
impl WasmAdvanceDeclineRatio {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new() -> WasmAdvanceDeclineRatio {
|
||||
Self {
|
||||
inner: wc::AdvanceDeclineRatio::new(),
|
||||
}
|
||||
}
|
||||
pub fn update(
|
||||
&mut self,
|
||||
change: Vec<f64>,
|
||||
volume: Vec<f64>,
|
||||
new_high: Vec<f64>,
|
||||
new_low: Vec<f64>,
|
||||
) -> Result<Option<f64>, JsError> {
|
||||
Ok(self
|
||||
.inner
|
||||
.update(build_cross_section(&change, &volume, &new_high, &new_low)?))
|
||||
}
|
||||
pub fn reset(&mut self) {
|
||||
self.inner.reset();
|
||||
}
|
||||
#[wasm_bindgen(js_name = isReady)]
|
||||
pub fn is_ready(&self) -> bool {
|
||||
self.inner.is_ready()
|
||||
}
|
||||
#[wasm_bindgen(js_name = warmupPeriod)]
|
||||
pub fn warmup_period(&self) -> usize {
|
||||
self.inner.warmup_period()
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = AdVolumeLine)]
|
||||
pub struct WasmAdVolumeLine {
|
||||
inner: wc::AdVolumeLine,
|
||||
}
|
||||
|
||||
impl Default for WasmAdVolumeLine {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_class = AdVolumeLine)]
|
||||
impl WasmAdVolumeLine {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new() -> WasmAdVolumeLine {
|
||||
Self {
|
||||
inner: wc::AdVolumeLine::new(),
|
||||
}
|
||||
}
|
||||
pub fn update(
|
||||
&mut self,
|
||||
change: Vec<f64>,
|
||||
volume: Vec<f64>,
|
||||
new_high: Vec<f64>,
|
||||
new_low: Vec<f64>,
|
||||
) -> Result<Option<f64>, JsError> {
|
||||
Ok(self
|
||||
.inner
|
||||
.update(build_cross_section(&change, &volume, &new_high, &new_low)?))
|
||||
}
|
||||
pub fn reset(&mut self) {
|
||||
self.inner.reset();
|
||||
}
|
||||
#[wasm_bindgen(js_name = isReady)]
|
||||
pub fn is_ready(&self) -> bool {
|
||||
self.inner.is_ready()
|
||||
}
|
||||
#[wasm_bindgen(js_name = warmupPeriod)]
|
||||
pub fn warmup_period(&self) -> usize {
|
||||
self.inner.warmup_period()
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = McClellanOscillator)]
|
||||
pub struct WasmMcClellanOscillator {
|
||||
inner: wc::McClellanOscillator,
|
||||
}
|
||||
|
||||
impl Default for WasmMcClellanOscillator {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_class = McClellanOscillator)]
|
||||
impl WasmMcClellanOscillator {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new() -> WasmMcClellanOscillator {
|
||||
Self {
|
||||
inner: wc::McClellanOscillator::new(),
|
||||
}
|
||||
}
|
||||
pub fn update(
|
||||
&mut self,
|
||||
change: Vec<f64>,
|
||||
volume: Vec<f64>,
|
||||
new_high: Vec<f64>,
|
||||
new_low: Vec<f64>,
|
||||
) -> Result<Option<f64>, JsError> {
|
||||
Ok(self
|
||||
.inner
|
||||
.update(build_cross_section(&change, &volume, &new_high, &new_low)?))
|
||||
}
|
||||
pub fn reset(&mut self) {
|
||||
self.inner.reset();
|
||||
}
|
||||
#[wasm_bindgen(js_name = isReady)]
|
||||
pub fn is_ready(&self) -> bool {
|
||||
self.inner.is_ready()
|
||||
}
|
||||
#[wasm_bindgen(js_name = warmupPeriod)]
|
||||
pub fn warmup_period(&self) -> usize {
|
||||
self.inner.warmup_period()
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = McClellanSummationIndex)]
|
||||
pub struct WasmMcClellanSummationIndex {
|
||||
inner: wc::McClellanSummationIndex,
|
||||
}
|
||||
|
||||
impl Default for WasmMcClellanSummationIndex {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_class = McClellanSummationIndex)]
|
||||
impl WasmMcClellanSummationIndex {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new() -> WasmMcClellanSummationIndex {
|
||||
Self {
|
||||
inner: wc::McClellanSummationIndex::new(),
|
||||
}
|
||||
}
|
||||
pub fn update(
|
||||
&mut self,
|
||||
change: Vec<f64>,
|
||||
volume: Vec<f64>,
|
||||
new_high: Vec<f64>,
|
||||
new_low: Vec<f64>,
|
||||
) -> Result<Option<f64>, JsError> {
|
||||
Ok(self
|
||||
.inner
|
||||
.update(build_cross_section(&change, &volume, &new_high, &new_low)?))
|
||||
}
|
||||
pub fn reset(&mut self) {
|
||||
self.inner.reset();
|
||||
}
|
||||
#[wasm_bindgen(js_name = isReady)]
|
||||
pub fn is_ready(&self) -> bool {
|
||||
self.inner.is_ready()
|
||||
}
|
||||
#[wasm_bindgen(js_name = warmupPeriod)]
|
||||
pub fn warmup_period(&self) -> usize {
|
||||
self.inner.warmup_period()
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = Trin)]
|
||||
pub struct WasmTrin {
|
||||
inner: wc::Trin,
|
||||
}
|
||||
|
||||
impl Default for WasmTrin {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_class = Trin)]
|
||||
impl WasmTrin {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new() -> WasmTrin {
|
||||
Self {
|
||||
inner: wc::Trin::new(),
|
||||
}
|
||||
}
|
||||
pub fn update(
|
||||
&mut self,
|
||||
change: Vec<f64>,
|
||||
volume: Vec<f64>,
|
||||
new_high: Vec<f64>,
|
||||
new_low: Vec<f64>,
|
||||
) -> Result<Option<f64>, JsError> {
|
||||
Ok(self
|
||||
.inner
|
||||
.update(build_cross_section(&change, &volume, &new_high, &new_low)?))
|
||||
}
|
||||
pub fn reset(&mut self) {
|
||||
self.inner.reset();
|
||||
}
|
||||
#[wasm_bindgen(js_name = isReady)]
|
||||
pub fn is_ready(&self) -> bool {
|
||||
self.inner.is_ready()
|
||||
}
|
||||
#[wasm_bindgen(js_name = warmupPeriod)]
|
||||
pub fn warmup_period(&self) -> usize {
|
||||
self.inner.warmup_period()
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = BreadthThrust)]
|
||||
pub struct WasmBreadthThrust {
|
||||
inner: wc::BreadthThrust,
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_class = BreadthThrust)]
|
||||
impl WasmBreadthThrust {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new(period: usize) -> Result<WasmBreadthThrust, JsError> {
|
||||
Ok(WasmBreadthThrust {
|
||||
inner: wc::BreadthThrust::new(period).map_err(map_err)?,
|
||||
})
|
||||
}
|
||||
pub fn update(
|
||||
&mut self,
|
||||
change: Vec<f64>,
|
||||
volume: Vec<f64>,
|
||||
new_high: Vec<f64>,
|
||||
new_low: Vec<f64>,
|
||||
) -> Result<Option<f64>, JsError> {
|
||||
Ok(self
|
||||
.inner
|
||||
.update(build_cross_section(&change, &volume, &new_high, &new_low)?))
|
||||
}
|
||||
pub fn reset(&mut self) {
|
||||
self.inner.reset();
|
||||
}
|
||||
#[wasm_bindgen(js_name = isReady)]
|
||||
pub fn is_ready(&self) -> bool {
|
||||
self.inner.is_ready()
|
||||
}
|
||||
#[wasm_bindgen(js_name = warmupPeriod)]
|
||||
pub fn warmup_period(&self) -> usize {
|
||||
self.inner.warmup_period()
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = NewHighsNewLows)]
|
||||
pub struct WasmNewHighsNewLows {
|
||||
inner: wc::NewHighsNewLows,
|
||||
}
|
||||
|
||||
impl Default for WasmNewHighsNewLows {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_class = NewHighsNewLows)]
|
||||
impl WasmNewHighsNewLows {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new() -> WasmNewHighsNewLows {
|
||||
Self {
|
||||
inner: wc::NewHighsNewLows::new(),
|
||||
}
|
||||
}
|
||||
pub fn update(
|
||||
&mut self,
|
||||
change: Vec<f64>,
|
||||
volume: Vec<f64>,
|
||||
new_high: Vec<f64>,
|
||||
new_low: Vec<f64>,
|
||||
) -> Result<Option<f64>, JsError> {
|
||||
Ok(self
|
||||
.inner
|
||||
.update(build_cross_section(&change, &volume, &new_high, &new_low)?))
|
||||
}
|
||||
pub fn reset(&mut self) {
|
||||
self.inner.reset();
|
||||
}
|
||||
#[wasm_bindgen(js_name = isReady)]
|
||||
pub fn is_ready(&self) -> bool {
|
||||
self.inner.is_ready()
|
||||
}
|
||||
#[wasm_bindgen(js_name = warmupPeriod)]
|
||||
pub fn warmup_period(&self) -> usize {
|
||||
self.inner.warmup_period()
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = HighLowIndex)]
|
||||
pub struct WasmHighLowIndex {
|
||||
inner: wc::HighLowIndex,
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_class = HighLowIndex)]
|
||||
impl WasmHighLowIndex {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new(period: usize) -> Result<WasmHighLowIndex, JsError> {
|
||||
Ok(WasmHighLowIndex {
|
||||
inner: wc::HighLowIndex::new(period).map_err(map_err)?,
|
||||
})
|
||||
}
|
||||
pub fn update(
|
||||
&mut self,
|
||||
change: Vec<f64>,
|
||||
volume: Vec<f64>,
|
||||
new_high: Vec<f64>,
|
||||
new_low: Vec<f64>,
|
||||
) -> Result<Option<f64>, JsError> {
|
||||
Ok(self
|
||||
.inner
|
||||
.update(build_cross_section(&change, &volume, &new_high, &new_low)?))
|
||||
}
|
||||
pub fn reset(&mut self) {
|
||||
self.inner.reset();
|
||||
}
|
||||
#[wasm_bindgen(js_name = isReady)]
|
||||
pub fn is_ready(&self) -> bool {
|
||||
self.inner.is_ready()
|
||||
}
|
||||
#[wasm_bindgen(js_name = warmupPeriod)]
|
||||
pub fn warmup_period(&self) -> usize {
|
||||
self.inner.warmup_period()
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = PercentAboveMa)]
|
||||
pub struct WasmPercentAboveMa {
|
||||
inner: wc::PercentAboveMa,
|
||||
}
|
||||
|
||||
impl Default for WasmPercentAboveMa {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_class = PercentAboveMa)]
|
||||
impl WasmPercentAboveMa {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new() -> WasmPercentAboveMa {
|
||||
Self {
|
||||
inner: wc::PercentAboveMa::new(),
|
||||
}
|
||||
}
|
||||
pub fn update(
|
||||
&mut self,
|
||||
change: Vec<f64>,
|
||||
volume: Vec<f64>,
|
||||
new_high: Vec<f64>,
|
||||
new_low: Vec<f64>,
|
||||
above_ma: Vec<f64>,
|
||||
) -> Result<Option<f64>, JsError> {
|
||||
Ok(self.inner.update(build_cross_section_above_ma(
|
||||
&change, &volume, &new_high, &new_low, &above_ma,
|
||||
)?))
|
||||
}
|
||||
pub fn reset(&mut self) {
|
||||
self.inner.reset();
|
||||
}
|
||||
#[wasm_bindgen(js_name = isReady)]
|
||||
pub fn is_ready(&self) -> bool {
|
||||
self.inner.is_ready()
|
||||
}
|
||||
#[wasm_bindgen(js_name = warmupPeriod)]
|
||||
pub fn warmup_period(&self) -> usize {
|
||||
self.inner.warmup_period()
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = UpDownVolumeRatio)]
|
||||
pub struct WasmUpDownVolumeRatio {
|
||||
inner: wc::UpDownVolumeRatio,
|
||||
}
|
||||
|
||||
impl Default for WasmUpDownVolumeRatio {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_class = UpDownVolumeRatio)]
|
||||
impl WasmUpDownVolumeRatio {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new() -> WasmUpDownVolumeRatio {
|
||||
Self {
|
||||
inner: wc::UpDownVolumeRatio::new(),
|
||||
}
|
||||
}
|
||||
pub fn update(
|
||||
&mut self,
|
||||
change: Vec<f64>,
|
||||
volume: Vec<f64>,
|
||||
new_high: Vec<f64>,
|
||||
new_low: Vec<f64>,
|
||||
) -> Result<Option<f64>, JsError> {
|
||||
Ok(self
|
||||
.inner
|
||||
.update(build_cross_section(&change, &volume, &new_high, &new_low)?))
|
||||
}
|
||||
pub fn reset(&mut self) {
|
||||
self.inner.reset();
|
||||
}
|
||||
#[wasm_bindgen(js_name = isReady)]
|
||||
pub fn is_ready(&self) -> bool {
|
||||
self.inner.is_ready()
|
||||
}
|
||||
#[wasm_bindgen(js_name = warmupPeriod)]
|
||||
pub fn warmup_period(&self) -> usize {
|
||||
self.inner.warmup_period()
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = BullishPercentIndex)]
|
||||
pub struct WasmBullishPercentIndex {
|
||||
inner: wc::BullishPercentIndex,
|
||||
}
|
||||
|
||||
impl Default for WasmBullishPercentIndex {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_class = BullishPercentIndex)]
|
||||
impl WasmBullishPercentIndex {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new() -> WasmBullishPercentIndex {
|
||||
Self {
|
||||
inner: wc::BullishPercentIndex::new(),
|
||||
}
|
||||
}
|
||||
pub fn update(
|
||||
&mut self,
|
||||
change: Vec<f64>,
|
||||
volume: Vec<f64>,
|
||||
new_high: Vec<f64>,
|
||||
new_low: Vec<f64>,
|
||||
on_buy_signal: Vec<f64>,
|
||||
) -> Result<Option<f64>, JsError> {
|
||||
Ok(self.inner.update(build_cross_section_buy(
|
||||
&change,
|
||||
&volume,
|
||||
&new_high,
|
||||
&new_low,
|
||||
&on_buy_signal,
|
||||
)?))
|
||||
}
|
||||
pub fn reset(&mut self) {
|
||||
self.inner.reset();
|
||||
}
|
||||
#[wasm_bindgen(js_name = isReady)]
|
||||
pub fn is_ready(&self) -> bool {
|
||||
self.inner.is_ready()
|
||||
}
|
||||
#[wasm_bindgen(js_name = warmupPeriod)]
|
||||
pub fn warmup_period(&self) -> usize {
|
||||
self.inner.warmup_period()
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = CumulativeVolumeIndex)]
|
||||
pub struct WasmCumulativeVolumeIndex {
|
||||
inner: wc::CumulativeVolumeIndex,
|
||||
}
|
||||
|
||||
impl Default for WasmCumulativeVolumeIndex {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_class = CumulativeVolumeIndex)]
|
||||
impl WasmCumulativeVolumeIndex {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new() -> WasmCumulativeVolumeIndex {
|
||||
Self {
|
||||
inner: wc::CumulativeVolumeIndex::new(),
|
||||
}
|
||||
}
|
||||
pub fn update(
|
||||
&mut self,
|
||||
change: Vec<f64>,
|
||||
volume: Vec<f64>,
|
||||
new_high: Vec<f64>,
|
||||
new_low: Vec<f64>,
|
||||
) -> Result<Option<f64>, JsError> {
|
||||
Ok(self
|
||||
.inner
|
||||
.update(build_cross_section(&change, &volume, &new_high, &new_low)?))
|
||||
}
|
||||
pub fn reset(&mut self) {
|
||||
self.inner.reset();
|
||||
}
|
||||
#[wasm_bindgen(js_name = isReady)]
|
||||
pub fn is_ready(&self) -> bool {
|
||||
self.inner.is_ready()
|
||||
}
|
||||
#[wasm_bindgen(js_name = warmupPeriod)]
|
||||
pub fn warmup_period(&self) -> usize {
|
||||
self.inner.warmup_period()
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = AbsoluteBreadthIndex)]
|
||||
pub struct WasmAbsoluteBreadthIndex {
|
||||
inner: wc::AbsoluteBreadthIndex,
|
||||
}
|
||||
|
||||
impl Default for WasmAbsoluteBreadthIndex {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_class = AbsoluteBreadthIndex)]
|
||||
impl WasmAbsoluteBreadthIndex {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new() -> WasmAbsoluteBreadthIndex {
|
||||
Self {
|
||||
inner: wc::AbsoluteBreadthIndex::new(),
|
||||
}
|
||||
}
|
||||
pub fn update(
|
||||
&mut self,
|
||||
change: Vec<f64>,
|
||||
volume: Vec<f64>,
|
||||
new_high: Vec<f64>,
|
||||
new_low: Vec<f64>,
|
||||
) -> Result<Option<f64>, JsError> {
|
||||
Ok(self
|
||||
.inner
|
||||
.update(build_cross_section(&change, &volume, &new_high, &new_low)?))
|
||||
}
|
||||
pub fn reset(&mut self) {
|
||||
self.inner.reset();
|
||||
}
|
||||
#[wasm_bindgen(js_name = isReady)]
|
||||
pub fn is_ready(&self) -> bool {
|
||||
self.inner.is_ready()
|
||||
}
|
||||
#[wasm_bindgen(js_name = warmupPeriod)]
|
||||
pub fn warmup_period(&self) -> usize {
|
||||
self.inner.warmup_period()
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = TickIndex)]
|
||||
pub struct WasmTickIndex {
|
||||
inner: wc::TickIndex,
|
||||
}
|
||||
|
||||
impl Default for WasmTickIndex {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_class = TickIndex)]
|
||||
impl WasmTickIndex {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new() -> WasmTickIndex {
|
||||
Self {
|
||||
inner: wc::TickIndex::new(),
|
||||
}
|
||||
}
|
||||
pub fn update(
|
||||
&mut self,
|
||||
change: Vec<f64>,
|
||||
volume: Vec<f64>,
|
||||
new_high: Vec<f64>,
|
||||
new_low: Vec<f64>,
|
||||
) -> Result<Option<f64>, JsError> {
|
||||
Ok(self
|
||||
.inner
|
||||
.update(build_cross_section(&change, &volume, &new_high, &new_low)?))
|
||||
}
|
||||
pub fn reset(&mut self) {
|
||||
self.inner.reset();
|
||||
}
|
||||
#[wasm_bindgen(js_name = isReady)]
|
||||
pub fn is_ready(&self) -> bool {
|
||||
self.inner.is_ready()
|
||||
}
|
||||
#[wasm_bindgen(js_name = warmupPeriod)]
|
||||
pub fn warmup_period(&self) -> usize {
|
||||
self.inner.warmup_period()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
Reference in New Issue
Block a user