feat(mcginley): add McGinley Dynamic moving average
John McGinley's self-adjusting moving average with the recurrence MD + (price - MD) / (0.6 * period * (price / MD)^4). Speeds up when price falls below the indicator and damps when price runs above the indicator. Seeded with the simple average of the first period inputs. Reference: McGinley, Technical Analysis of Stocks & Commodities, 1990. Touchpoints: - crates/wickra-core: mcginley_dynamic.rs + mod.rs + lib.rs re-export - bindings/python: PyMcGinleyDynamic + __init__.py + test_new_indicators + test_known_values reference - bindings/node: McGinleyDynamicNode (scalar macro) + index.d.ts/index.js + indicators.test.js factory + reference value - bindings/wasm: wasm_scalar_indicator! macro - fuzz: indicator_update target covers McGinleyDynamic(10) - crates/wickra/benches: bench_scalar entry - README + CHANGELOG: Moving Averages row + Unreleased entry
This commit is contained in:
@@ -38,6 +38,7 @@ const scalarFactories = {
|
||||
TRIX: () => new wickra.TRIX(9),
|
||||
KAMA: () => new wickra.KAMA(10, 2, 30),
|
||||
ALMA: () => new wickra.ALMA(9, 0.85, 6.0),
|
||||
McGinleyDynamic: () => new wickra.McGinleyDynamic(10),
|
||||
SMMA: () => new wickra.SMMA(14),
|
||||
TRIMA: () => new wickra.TRIMA(20),
|
||||
ZLEMA: () => new wickra.ZLEMA(14),
|
||||
@@ -260,6 +261,15 @@ test('LinRegAngle of a unit-slope series is 45 degrees', () => {
|
||||
assert.ok(Math.abs(out[4] - 45) < 1e-9);
|
||||
});
|
||||
|
||||
test('McGinleyDynamic(3) seeds with SMA and recurses on the next price', () => {
|
||||
// Seed = SMA([10, 20, 30]) = 20. On 40: ratio = 2, divisor = 0.6*3*16 = 28.8.
|
||||
const out = new wickra.McGinleyDynamic(3).batch([10, 20, 30, 40]);
|
||||
assert.ok(Number.isNaN(out[0]) && Number.isNaN(out[1]));
|
||||
assert.ok(Math.abs(out[2] - 20) < 1e-12);
|
||||
const expected = 20 + 20 / (0.6 * 3 * 16);
|
||||
assert.ok(Math.abs(out[3] - expected) < 1e-12);
|
||||
});
|
||||
|
||||
test('ALMA(3, 0.85, 6) reference value on [10, 20, 30]', () => {
|
||||
// m = 0.85 * 2 = 1.7; s = 3 / 6 = 0.5; 2*s^2 = 0.5.
|
||||
const out = new wickra.ALMA(3, 0.85, 6).batch([10, 20, 30]);
|
||||
|
||||
@@ -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, MACD, BollingerBands, ATR, Stochastic, OBV, ADX, CCI, WilliamsR, MFI, PSAR, Keltner, Donchian, VWAP, RollingVWAP, AwesomeOscillator, Aroon, KAMA, ALMA, T3, TSI, PMO, ADL, VolumePriceTrend, ChaikinMoneyFlow, ChaikinOscillator, ForceIndex, EaseOfMovement, SuperTrend, ChandelierExit, ChandeKrollStop, AtrTrailingStop, TypicalPrice, MedianPrice, WeightedClose, LinearRegression, LinRegSlope, AcceleratorOscillator, BalanceOfPower, ChoppinessIndex, TrueRange, ChaikinVolatility, LinRegAngle, BollingerBandwidth, PercentB, NATR, HistoricalVolatility, AroonOscillator, Vortex, MassIndex, StochRSI, UltimateOscillator, PPO, Coppock, VWMA } = nativeBinding
|
||||
const { version, SMA, EMA, WMA, RSI, DEMA, TEMA, HMA, ROC, TRIX, SMMA, TRIMA, ZLEMA, MOM, CMO, DPO, StdDev, UlcerIndex, VerticalHorizontalFilter, ZScore, MACD, BollingerBands, ATR, Stochastic, OBV, ADX, CCI, WilliamsR, MFI, PSAR, Keltner, Donchian, VWAP, RollingVWAP, AwesomeOscillator, Aroon, KAMA, ALMA, McGinleyDynamic, T3, TSI, PMO, ADL, VolumePriceTrend, ChaikinMoneyFlow, ChaikinOscillator, ForceIndex, EaseOfMovement, SuperTrend, ChandelierExit, ChandeKrollStop, AtrTrailingStop, TypicalPrice, MedianPrice, WeightedClose, LinearRegression, LinRegSlope, AcceleratorOscillator, BalanceOfPower, ChoppinessIndex, TrueRange, ChaikinVolatility, LinRegAngle, BollingerBandwidth, PercentB, NATR, HistoricalVolatility, AroonOscillator, Vortex, MassIndex, StochRSI, UltimateOscillator, PPO, Coppock, VWMA } = nativeBinding
|
||||
|
||||
module.exports.version = version
|
||||
module.exports.SMA = SMA
|
||||
@@ -350,6 +350,7 @@ module.exports.AwesomeOscillator = AwesomeOscillator
|
||||
module.exports.Aroon = Aroon
|
||||
module.exports.KAMA = KAMA
|
||||
module.exports.ALMA = ALMA
|
||||
module.exports.McGinleyDynamic = McGinleyDynamic
|
||||
module.exports.T3 = T3
|
||||
module.exports.TSI = TSI
|
||||
module.exports.PMO = PMO
|
||||
|
||||
@@ -116,6 +116,7 @@ node_scalar_indicator!(
|
||||
wc::VerticalHorizontalFilter
|
||||
);
|
||||
node_scalar_indicator!(ZScoreNode, "ZScore", wc::ZScore);
|
||||
node_scalar_indicator!(McGinleyDynamicNode, "McGinleyDynamic", wc::McGinleyDynamic);
|
||||
|
||||
// ============================== MACD ==============================
|
||||
|
||||
|
||||
Reference in New Issue
Block a user