@vulcan-js/indicators 0.0.0 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -56,6 +56,7 @@ process(98)
56
56
  | Moving Max | `mmax` | `movingMax` |
57
57
  | Moving Min | `mmin` | `movingMin` |
58
58
  | Moving Sum | `msum` | — |
59
+ | Parabolic SAR | `psar` | `parabolicSar` |
59
60
  | Rolling Moving Average | `rma` | `rollingMovingAverage` |
60
61
  | Simple Moving Average | `sma` | `simpleMovingAverage` |
61
62
  | Triangular Moving Average | `trima` | `triangularMovingAverage` |
package/dist/index.d.ts CHANGED
@@ -415,6 +415,51 @@ declare const defaultMovingSumOptions: MovingSumOptions;
415
415
  */
416
416
  declare const msum: _vulcan_js_core0.SignalGenerator<Numberish, Dnum, MovingSumOptions>;
417
417
  //#endregion
418
+ //#region src/trend/parabolicSar.d.ts
419
+ interface ParabolicSarOptions {
420
+ start: number;
421
+ increment: number;
422
+ max: number;
423
+ }
424
+ declare const defaultParabolicSarOptions: ParabolicSarOptions;
425
+ interface PSARPoint {
426
+ psar: Dnum;
427
+ isUptrend: boolean;
428
+ }
429
+ /**
430
+ * Parabolic SAR (Stop and Reverse)
431
+ *
432
+ * Developed by J. Welles Wilder Jr. in 1978, the Parabolic SAR is a
433
+ * trend-following indicator that provides potential entry and exit points.
434
+ * It appears as a series of dots above or below the price, indicating
435
+ * the current trend direction and potential reversal points.
436
+ *
437
+ * Formula:
438
+ * SAR_new = SAR_prev + AF * (EP - SAR_prev)
439
+ *
440
+ * Where:
441
+ * AF = Acceleration Factor (starts at `start`, increments by `increment`
442
+ * on each new EP, capped at `max`)
443
+ * EP = Extreme Point (highest high in uptrend, lowest low in downtrend)
444
+ *
445
+ * The SAR is clamped to not exceed the two prior bars' price range.
446
+ * A reversal occurs when price penetrates the SAR level.
447
+ *
448
+ * @param source - Iterable of candle data with high and low prices
449
+ * @param options - Configuration options
450
+ * @param options.start - Initial acceleration factor (default: 0.02)
451
+ * @param options.increment - AF increment per new extreme (default: 0.02)
452
+ * @param options.max - Maximum acceleration factor (default: 0.2)
453
+ * @returns Generator yielding PSARPoint objects with `psar` value and `isUptrend` flag
454
+ */
455
+ declare const psar: _vulcan_js_core0.SignalGenerator<{
456
+ h: dnum.Numberish;
457
+ l: dnum.Numberish;
458
+ }, {
459
+ psar: Dnum;
460
+ isUptrend: boolean;
461
+ }, ParabolicSarOptions>;
462
+ //#endregion
418
463
  //#region src/trend/rollingMovingAverage.d.ts
419
464
  interface RMAOptions {
420
465
  /**
@@ -481,5 +526,5 @@ declare const ad: _vulcan_js_core0.SignalGenerator<{
481
526
  v: dnum.Numberish;
482
527
  }, Dnum, Record<string, any>>;
483
528
  //#endregion
484
- export { AbsolutePriceOscillatorOptions, AroonOptions, AroonPoint, AwesomeOscillatorOptions, ChaikinOscillatorOptions, ChandeForecastOscillatorOptions, CommodityChannelIndexOptions, DoubleExponentialMovingAverageOptions, ExponentialMovingAverageOptions, IchimokuCloudOptions, IchimokuCloudPoint, MACDOptions, MACDPoint, MassIndexOptions, MovingMaxOptions, MovingMinOptions, MovingSumOptions, PercentagePriceOscillatorOptions, PercentagePriceOscillatorPoint, RMAOptions, RSIOptions, SimpleMovingAverageOptions, StochPoint, StochasticOscillatorOptions, TriangularMovingAverageOptions, apo as absolutePriceOscillator, apo, ad as accumulationDistribution, ad, ao, ao as awesomeOscillator, aroon, bop as balanceOfPower, bop, cci, cci as commodityChannelIndex, cfo, cfo as chandeForecastOscillator, cmo as chaikinOscillator, cmo, defaultAbsolutePriceOscillatorOptions, defaultAroonOptions, defaultAwesomeOscillatorOptions, defaultCCIOptions, defaultCFOOptions, defaultChaikinOscillatorOptions, defaultDoubleExponentialMovingAverageOptions, defaultExponentialMovingAverageOptions, defaultIchimokuCloudOptions, defaultMACDOptions, defaultMassIndexOptions, defaultMovingMaxOptions, defaultMovingMinOptions, defaultMovingSumOptions, defaultPercentagePriceOscillatorOptions, defaultRMAOptions, defaultRSIOptions, defaultSMAOptions, defaultStochasticOscillatorOptions, defaultTriangularMovingAverageOptions, dema, dema as doubleExponentialMovingAverage, ema, ema as exponentialMovingAverage, ichimokuCloud, macd, macd as movingAverageConvergenceDivergence, mi as massIndex, mi, mmax, mmax as movingMax, mmin, mmin as movingMin, msum, ppo as percentagePriceOscillator, ppo, rsi as relativeStrengthIndex, rsi, rma, rma as rollingMovingAverage, sma as simpleMovingAverage, sma, stoch, stoch as stochasticOscillator, trima as triangularMovingAverage, trima };
529
+ export { AbsolutePriceOscillatorOptions, AroonOptions, AroonPoint, AwesomeOscillatorOptions, ChaikinOscillatorOptions, ChandeForecastOscillatorOptions, CommodityChannelIndexOptions, DoubleExponentialMovingAverageOptions, ExponentialMovingAverageOptions, IchimokuCloudOptions, IchimokuCloudPoint, MACDOptions, MACDPoint, MassIndexOptions, MovingMaxOptions, MovingMinOptions, MovingSumOptions, PSARPoint, ParabolicSarOptions, PercentagePriceOscillatorOptions, PercentagePriceOscillatorPoint, RMAOptions, RSIOptions, SimpleMovingAverageOptions, StochPoint, StochasticOscillatorOptions, TriangularMovingAverageOptions, apo as absolutePriceOscillator, apo, ad as accumulationDistribution, ad, ao, ao as awesomeOscillator, aroon, bop as balanceOfPower, bop, cci, cci as commodityChannelIndex, cfo, cfo as chandeForecastOscillator, cmo as chaikinOscillator, cmo, defaultAbsolutePriceOscillatorOptions, defaultAroonOptions, defaultAwesomeOscillatorOptions, defaultCCIOptions, defaultCFOOptions, defaultChaikinOscillatorOptions, defaultDoubleExponentialMovingAverageOptions, defaultExponentialMovingAverageOptions, defaultIchimokuCloudOptions, defaultMACDOptions, defaultMassIndexOptions, defaultMovingMaxOptions, defaultMovingMinOptions, defaultMovingSumOptions, defaultParabolicSarOptions, defaultPercentagePriceOscillatorOptions, defaultRMAOptions, defaultRSIOptions, defaultSMAOptions, defaultStochasticOscillatorOptions, defaultTriangularMovingAverageOptions, dema, dema as doubleExponentialMovingAverage, ema, ema as exponentialMovingAverage, ichimokuCloud, macd, macd as movingAverageConvergenceDivergence, mi as massIndex, mi, mmax, mmax as movingMax, mmin, mmin as movingMin, msum, psar as parabolicSar, psar, ppo as percentagePriceOscillator, ppo, rsi as relativeStrengthIndex, rsi, rma, rma as rollingMovingAverage, sma as simpleMovingAverage, sma, stoch, stoch as stochasticOscillator, trima as triangularMovingAverage, trima };
485
530
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -657,6 +657,138 @@ const mi = createSignal(({ emaPeriod, miPeriod }) => {
657
657
  };
658
658
  }, defaultMassIndexOptions);
659
659
 
660
+ //#endregion
661
+ //#region src/trend/parabolicSar.ts
662
+ const defaultParabolicSarOptions = {
663
+ start: .02,
664
+ increment: .02,
665
+ max: .2
666
+ };
667
+ /**
668
+ * Parabolic SAR (Stop and Reverse)
669
+ *
670
+ * Developed by J. Welles Wilder Jr. in 1978, the Parabolic SAR is a
671
+ * trend-following indicator that provides potential entry and exit points.
672
+ * It appears as a series of dots above or below the price, indicating
673
+ * the current trend direction and potential reversal points.
674
+ *
675
+ * Formula:
676
+ * SAR_new = SAR_prev + AF * (EP - SAR_prev)
677
+ *
678
+ * Where:
679
+ * AF = Acceleration Factor (starts at `start`, increments by `increment`
680
+ * on each new EP, capped at `max`)
681
+ * EP = Extreme Point (highest high in uptrend, lowest low in downtrend)
682
+ *
683
+ * The SAR is clamped to not exceed the two prior bars' price range.
684
+ * A reversal occurs when price penetrates the SAR level.
685
+ *
686
+ * @param source - Iterable of candle data with high and low prices
687
+ * @param options - Configuration options
688
+ * @param options.start - Initial acceleration factor (default: 0.02)
689
+ * @param options.increment - AF increment per new extreme (default: 0.02)
690
+ * @param options.max - Maximum acceleration factor (default: 0.2)
691
+ * @returns Generator yielding PSARPoint objects with `psar` value and `isUptrend` flag
692
+ */
693
+ const psar = createSignal(({ start, increment, max }) => {
694
+ assert(start > 0, /* @__PURE__ */ new RangeError(`Expected start to be positive, got ${start}`));
695
+ assert(increment > 0, /* @__PURE__ */ new RangeError(`Expected increment to be positive, got ${increment}`));
696
+ assert(max > 0 && max >= start, /* @__PURE__ */ new RangeError(`Expected max to be positive and >= start, got ${max}`));
697
+ let count = 0;
698
+ let isUptrend = true;
699
+ let sar;
700
+ let ep;
701
+ let af;
702
+ let prevHigh;
703
+ let prevLow;
704
+ let prevPrevHigh;
705
+ let prevPrevLow;
706
+ const afStart = from(start, 18);
707
+ const afIncrement = from(increment, 18);
708
+ const afMax = from(max, 18);
709
+ return (bar) => {
710
+ const h = from(bar.h, 18);
711
+ const l = from(bar.l, 18);
712
+ count++;
713
+ if (count === 1) {
714
+ isUptrend = true;
715
+ sar = l;
716
+ ep = h;
717
+ af = afStart;
718
+ prevHigh = h;
719
+ prevLow = l;
720
+ prevPrevHigh = h;
721
+ prevPrevLow = l;
722
+ return {
723
+ psar: sar,
724
+ isUptrend
725
+ };
726
+ }
727
+ if (count === 2) {
728
+ if (gt(h, prevHigh)) {
729
+ isUptrend = true;
730
+ sar = prevLow;
731
+ ep = h;
732
+ } else {
733
+ isUptrend = false;
734
+ sar = prevHigh;
735
+ ep = l;
736
+ }
737
+ af = afStart;
738
+ prevPrevHigh = prevHigh;
739
+ prevPrevLow = prevLow;
740
+ prevHigh = h;
741
+ prevLow = l;
742
+ return {
743
+ psar: sar,
744
+ isUptrend
745
+ };
746
+ }
747
+ let nextSar = add(sar, mul(af, sub(ep, sar), 18));
748
+ if (isUptrend) {
749
+ if (gt(nextSar, prevLow)) nextSar = prevLow;
750
+ if (gt(nextSar, prevPrevLow)) nextSar = prevPrevLow;
751
+ } else {
752
+ if (lt(nextSar, prevHigh)) nextSar = prevHigh;
753
+ if (lt(nextSar, prevPrevHigh)) nextSar = prevPrevHigh;
754
+ }
755
+ sar = nextSar;
756
+ let reversed = false;
757
+ if (isUptrend && lt(l, sar)) {
758
+ isUptrend = false;
759
+ sar = ep;
760
+ ep = l;
761
+ af = afStart;
762
+ reversed = true;
763
+ } else if (!isUptrend && gt(h, sar)) {
764
+ isUptrend = true;
765
+ sar = ep;
766
+ ep = h;
767
+ af = afStart;
768
+ reversed = true;
769
+ }
770
+ if (!reversed) {
771
+ if (isUptrend && gt(h, ep)) {
772
+ ep = h;
773
+ const newAf = add(af, afIncrement);
774
+ af = gt(newAf, afMax) ? afMax : newAf;
775
+ } else if (!isUptrend && lt(l, ep)) {
776
+ ep = l;
777
+ const newAf = add(af, afIncrement);
778
+ af = gt(newAf, afMax) ? afMax : newAf;
779
+ }
780
+ }
781
+ prevPrevHigh = prevHigh;
782
+ prevPrevLow = prevLow;
783
+ prevHigh = h;
784
+ prevLow = l;
785
+ return {
786
+ psar: sar,
787
+ isUptrend
788
+ };
789
+ };
790
+ }, defaultParabolicSarOptions);
791
+
660
792
  //#endregion
661
793
  //#region src/trend/triangularMovingAverage.ts
662
794
  const defaultTriangularMovingAverageOptions = { period: 4 };
@@ -679,5 +811,5 @@ const trima = createSignal(({ period }) => {
679
811
  }, defaultTriangularMovingAverageOptions);
680
812
 
681
813
  //#endregion
682
- export { apo as absolutePriceOscillator, apo, ad as accumulationDistribution, ad, ao, ao as awesomeOscillator, aroon, bop as balanceOfPower, bop, cci, cci as commodityChannelIndex, cfo, cfo as chandeForecastOscillator, cmo as chaikinOscillator, cmo, defaultAbsolutePriceOscillatorOptions, defaultAroonOptions, defaultAwesomeOscillatorOptions, defaultCCIOptions, defaultCFOOptions, defaultChaikinOscillatorOptions, defaultDoubleExponentialMovingAverageOptions, defaultExponentialMovingAverageOptions, defaultIchimokuCloudOptions, defaultMACDOptions, defaultMassIndexOptions, defaultMovingMaxOptions, defaultMovingMinOptions, defaultMovingSumOptions, defaultPercentagePriceOscillatorOptions, defaultRMAOptions, defaultRSIOptions, defaultSMAOptions, defaultStochasticOscillatorOptions, defaultTriangularMovingAverageOptions, dema, dema as doubleExponentialMovingAverage, ema, ema as exponentialMovingAverage, ichimokuCloud, macd, macd as movingAverageConvergenceDivergence, mi as massIndex, mi, mmax, mmax as movingMax, mmin, mmin as movingMin, msum, ppo as percentagePriceOscillator, ppo, rsi as relativeStrengthIndex, rsi, rma, rma as rollingMovingAverage, sma as simpleMovingAverage, sma, stoch, stoch as stochasticOscillator, trima as triangularMovingAverage, trima };
814
+ export { apo as absolutePriceOscillator, apo, ad as accumulationDistribution, ad, ao, ao as awesomeOscillator, aroon, bop as balanceOfPower, bop, cci, cci as commodityChannelIndex, cfo, cfo as chandeForecastOscillator, cmo as chaikinOscillator, cmo, defaultAbsolutePriceOscillatorOptions, defaultAroonOptions, defaultAwesomeOscillatorOptions, defaultCCIOptions, defaultCFOOptions, defaultChaikinOscillatorOptions, defaultDoubleExponentialMovingAverageOptions, defaultExponentialMovingAverageOptions, defaultIchimokuCloudOptions, defaultMACDOptions, defaultMassIndexOptions, defaultMovingMaxOptions, defaultMovingMinOptions, defaultMovingSumOptions, defaultParabolicSarOptions, defaultPercentagePriceOscillatorOptions, defaultRMAOptions, defaultRSIOptions, defaultSMAOptions, defaultStochasticOscillatorOptions, defaultTriangularMovingAverageOptions, dema, dema as doubleExponentialMovingAverage, ema, ema as exponentialMovingAverage, ichimokuCloud, macd, macd as movingAverageConvergenceDivergence, mi as massIndex, mi, mmax, mmax as movingMax, mmin, mmin as movingMin, msum, psar as parabolicSar, psar, ppo as percentagePriceOscillator, ppo, rsi as relativeStrengthIndex, rsi, rma, rma as rollingMovingAverage, sma as simpleMovingAverage, sma, stoch, stoch as stochasticOscillator, trima as triangularMovingAverage, trima };
683
815
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/trend/exponentialMovingAverage.ts","../src/momentum/absolutePriceOscillator.ts","../src/trend/simpleMovingAverage.ts","../src/momentum/awesomeOscillator.ts","../src/volume/accumulationDistribution.ts","../src/momentum/chaikinOscillator.ts","../src/trend/movingMax.ts","../src/trend/movingMin.ts","../src/momentum/ichimokuCloud.ts","../src/momentum/percentagePriceOscillator.ts","../src/trend/rollingMovingAverage.ts","../src/momentum/relativeStrengthIndex.ts","../src/momentum/stochasticOscillator.ts","../src/trend/aroon.ts","../src/trend/balanceOfPower.ts","../src/trend/chandeForecastOscillator.ts","../src/trend/commodityChannelIndex.ts","../src/trend/doubleExponentialMovingAverage.ts","../src/trend/macd.ts","../src/trend/movingSum.ts","../src/trend/massIndex.ts","../src/trend/triangularMovingAverage.ts"],"sourcesContent":["import type { Dnum, Numberish } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { add, divide, from, mul, subtract } from 'dnum'\n\nexport interface ExponentialMovingAverageOptions {\n period: number\n}\n\nexport const defaultExponentialMovingAverageOptions: ExponentialMovingAverageOptions = {\n period: 12,\n}\n\n/**\n * Exponential Moving Average (EMA)\n *\n * EMA = Price * k + PrevEMA * (1 - k)\n * Where k = 2 / (period + 1)\n */\nexport const ema = createSignal(\n ({ period }) => {\n assert(Number.isInteger(period) && period >= 1, new RangeError(`Expected period to be a positive integer, got ${period}`))\n const k = divide(from(2, 18), from(1 + period, 18), 18)\n const m = subtract(from(1, 18), k)\n let prev: Dnum | undefined\n return (value: Numberish) => {\n if (prev === undefined) {\n prev = from(value, 18)\n return prev\n }\n prev = add(\n mul(value, k, 18),\n mul(prev, m, 18),\n )\n return prev\n }\n },\n defaultExponentialMovingAverageOptions,\n)\n\nexport { ema as exponentialMovingAverage }\n","import type { Numberish } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { sub } from 'dnum'\nimport { ema } from '../trend/exponentialMovingAverage'\n\nexport interface AbsolutePriceOscillatorOptions {\n fastPeriod: number\n slowPeriod: number\n}\n\nexport const defaultAbsolutePriceOscillatorOptions: AbsolutePriceOscillatorOptions = {\n fastPeriod: 12,\n slowPeriod: 26,\n}\n\nexport const apo = createSignal(\n ({ fastPeriod, slowPeriod }) => {\n assert(Number.isInteger(fastPeriod) && fastPeriod >= 1, new RangeError(`Expected fastPeriod to be a positive integer, got ${fastPeriod}`))\n assert(Number.isInteger(slowPeriod) && slowPeriod >= 1, new RangeError(`Expected slowPeriod to be a positive integer, got ${slowPeriod}`))\n const fastProc = ema.create({ period: fastPeriod })\n const slowProc = ema.create({ period: slowPeriod })\n return (value: Numberish) => sub(fastProc(value), slowProc(value))\n },\n defaultAbsolutePriceOscillatorOptions,\n)\n\nexport { apo as absolutePriceOscillator }\n","import type { Dnum, Numberish } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { add, div, from, subtract } from 'dnum'\n\nexport interface SimpleMovingAverageOptions {\n /**\n * The period for calculating the moving average\n * @default 2\n */\n period: number\n}\n\nexport const defaultSMAOptions: SimpleMovingAverageOptions = {\n period: 2,\n}\n\n/**\n * Simple Moving Average (SMA)\n *\n * Calculates the arithmetic mean of a set of values over a specified period.\n * The SMA is calculated by summing all values in the period and dividing by the period length.\n *\n * Formula: SMA = (P1 + P2 + ... + Pn) / n\n * Where: P = Price values, n = Period\n *\n * @param source - Iterable of price values\n * @param options - Configuration options\n * @param options.period - The period for calculating the moving average (default: 2)\n * @returns Generator yielding SMA values\n */\nexport const sma = createSignal(\n ({ period }) => {\n assert(Number.isInteger(period) && period >= 1, new RangeError(`Expected period to be a positive integer, got ${period}`))\n const buffer: Dnum[] = Array.from({ length: period })\n let head = 0\n let count = 0\n let runningSum: Dnum = from(0, 18)\n\n return (value: Numberish) => {\n const v = from(value, 18)\n if (count < period) {\n buffer[count] = v\n runningSum = add(runningSum, v)\n count++\n }\n else {\n runningSum = subtract(runningSum, buffer[head])\n runningSum = add(runningSum, v)\n buffer[head] = v\n head = (head + 1) % period\n }\n return div(runningSum, count, 18)\n }\n },\n defaultSMAOptions,\n)\n\nexport { sma as simpleMovingAverage }\n","import type { CandleData, RequiredProperties } from '@vulcan-js/core'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { add, div, from, sub } from 'dnum'\nimport { sma } from '../trend/simpleMovingAverage'\n\nexport interface AwesomeOscillatorOptions {\n fastPeriod: number\n slowPeriod: number\n}\n\nexport const defaultAwesomeOscillatorOptions: AwesomeOscillatorOptions = {\n fastPeriod: 5,\n slowPeriod: 34,\n}\n\n/**\n * Awesome Oscillator (AO)\n *\n * AO = SMA(median, fastPeriod) - SMA(median, slowPeriod)\n * Where median = (high + low) / 2\n */\nexport const ao = createSignal(\n ({ fastPeriod, slowPeriod }) => {\n assert(Number.isInteger(fastPeriod) && fastPeriod >= 1, new RangeError(`Expected fastPeriod to be a positive integer, got ${fastPeriod}`))\n assert(Number.isInteger(slowPeriod) && slowPeriod >= 1, new RangeError(`Expected slowPeriod to be a positive integer, got ${slowPeriod}`))\n const fastProc = sma.create({ period: fastPeriod })\n const slowProc = sma.create({ period: slowPeriod })\n return (bar: RequiredProperties<CandleData, 'h' | 'l'>) => {\n const median = div(add(from(bar.h, 18), from(bar.l, 18)), 2, 18)\n return sub(fastProc(median), slowProc(median))\n }\n },\n defaultAwesomeOscillatorOptions,\n)\n\nexport { ao as awesomeOscillator }\n","import type { CandleData, RequiredProperties } from '@vulcan-js/core'\nimport type { Dnum } from 'dnum'\nimport { createSignal } from '@vulcan-js/core'\nimport { add, divide, equal, from, multiply, subtract } from 'dnum'\n\n/**\n * Accumulation/Distribution Indicator (A/D). Cumulative indicator\n * that uses volume and price to assess whether a stock is\n * being accumulated or distributed.\n *\n * MFM = ((Closing - Low) - (High - Closing)) / (High - Low)\n * MFV = MFM * Period Volume\n * AD = Previous AD + CMFV\n */\nexport const ad = createSignal(\n () => {\n let prevAD: Dnum = from(0, 18)\n return (bar: RequiredProperties<CandleData, 'h' | 'l' | 'c' | 'v'>) => {\n const h = from(bar.h, 18)\n const l = from(bar.l, 18)\n const c = from(bar.c, 18)\n const v = from(bar.v, 18)\n\n const range = subtract(h, l)\n // When high equals low, the range is zero and MFM is undefined; treat as 0\n const mfm = equal(range, 0)\n ? from(0, 18)\n : divide(\n subtract(subtract(c, l), subtract(h, c)),\n range,\n 18,\n )\n const mfv = multiply(mfm, v)\n prevAD = add(mfv, prevAD)\n return prevAD\n }\n },\n)\n\nexport { ad as accumulationDistribution }\n","import type { CandleData, RequiredProperties } from '@vulcan-js/core'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { sub } from 'dnum'\nimport { ema } from '../trend/exponentialMovingAverage'\nimport { ad } from '../volume/accumulationDistribution'\n\nexport interface ChaikinOscillatorOptions {\n fastPeriod: number\n slowPeriod: number\n}\n\nexport const defaultChaikinOscillatorOptions: ChaikinOscillatorOptions = {\n fastPeriod: 3,\n slowPeriod: 10,\n}\n\n/**\n * The ChaikinOscillator function measures the momentum of the\n * Accumulation/Distribution (A/D) using the Moving Average\n * Convergence Divergence (MACD) formula. It takes the\n * difference between fast and slow periods EMA of the A/D.\n * Cross above the A/D line indicates bullish.\n *\n * CO = Ema(fastPeriod, AD) - Ema(slowPeriod, AD)\n */\nexport const cmo = createSignal(\n ({ fastPeriod, slowPeriod }) => {\n assert(Number.isInteger(fastPeriod) && fastPeriod >= 1, new RangeError(`Expected fastPeriod to be a positive integer, got ${fastPeriod}`))\n assert(Number.isInteger(slowPeriod) && slowPeriod >= 1, new RangeError(`Expected slowPeriod to be a positive integer, got ${slowPeriod}`))\n const adProc = ad.create()\n const fastProc = ema.create({ period: fastPeriod })\n const slowProc = ema.create({ period: slowPeriod })\n return (bar: RequiredProperties<CandleData, 'h' | 'l' | 'c' | 'v'>) => {\n const adVal = adProc(bar)\n return sub(fastProc(adVal), slowProc(adVal))\n }\n },\n defaultChaikinOscillatorOptions,\n)\n\nexport { cmo as chaikinOscillator }\n","import type { Dnum, Numberish } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { from, gt } from 'dnum'\n\nexport interface MovingMaxOptions {\n /**\n * period\n */\n period: number\n}\n\nexport const defaultMovingMaxOptions: MovingMaxOptions = {\n period: 4,\n}\n\n/**\n * Moving Maximum (MovingMax)\n */\nexport const mmax = createSignal(\n ({ period }) => {\n assert(Number.isInteger(period) && period >= 1, new RangeError(`Expected period to be a positive integer, got ${period}`))\n const buffer: Dnum[] = []\n return (value: Numberish) => {\n buffer.push(from(value, 18))\n if (buffer.length > period)\n buffer.shift()\n return buffer.reduce((max, cur) => gt(max, cur) ? max : cur)\n }\n },\n defaultMovingMaxOptions,\n)\n\nexport { mmax as movingMax }\n","import type { Dnum, Numberish } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { from, lt } from 'dnum'\n\nexport interface MovingMinOptions {\n /**\n * period\n */\n period: number\n}\n\nexport const defaultMovingMinOptions: MovingMinOptions = {\n period: 4,\n}\n\n/**\n * Moving Minimum (MovingMin)\n */\nexport const mmin = createSignal(\n ({ period }) => {\n assert(Number.isInteger(period) && period >= 1, new RangeError(`Expected period to be a positive integer, got ${period}`))\n const buffer: Dnum[] = []\n return (value: Numberish) => {\n buffer.push(from(value, 18))\n if (buffer.length > period)\n buffer.shift()\n return buffer.reduce((min, cur) => lt(min, cur) ? min : cur)\n }\n },\n defaultMovingMinOptions,\n)\n\nexport { mmin as movingMin }\n","import type { CandleData, RequiredProperties } from '@vulcan-js/core'\nimport type { Dnum } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { add, div, from } from 'dnum'\nimport { mmax } from '../trend/movingMax'\nimport { mmin } from '../trend/movingMin'\n\nexport interface IchimokuCloudOptions {\n /** Conversion line period */\n conversionPeriod: number\n /** Base line period */\n basePeriod: number\n /** Leading span B period */\n leadingBPeriod: number\n}\n\nexport const defaultIchimokuCloudOptions: IchimokuCloudOptions = {\n conversionPeriod: 9,\n basePeriod: 26,\n leadingBPeriod: 52,\n}\n\nexport interface IchimokuCloudPoint {\n conversion: Dnum\n base: Dnum\n leadingA: Dnum\n leadingB: Dnum\n lagging: Dnum\n}\n\n/**\n * Ichimoku Cloud (Ichimoku Kinko Hyo)\n *\n * Computes raw values for each component. Displacement (shifting\n * Leading Spans forward and Lagging Span backward on the chart)\n * is a presentation concern left to the consumer.\n *\n * - Conversion (Tenkan-sen): (highest high + lowest low) / 2 over conversionPeriod\n * - Base (Kijun-sen): (highest high + lowest low) / 2 over basePeriod\n * - Leading Span A (Senkou A): (conversion + base) / 2\n * - Leading Span B (Senkou B): (highest high + lowest low) / 2 over leadingBPeriod\n * - Lagging (Chikou): current close price\n */\nexport const ichimokuCloud = createSignal(\n ({ conversionPeriod, basePeriod, leadingBPeriod }) => {\n assert(Number.isInteger(conversionPeriod) && conversionPeriod >= 1, new RangeError(`Expected conversionPeriod to be a positive integer, got ${conversionPeriod}`))\n assert(Number.isInteger(basePeriod) && basePeriod >= 1, new RangeError(`Expected basePeriod to be a positive integer, got ${basePeriod}`))\n assert(Number.isInteger(leadingBPeriod) && leadingBPeriod >= 1, new RangeError(`Expected leadingBPeriod to be a positive integer, got ${leadingBPeriod}`))\n const convHighProc = mmax.create({ period: conversionPeriod })\n const convLowProc = mmin.create({ period: conversionPeriod })\n const baseHighProc = mmax.create({ period: basePeriod })\n const baseLowProc = mmin.create({ period: basePeriod })\n const leadBHighProc = mmax.create({ period: leadingBPeriod })\n const leadBLowProc = mmin.create({ period: leadingBPeriod })\n\n return (bar: RequiredProperties<CandleData, 'h' | 'l' | 'c'>) => {\n const h = from(bar.h, 18)\n const l = from(bar.l, 18)\n\n const conversion = div(add(convHighProc(h), convLowProc(l)), 2, 18)\n const base = div(add(baseHighProc(h), baseLowProc(l)), 2, 18)\n const leadingA = div(add(conversion, base), 2, 18)\n const leadingB = div(add(leadBHighProc(h), leadBLowProc(l)), 2, 18)\n\n return { conversion, base, leadingA, leadingB, lagging: from(bar.c, 18) }\n }\n },\n defaultIchimokuCloudOptions,\n)\n","import type { Dnum, Numberish } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { div, eq, from, mul, sub } from 'dnum'\nimport { ema } from '../trend/exponentialMovingAverage'\n\nexport interface PercentagePriceOscillatorOptions {\n fastPeriod: number\n slowPeriod: number\n signalPeriod: number\n}\n\nexport const defaultPercentagePriceOscillatorOptions: PercentagePriceOscillatorOptions = {\n fastPeriod: 12,\n slowPeriod: 26,\n signalPeriod: 9,\n}\n\nexport interface PercentagePriceOscillatorPoint {\n ppo: Dnum\n signal: Dnum\n histogram: Dnum\n}\n\n/**\n * Percentage Price Oscillator (PPO)\n *\n * The Percentage Price Oscillator (PPO) is a momentum oscillator that measures the difference\n * between two moving averages as a percentage of the slower moving average. It consists of three components:\n * - PPO Line: ((Fast EMA - Slow EMA) / Slow EMA) * 100\n * - Signal Line: EMA of the PPO line\n * - Histogram: PPO - Signal\n *\n * Formula:\n * - PPO = ((EMA(fastPeriod, prices) - EMA(slowPeriod, prices)) / EMA(slowPeriod, prices)) * 100\n * - Signal = EMA(signalPeriod, PPO)\n * - Histogram = PPO - Signal\n *\n * @param source - Iterable of price values\n * @param options - Configuration options\n * @param options.fastPeriod - Period for the fast EMA (default: 12)\n * @param options.slowPeriod - Period for the slow EMA (default: 26)\n * @param options.signalPeriod - Period for the signal EMA (default: 9)\n * @returns Generator yielding PPO point objects\n */\nexport const ppo = createSignal(\n ({ fastPeriod, slowPeriod, signalPeriod }) => {\n assert(Number.isInteger(fastPeriod) && fastPeriod >= 1, new RangeError(`Expected fastPeriod to be a positive integer, got ${fastPeriod}`))\n assert(Number.isInteger(slowPeriod) && slowPeriod >= 1, new RangeError(`Expected slowPeriod to be a positive integer, got ${slowPeriod}`))\n assert(Number.isInteger(signalPeriod) && signalPeriod >= 1, new RangeError(`Expected signalPeriod to be a positive integer, got ${signalPeriod}`))\n const fastProc = ema.create({ period: fastPeriod })\n const slowProc = ema.create({ period: slowPeriod })\n const signalProc = ema.create({ period: signalPeriod })\n return (value: Numberish) => {\n const fast = fastProc(from(value, 18))\n const slow = slowProc(from(value, 18))\n const ppoVal = eq(slow, 0) ? from(0, 18) : mul(div(sub(fast, slow), slow, 18), 100, 18)\n const sig = signalProc(ppoVal)\n return { ppo: ppoVal, signal: sig, histogram: sub(ppoVal, sig) }\n }\n },\n defaultPercentagePriceOscillatorOptions,\n)\n\nexport { ppo as percentagePriceOscillator }\n","import type { Dnum, Numberish } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { add, div, from, mul } from 'dnum'\n\nexport interface RMAOptions {\n /**\n * period\n */\n period: number\n}\n\nexport const defaultRMAOptions: RMAOptions = {\n period: 4,\n}\n\n/**\n * Rolling moving average (RMA).\n *\n * R[0] to R[p-1] is SMA(values)\n *\n * R[p] and after is R[i] = ((R[i-1]*(p-1)) + v[i]) / p\n */\nexport const rma = createSignal(\n ({ period }) => {\n assert(Number.isInteger(period) && period >= 1, new RangeError(`Expected period to be a positive integer, got ${period}`))\n let count = 0\n let sum: Dnum = from(0, 18)\n let prev: Dnum = from(0, 18)\n\n return (value: Numberish) => {\n if (count < period) {\n sum = add(sum, value)\n count++\n prev = div(sum, count, 18)\n return prev\n }\n prev = div(\n add(mul(prev, period - 1, 18), value),\n period,\n 18,\n )\n return prev\n }\n },\n defaultRMAOptions,\n)\n\nexport { rma as rollingMovingAverage }\n","import type { Dnum, Numberish } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { add, div, eq, from, gt, mul, sub } from 'dnum'\nimport { rma } from '../trend/rollingMovingAverage'\n\nexport interface RSIOptions {\n period: number\n}\n\nexport const defaultRSIOptions: RSIOptions = {\n period: 14,\n}\n\n/**\n * Relative Strength Index (RSI). It is a momentum indicator that measures the magnitude of\n * recent price changes to evaluate overbought and oversold conditions\n * using the given window period.\n *\n * RS = Average Gain / Average Loss\n *\n * RSI = 100 - (100 / (1 + RS))\n */\nexport const rsi = createSignal(\n ({ period }) => {\n assert(Number.isInteger(period) && period >= 1, new RangeError(`Expected period to be a positive integer, got ${period}`))\n const gainProc = rma.create({ period })\n const lossProc = rma.create({ period })\n let prev: Dnum | undefined\n\n return (value: Numberish) => {\n const price = from(value, 18)\n\n if (prev === undefined) {\n prev = price\n gainProc(from(0, 18))\n lossProc(from(0, 18))\n return from(0, 18)\n }\n\n const change = sub(price, prev)\n prev = price\n\n const gain = gt(change, 0) ? change : from(0, 18)\n const loss = gt(change, 0) ? from(0, 18) : mul(change, -1, 18)\n\n const avgGain = gainProc(gain)\n const avgLoss = lossProc(loss)\n\n if (eq(avgLoss, 0)) {\n return from(100, 18)\n }\n\n const rs = div(avgGain, avgLoss, 18)\n return sub(100, div(100, add(1, rs), 18))\n }\n },\n defaultRSIOptions,\n)\n\nexport { rsi as relativeStrengthIndex }\n","import type { CandleData, RequiredProperties } from '@vulcan-js/core'\nimport type { Dnum } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { div, eq, from, mul, sub } from 'dnum'\nimport { mmax } from '../trend/movingMax'\nimport { mmin } from '../trend/movingMin'\nimport { sma } from '../trend/simpleMovingAverage'\n\nexport interface StochasticOscillatorOptions {\n /** The %k period */\n kPeriod: number\n /** The %k slowing period */\n slowingPeriod: number\n /** The %d period */\n dPeriod: number\n}\n\nexport const defaultStochasticOscillatorOptions: StochasticOscillatorOptions = {\n kPeriod: 14,\n slowingPeriod: 1,\n dPeriod: 3,\n}\n\nexport interface StochPoint {\n k: Dnum\n d: Dnum\n}\n\n/**\n * Stochastic Oscillator\n *\n * %K = ((Close - Lowest Low) / (Highest High - Lowest Low)) * 100\n * %D = SMA(%K, dPeriod)\n */\nexport const stoch = createSignal(\n ({ kPeriod, slowingPeriod, dPeriod }) => {\n assert(Number.isInteger(kPeriod) && kPeriod >= 1, new RangeError(`Expected kPeriod to be a positive integer, got ${kPeriod}`))\n assert(Number.isInteger(slowingPeriod) && slowingPeriod >= 1, new RangeError(`Expected slowingPeriod to be a positive integer, got ${slowingPeriod}`))\n assert(Number.isInteger(dPeriod) && dPeriod >= 1, new RangeError(`Expected dPeriod to be a positive integer, got ${dPeriod}`))\n const mmaxProc = mmax.create({ period: kPeriod })\n const mminProc = mmin.create({ period: kPeriod })\n const slowingProc = slowingPeriod > 1 ? sma.create({ period: slowingPeriod }) : null\n const dProc = sma.create({ period: dPeriod })\n return (bar: RequiredProperties<CandleData, 'h' | 'l' | 'c'>) => {\n const h = from(bar.h, 18)\n const l = from(bar.l, 18)\n const c = from(bar.c, 18)\n\n const highestHigh = mmaxProc(h)\n const lowestLow = mminProc(l)\n\n const range = sub(highestHigh, lowestLow, 18)\n const rawK = eq(range, 0) ? from(0, 18) : mul(div(sub(c, lowestLow, 18), range, 18), 100, 18)\n const k = slowingProc ? slowingProc(rawK) : rawK\n return { k, d: dProc(k) }\n }\n },\n defaultStochasticOscillatorOptions,\n)\n\nexport { stoch as stochasticOscillator }\n","import type { CandleData, RequiredProperties } from '@vulcan-js/core'\nimport type { Dnum } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { divide, from, gt, lt, multiply, subtract } from 'dnum'\n\nexport interface AroonOptions {\n period: number\n}\n\nexport const defaultAroonOptions: AroonOptions = {\n period: 25,\n}\n\nexport interface AroonPoint {\n up: Dnum\n down: Dnum\n oscillator: Dnum\n}\n\n/**\n * Aroon Indicator\n *\n * Aroon Up = ((period - days since highest high) / period) * 100\n * Aroon Down = ((period - days since lowest low) / period) * 100\n * Oscillator = Aroon Up - Aroon Down\n */\nexport const aroon = createSignal(\n ({ period }) => {\n assert(Number.isInteger(period) && period >= 1, new RangeError(`Expected period to be a positive integer, got ${period}`))\n const highBuffer: Dnum[] = []\n const lowBuffer: Dnum[] = []\n\n return (bar: RequiredProperties<CandleData, 'h' | 'l'>) => {\n const h = from(bar.h, 18)\n const l = from(bar.l, 18)\n\n highBuffer.push(h)\n lowBuffer.push(l)\n if (highBuffer.length > period + 1)\n highBuffer.shift()\n if (lowBuffer.length > period + 1)\n lowBuffer.shift()\n\n let highestIdx = 0\n let lowestIdx = 0\n for (let j = 1; j < highBuffer.length; j++) {\n if (!gt(highBuffer[highestIdx], highBuffer[j]))\n highestIdx = j\n if (!lt(lowBuffer[lowestIdx], lowBuffer[j]))\n lowestIdx = j\n }\n\n const daysSinceHigh = highBuffer.length - 1 - highestIdx\n const daysSinceLow = lowBuffer.length - 1 - lowestIdx\n\n const periodDnum = from(period, 18)\n const up = divide(multiply(from(period - daysSinceHigh, 18), 100, 18), periodDnum, 18)\n const down = divide(multiply(from(period - daysSinceLow, 18), 100, 18), periodDnum, 18)\n\n return {\n up,\n down,\n oscillator: subtract(up, down),\n }\n }\n },\n defaultAroonOptions,\n)\n","import type { CandleData, RequiredProperties } from '@vulcan-js/core'\nimport { createSignal } from '@vulcan-js/core'\nimport { divide, equal, from, subtract } from 'dnum'\n\nexport const bop = createSignal(\n () => {\n return (bar: RequiredProperties<CandleData, 'o' | 'h' | 'l' | 'c'>) => {\n const o = from(bar.o, 18)\n const h = from(bar.h, 18)\n const l = from(bar.l, 18)\n const c = from(bar.c, 18)\n const range = subtract(h, l)\n if (equal(range, 0)) {\n return from(0, 18)\n }\n return divide(subtract(c, o), range, 18)\n }\n },\n)\n\nexport { bop as balanceOfPower }\n","import type { Dnum, Numberish } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { add, divide, equal, from, multiply, subtract } from 'dnum'\n\nexport interface ChandeForecastOscillatorOptions {\n /**\n * The period for linear regression\n * @default 14\n */\n period: number\n}\n\nexport const defaultCFOOptions: ChandeForecastOscillatorOptions = {\n period: 14,\n}\n\n/**\n * Chande Forecast Oscillator (CFO)\n *\n * Measures the percentage difference between the actual close price and the\n * n-period linear regression forecast price. Positive values indicate bullish\n * momentum (price above forecast), negative values indicate bearish momentum.\n *\n * Formula: CFO = ((Close - Forecast) / Close) * 100\n * Where: Forecast = Linear regression value at current point\n *\n * @param source - Iterable of price values\n * @param options - Configuration options\n * @param options.period - The period for linear regression (default: 14)\n * @returns Generator yielding CFO values as percentages\n */\nexport const cfo = createSignal(\n ({ period }) => {\n assert(Number.isInteger(period) && period >= 1, new RangeError(`Expected period to be a positive integer, got ${period}`))\n const buffer: Dnum[] = []\n\n return (value: Numberish) => {\n buffer.push(from(value, 18))\n if (buffer.length > period)\n buffer.shift()\n\n const n = buffer.length\n if (n < 2) {\n return from(0, 18)\n }\n\n // Precompute X-related sums as plain integers\n const xSum = n * (n + 1) / 2\n const x2Sum = n * (n + 1) * (2 * n + 1) / 6\n const denom = n * x2Sum - xSum * xSum\n\n // Compute Y-dependent sums (keep all Dnum at 18 decimals)\n let sumY: Dnum = from(0, 18)\n let sumXY: Dnum = from(0, 18)\n for (let i = 0; i < n; i++) {\n sumY = add(sumY, buffer[i])\n sumXY = add(sumXY, multiply(buffer[i], i + 1))\n }\n\n // slope = (n * SUM(XY) - SUM(X) * SUM(Y)) / denom\n const num = subtract(multiply(sumXY, n), multiply(sumY, xSum))\n const slope = divide(num, denom, 18)\n\n // intercept = (SUM(Y) - slope * SUM(X)) / n\n const intercept = divide(subtract(sumY, multiply(slope, xSum)), n, 18)\n\n // forecast = slope * n + intercept\n const forecast = add(multiply(slope, n), intercept)\n\n const close = buffer[n - 1]\n if (equal(close, 0)) {\n return from(0, 18)\n }\n\n return divide(multiply(subtract(close, forecast), 100), close, 18)\n }\n },\n defaultCFOOptions,\n)\n\nexport { cfo as chandeForecastOscillator }\n","import type { CandleData, RequiredProperties } from '@vulcan-js/core'\nimport type { Dnum } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { abs, add, divide, equal, from, subtract } from 'dnum'\n\nexport interface CommodityChannelIndexOptions {\n /**\n * The period for CCI calculation\n * @default 20\n */\n period: number\n}\n\nexport const defaultCCIOptions: CommodityChannelIndexOptions = {\n period: 20,\n}\n\n/**\n * Commodity Channel Index (CCI)\n *\n * Developed by Donald Lambert in 1980, CCI measures the deviation of the\n * typical price from its simple moving average, normalized by mean deviation.\n * The constant 0.015 ensures approximately 70-80% of CCI values fall between\n * +100 and -100.\n *\n * Formula:\n * TP = (High + Low + Close) / 3\n * CCI = (TP - SMA(TP, period)) / (0.015 * Mean Deviation)\n * Mean Deviation = SUM(|TP_i - SMA|) / period\n *\n * @param source - Iterable of OHLC candle data\n * @param options - Configuration options\n * @param options.period - The period for CCI calculation (default: 20)\n * @returns Generator yielding CCI values\n */\nexport const cci = createSignal(\n ({ period }) => {\n assert(Number.isInteger(period) && period >= 1, new RangeError(`Expected period to be a positive integer, got ${period}`))\n const buffer: Dnum[] = []\n\n return (bar: RequiredProperties<CandleData, 'h' | 'l' | 'c'>) => {\n const h = from(bar.h, 18)\n const l = from(bar.l, 18)\n const c = from(bar.c, 18)\n const tp = divide(add(add(h, l), c), 3, 18)\n\n buffer.push(tp)\n if (buffer.length > period)\n buffer.shift()\n\n const n = buffer.length\n if (n < period) {\n return from(0, 18)\n }\n\n // SMA and Mean Deviation in a single pass\n let sum: Dnum = from(0, 18)\n for (const v of buffer) {\n sum = add(sum, v)\n }\n const smaVal = divide(sum, n, 18)\n\n let devSum: Dnum = from(0, 18)\n for (const v of buffer) {\n devSum = add(devSum, abs(subtract(v, smaVal)))\n }\n const meanDev = divide(devSum, n, 18)\n\n if (equal(meanDev, 0)) {\n return from(0, 18)\n }\n\n const currentTP = buffer[n - 1]\n const numerator = subtract(currentTP, smaVal)\n // 0.015 = 15/1000, the Lambert constant\n const lambertMeanDev = divide(\n [meanDev[0] * 15n, meanDev[1]],\n [1000n, 0],\n 18,\n )\n return divide(numerator, lambertMeanDev, 18)\n }\n },\n defaultCCIOptions,\n)\n\nexport { cci as commodityChannelIndex }\n","import type { Numberish } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { mul, sub } from 'dnum'\nimport { ema } from './exponentialMovingAverage'\n\nexport interface DoubleExponentialMovingAverageOptions {\n period: number\n}\n\nexport const defaultDoubleExponentialMovingAverageOptions: DoubleExponentialMovingAverageOptions = {\n period: 12,\n}\n\n/**\n * Double Exponential Moving Average (DEMA)\n *\n * DEMA reduces lag compared to a traditional EMA by applying the formula:\n * DEMA = 2 * EMA(data, period) - EMA(EMA(data, period), period)\n *\n * @param source - Iterable of input values\n * @param options - Configuration options\n * @param options.period - The lookback period (default: 12)\n * @returns Generator yielding DEMA values\n */\nexport const dema = createSignal(\n ({ period }) => {\n assert(Number.isInteger(period) && period >= 1, new RangeError(`Expected period to be a positive integer, got ${period}`))\n const ema1 = ema.create({ period })\n const ema2 = ema.create({ period })\n return (value: Numberish) => {\n const e1 = ema1(value)\n const e2 = ema2(e1)\n return sub(mul(e1, 2, 18), e2)\n }\n },\n defaultDoubleExponentialMovingAverageOptions,\n)\n\nexport { dema as doubleExponentialMovingAverage }\n","import type { Dnum, Numberish } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { sub } from 'dnum'\nimport { ema } from './exponentialMovingAverage'\n\nexport interface MACDOptions {\n fastPeriod: number\n slowPeriod: number\n signalPeriod: number\n}\n\nexport const defaultMACDOptions: MACDOptions = {\n fastPeriod: 12,\n slowPeriod: 26,\n signalPeriod: 9,\n}\n\nexport interface MACDPoint {\n macd: Dnum\n signal: Dnum\n histogram: Dnum\n}\n\n/**\n * Moving Average Convergence Divergence (MACD)\n *\n * MACD is a trend-following momentum indicator that shows the relationship\n * between two exponential moving averages of prices. It consists of three components:\n * - MACD Line: Fast EMA - Slow EMA\n * - Signal Line: EMA of the MACD line\n * - Histogram: MACD - Signal\n *\n * Formula:\n * - MACD = EMA(fastPeriod, prices) - EMA(slowPeriod, prices)\n * - Signal = EMA(signalPeriod, MACD)\n * - Histogram = MACD - Signal\n *\n * @param source - Iterable of price values\n * @param options - Configuration options\n * @param options.fastPeriod - Period for the fast EMA (default: 12)\n * @param options.slowPeriod - Period for the slow EMA (default: 26)\n * @param options.signalPeriod - Period for the signal EMA (default: 9)\n * @returns Generator yielding MACDPoint objects\n */\nexport const macd = createSignal(\n ({ fastPeriod, slowPeriod, signalPeriod }) => {\n assert(Number.isInteger(fastPeriod) && fastPeriod >= 1, new RangeError(`Expected fastPeriod to be a positive integer, got ${fastPeriod}`))\n assert(Number.isInteger(slowPeriod) && slowPeriod >= 1, new RangeError(`Expected slowPeriod to be a positive integer, got ${slowPeriod}`))\n assert(Number.isInteger(signalPeriod) && signalPeriod >= 1, new RangeError(`Expected signalPeriod to be a positive integer, got ${signalPeriod}`))\n const fastProc = ema.create({ period: fastPeriod })\n const slowProc = ema.create({ period: slowPeriod })\n const signalProc = ema.create({ period: signalPeriod })\n return (value: Numberish) => {\n const fast = fastProc(value)\n const slow = slowProc(value)\n const m = sub(fast, slow)\n const sig = signalProc(m)\n return { macd: m, signal: sig, histogram: sub(m, sig) }\n }\n },\n defaultMACDOptions,\n)\n\nexport { macd as movingAverageConvergenceDivergence }\n","import type { Dnum, Numberish } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { add, from, subtract } from 'dnum'\n\nexport interface MovingSumOptions {\n period: number\n}\n\nexport const defaultMovingSumOptions: MovingSumOptions = {\n period: 4,\n}\n\n/**\n * Moving Sum\n *\n * Calculates the sum of values in a sliding window of the specified period.\n */\nexport const msum = createSignal(\n ({ period }) => {\n assert(Number.isInteger(period) && period >= 1, new RangeError(`Expected period to be a positive integer, got ${period}`))\n const buffer: Dnum[] = Array.from({ length: period })\n let head = 0\n let count = 0\n let runningSum: Dnum = from(0, 18)\n\n return (value: Numberish) => {\n const v = from(value, 18)\n if (count < period) {\n buffer[count] = v\n runningSum = add(runningSum, v)\n count++\n }\n else {\n runningSum = subtract(runningSum, buffer[head])\n runningSum = add(runningSum, v)\n buffer[head] = v\n head = (head + 1) % period\n }\n return runningSum\n }\n },\n defaultMovingSumOptions,\n)\n","import type { CandleData, RequiredProperties } from '@vulcan-js/core'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { divide, from, subtract } from 'dnum'\nimport { ema } from './exponentialMovingAverage'\nimport { msum } from './movingSum'\n\nexport interface MassIndexOptions {\n /**\n * The period for EMA smoothing of the high-low range\n * @default 9\n */\n emaPeriod: number\n /**\n * The period for the moving sum of the EMA ratio\n * @default 25\n */\n miPeriod: number\n}\n\nexport const defaultMassIndexOptions: MassIndexOptions = {\n emaPeriod: 9,\n miPeriod: 25,\n}\n\n/**\n * Mass Index (MI)\n *\n * Developed by Donald Dorsey, the Mass Index uses the high-low range\n * to identify trend reversals based on range expansions. A \"reversal bulge\"\n * occurs when the Mass Index rises above 27 and then falls below 26.5.\n *\n * Formula:\n * Range = High - Low\n * EMA1 = EMA(Range, emaPeriod)\n * EMA2 = EMA(EMA1, emaPeriod)\n * Ratio = EMA1 / EMA2\n * MI = MovingSum(Ratio, miPeriod)\n *\n * @param source - Iterable of OHLC candle data (requires high and low)\n * @param options - Configuration options\n * @param options.emaPeriod - The EMA smoothing period (default: 9)\n * @param options.miPeriod - The moving sum period (default: 25)\n * @returns Generator yielding Mass Index values\n */\nexport const mi = createSignal(\n ({ emaPeriod, miPeriod }) => {\n assert(Number.isInteger(emaPeriod) && emaPeriod >= 1, new RangeError(`Expected emaPeriod to be a positive integer, got ${emaPeriod}`))\n assert(Number.isInteger(miPeriod) && miPeriod >= 1, new RangeError(`Expected miPeriod to be a positive integer, got ${miPeriod}`))\n const ema1Proc = ema.create({ period: emaPeriod })\n const ema2Proc = ema.create({ period: emaPeriod })\n const msumProc = msum.create({ period: miPeriod })\n\n return (bar: RequiredProperties<CandleData, 'h' | 'l'>) => {\n const range = subtract(from(bar.h, 18), from(bar.l, 18))\n const e1 = ema1Proc(range)\n const e2 = ema2Proc(e1)\n const ratio = divide(e1, e2, 18)\n return msumProc(ratio)\n }\n },\n defaultMassIndexOptions,\n)\n\nexport { mi as massIndex }\n","import type { Numberish } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { sma } from './simpleMovingAverage'\n\nexport interface TriangularMovingAverageOptions {\n period: number\n}\n\nexport const defaultTriangularMovingAverageOptions: TriangularMovingAverageOptions = {\n period: 4,\n}\n\nexport const trima = createSignal(\n ({ period }) => {\n assert(Number.isInteger(period) && period >= 1, new RangeError(`Expected period to be a positive integer, got ${period}`))\n let n1: number\n let n2: number\n\n if (period % 2 === 0) {\n n1 = period / 2\n n2 = n1 + 1\n }\n else {\n n1 = (period + 1) / 2\n n2 = n1\n }\n\n const sma1 = sma.create({ period: n2 })\n const sma2 = sma.create({ period: n1 })\n\n return (value: Numberish) => {\n const s1 = sma1(value)\n return sma2(s1)\n }\n },\n defaultTriangularMovingAverageOptions,\n)\n\nexport { trima as triangularMovingAverage }\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/trend/exponentialMovingAverage.ts","../src/momentum/absolutePriceOscillator.ts","../src/trend/simpleMovingAverage.ts","../src/momentum/awesomeOscillator.ts","../src/volume/accumulationDistribution.ts","../src/momentum/chaikinOscillator.ts","../src/trend/movingMax.ts","../src/trend/movingMin.ts","../src/momentum/ichimokuCloud.ts","../src/momentum/percentagePriceOscillator.ts","../src/trend/rollingMovingAverage.ts","../src/momentum/relativeStrengthIndex.ts","../src/momentum/stochasticOscillator.ts","../src/trend/aroon.ts","../src/trend/balanceOfPower.ts","../src/trend/chandeForecastOscillator.ts","../src/trend/commodityChannelIndex.ts","../src/trend/doubleExponentialMovingAverage.ts","../src/trend/macd.ts","../src/trend/movingSum.ts","../src/trend/massIndex.ts","../src/trend/parabolicSar.ts","../src/trend/triangularMovingAverage.ts"],"sourcesContent":["import type { Dnum, Numberish } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { add, divide, from, mul, subtract } from 'dnum'\n\nexport interface ExponentialMovingAverageOptions {\n period: number\n}\n\nexport const defaultExponentialMovingAverageOptions: ExponentialMovingAverageOptions = {\n period: 12,\n}\n\n/**\n * Exponential Moving Average (EMA)\n *\n * EMA = Price * k + PrevEMA * (1 - k)\n * Where k = 2 / (period + 1)\n */\nexport const ema = createSignal(\n ({ period }) => {\n assert(Number.isInteger(period) && period >= 1, new RangeError(`Expected period to be a positive integer, got ${period}`))\n const k = divide(from(2, 18), from(1 + period, 18), 18)\n const m = subtract(from(1, 18), k)\n let prev: Dnum | undefined\n return (value: Numberish) => {\n if (prev === undefined) {\n prev = from(value, 18)\n return prev\n }\n prev = add(\n mul(value, k, 18),\n mul(prev, m, 18),\n )\n return prev\n }\n },\n defaultExponentialMovingAverageOptions,\n)\n\nexport { ema as exponentialMovingAverage }\n","import type { Numberish } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { sub } from 'dnum'\nimport { ema } from '../trend/exponentialMovingAverage'\n\nexport interface AbsolutePriceOscillatorOptions {\n fastPeriod: number\n slowPeriod: number\n}\n\nexport const defaultAbsolutePriceOscillatorOptions: AbsolutePriceOscillatorOptions = {\n fastPeriod: 12,\n slowPeriod: 26,\n}\n\nexport const apo = createSignal(\n ({ fastPeriod, slowPeriod }) => {\n assert(Number.isInteger(fastPeriod) && fastPeriod >= 1, new RangeError(`Expected fastPeriod to be a positive integer, got ${fastPeriod}`))\n assert(Number.isInteger(slowPeriod) && slowPeriod >= 1, new RangeError(`Expected slowPeriod to be a positive integer, got ${slowPeriod}`))\n const fastProc = ema.create({ period: fastPeriod })\n const slowProc = ema.create({ period: slowPeriod })\n return (value: Numberish) => sub(fastProc(value), slowProc(value))\n },\n defaultAbsolutePriceOscillatorOptions,\n)\n\nexport { apo as absolutePriceOscillator }\n","import type { Dnum, Numberish } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { add, div, from, subtract } from 'dnum'\n\nexport interface SimpleMovingAverageOptions {\n /**\n * The period for calculating the moving average\n * @default 2\n */\n period: number\n}\n\nexport const defaultSMAOptions: SimpleMovingAverageOptions = {\n period: 2,\n}\n\n/**\n * Simple Moving Average (SMA)\n *\n * Calculates the arithmetic mean of a set of values over a specified period.\n * The SMA is calculated by summing all values in the period and dividing by the period length.\n *\n * Formula: SMA = (P1 + P2 + ... + Pn) / n\n * Where: P = Price values, n = Period\n *\n * @param source - Iterable of price values\n * @param options - Configuration options\n * @param options.period - The period for calculating the moving average (default: 2)\n * @returns Generator yielding SMA values\n */\nexport const sma = createSignal(\n ({ period }) => {\n assert(Number.isInteger(period) && period >= 1, new RangeError(`Expected period to be a positive integer, got ${period}`))\n const buffer: Dnum[] = Array.from({ length: period })\n let head = 0\n let count = 0\n let runningSum: Dnum = from(0, 18)\n\n return (value: Numberish) => {\n const v = from(value, 18)\n if (count < period) {\n buffer[count] = v\n runningSum = add(runningSum, v)\n count++\n }\n else {\n runningSum = subtract(runningSum, buffer[head])\n runningSum = add(runningSum, v)\n buffer[head] = v\n head = (head + 1) % period\n }\n return div(runningSum, count, 18)\n }\n },\n defaultSMAOptions,\n)\n\nexport { sma as simpleMovingAverage }\n","import type { CandleData, RequiredProperties } from '@vulcan-js/core'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { add, div, from, sub } from 'dnum'\nimport { sma } from '../trend/simpleMovingAverage'\n\nexport interface AwesomeOscillatorOptions {\n fastPeriod: number\n slowPeriod: number\n}\n\nexport const defaultAwesomeOscillatorOptions: AwesomeOscillatorOptions = {\n fastPeriod: 5,\n slowPeriod: 34,\n}\n\n/**\n * Awesome Oscillator (AO)\n *\n * AO = SMA(median, fastPeriod) - SMA(median, slowPeriod)\n * Where median = (high + low) / 2\n */\nexport const ao = createSignal(\n ({ fastPeriod, slowPeriod }) => {\n assert(Number.isInteger(fastPeriod) && fastPeriod >= 1, new RangeError(`Expected fastPeriod to be a positive integer, got ${fastPeriod}`))\n assert(Number.isInteger(slowPeriod) && slowPeriod >= 1, new RangeError(`Expected slowPeriod to be a positive integer, got ${slowPeriod}`))\n const fastProc = sma.create({ period: fastPeriod })\n const slowProc = sma.create({ period: slowPeriod })\n return (bar: RequiredProperties<CandleData, 'h' | 'l'>) => {\n const median = div(add(from(bar.h, 18), from(bar.l, 18)), 2, 18)\n return sub(fastProc(median), slowProc(median))\n }\n },\n defaultAwesomeOscillatorOptions,\n)\n\nexport { ao as awesomeOscillator }\n","import type { CandleData, RequiredProperties } from '@vulcan-js/core'\nimport type { Dnum } from 'dnum'\nimport { createSignal } from '@vulcan-js/core'\nimport { add, divide, equal, from, multiply, subtract } from 'dnum'\n\n/**\n * Accumulation/Distribution Indicator (A/D). Cumulative indicator\n * that uses volume and price to assess whether a stock is\n * being accumulated or distributed.\n *\n * MFM = ((Closing - Low) - (High - Closing)) / (High - Low)\n * MFV = MFM * Period Volume\n * AD = Previous AD + CMFV\n */\nexport const ad = createSignal(\n () => {\n let prevAD: Dnum = from(0, 18)\n return (bar: RequiredProperties<CandleData, 'h' | 'l' | 'c' | 'v'>) => {\n const h = from(bar.h, 18)\n const l = from(bar.l, 18)\n const c = from(bar.c, 18)\n const v = from(bar.v, 18)\n\n const range = subtract(h, l)\n // When high equals low, the range is zero and MFM is undefined; treat as 0\n const mfm = equal(range, 0)\n ? from(0, 18)\n : divide(\n subtract(subtract(c, l), subtract(h, c)),\n range,\n 18,\n )\n const mfv = multiply(mfm, v)\n prevAD = add(mfv, prevAD)\n return prevAD\n }\n },\n)\n\nexport { ad as accumulationDistribution }\n","import type { CandleData, RequiredProperties } from '@vulcan-js/core'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { sub } from 'dnum'\nimport { ema } from '../trend/exponentialMovingAverage'\nimport { ad } from '../volume/accumulationDistribution'\n\nexport interface ChaikinOscillatorOptions {\n fastPeriod: number\n slowPeriod: number\n}\n\nexport const defaultChaikinOscillatorOptions: ChaikinOscillatorOptions = {\n fastPeriod: 3,\n slowPeriod: 10,\n}\n\n/**\n * The ChaikinOscillator function measures the momentum of the\n * Accumulation/Distribution (A/D) using the Moving Average\n * Convergence Divergence (MACD) formula. It takes the\n * difference between fast and slow periods EMA of the A/D.\n * Cross above the A/D line indicates bullish.\n *\n * CO = Ema(fastPeriod, AD) - Ema(slowPeriod, AD)\n */\nexport const cmo = createSignal(\n ({ fastPeriod, slowPeriod }) => {\n assert(Number.isInteger(fastPeriod) && fastPeriod >= 1, new RangeError(`Expected fastPeriod to be a positive integer, got ${fastPeriod}`))\n assert(Number.isInteger(slowPeriod) && slowPeriod >= 1, new RangeError(`Expected slowPeriod to be a positive integer, got ${slowPeriod}`))\n const adProc = ad.create()\n const fastProc = ema.create({ period: fastPeriod })\n const slowProc = ema.create({ period: slowPeriod })\n return (bar: RequiredProperties<CandleData, 'h' | 'l' | 'c' | 'v'>) => {\n const adVal = adProc(bar)\n return sub(fastProc(adVal), slowProc(adVal))\n }\n },\n defaultChaikinOscillatorOptions,\n)\n\nexport { cmo as chaikinOscillator }\n","import type { Dnum, Numberish } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { from, gt } from 'dnum'\n\nexport interface MovingMaxOptions {\n /**\n * period\n */\n period: number\n}\n\nexport const defaultMovingMaxOptions: MovingMaxOptions = {\n period: 4,\n}\n\n/**\n * Moving Maximum (MovingMax)\n */\nexport const mmax = createSignal(\n ({ period }) => {\n assert(Number.isInteger(period) && period >= 1, new RangeError(`Expected period to be a positive integer, got ${period}`))\n const buffer: Dnum[] = []\n return (value: Numberish) => {\n buffer.push(from(value, 18))\n if (buffer.length > period)\n buffer.shift()\n return buffer.reduce((max, cur) => gt(max, cur) ? max : cur)\n }\n },\n defaultMovingMaxOptions,\n)\n\nexport { mmax as movingMax }\n","import type { Dnum, Numberish } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { from, lt } from 'dnum'\n\nexport interface MovingMinOptions {\n /**\n * period\n */\n period: number\n}\n\nexport const defaultMovingMinOptions: MovingMinOptions = {\n period: 4,\n}\n\n/**\n * Moving Minimum (MovingMin)\n */\nexport const mmin = createSignal(\n ({ period }) => {\n assert(Number.isInteger(period) && period >= 1, new RangeError(`Expected period to be a positive integer, got ${period}`))\n const buffer: Dnum[] = []\n return (value: Numberish) => {\n buffer.push(from(value, 18))\n if (buffer.length > period)\n buffer.shift()\n return buffer.reduce((min, cur) => lt(min, cur) ? min : cur)\n }\n },\n defaultMovingMinOptions,\n)\n\nexport { mmin as movingMin }\n","import type { CandleData, RequiredProperties } from '@vulcan-js/core'\nimport type { Dnum } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { add, div, from } from 'dnum'\nimport { mmax } from '../trend/movingMax'\nimport { mmin } from '../trend/movingMin'\n\nexport interface IchimokuCloudOptions {\n /** Conversion line period */\n conversionPeriod: number\n /** Base line period */\n basePeriod: number\n /** Leading span B period */\n leadingBPeriod: number\n}\n\nexport const defaultIchimokuCloudOptions: IchimokuCloudOptions = {\n conversionPeriod: 9,\n basePeriod: 26,\n leadingBPeriod: 52,\n}\n\nexport interface IchimokuCloudPoint {\n conversion: Dnum\n base: Dnum\n leadingA: Dnum\n leadingB: Dnum\n lagging: Dnum\n}\n\n/**\n * Ichimoku Cloud (Ichimoku Kinko Hyo)\n *\n * Computes raw values for each component. Displacement (shifting\n * Leading Spans forward and Lagging Span backward on the chart)\n * is a presentation concern left to the consumer.\n *\n * - Conversion (Tenkan-sen): (highest high + lowest low) / 2 over conversionPeriod\n * - Base (Kijun-sen): (highest high + lowest low) / 2 over basePeriod\n * - Leading Span A (Senkou A): (conversion + base) / 2\n * - Leading Span B (Senkou B): (highest high + lowest low) / 2 over leadingBPeriod\n * - Lagging (Chikou): current close price\n */\nexport const ichimokuCloud = createSignal(\n ({ conversionPeriod, basePeriod, leadingBPeriod }) => {\n assert(Number.isInteger(conversionPeriod) && conversionPeriod >= 1, new RangeError(`Expected conversionPeriod to be a positive integer, got ${conversionPeriod}`))\n assert(Number.isInteger(basePeriod) && basePeriod >= 1, new RangeError(`Expected basePeriod to be a positive integer, got ${basePeriod}`))\n assert(Number.isInteger(leadingBPeriod) && leadingBPeriod >= 1, new RangeError(`Expected leadingBPeriod to be a positive integer, got ${leadingBPeriod}`))\n const convHighProc = mmax.create({ period: conversionPeriod })\n const convLowProc = mmin.create({ period: conversionPeriod })\n const baseHighProc = mmax.create({ period: basePeriod })\n const baseLowProc = mmin.create({ period: basePeriod })\n const leadBHighProc = mmax.create({ period: leadingBPeriod })\n const leadBLowProc = mmin.create({ period: leadingBPeriod })\n\n return (bar: RequiredProperties<CandleData, 'h' | 'l' | 'c'>) => {\n const h = from(bar.h, 18)\n const l = from(bar.l, 18)\n\n const conversion = div(add(convHighProc(h), convLowProc(l)), 2, 18)\n const base = div(add(baseHighProc(h), baseLowProc(l)), 2, 18)\n const leadingA = div(add(conversion, base), 2, 18)\n const leadingB = div(add(leadBHighProc(h), leadBLowProc(l)), 2, 18)\n\n return { conversion, base, leadingA, leadingB, lagging: from(bar.c, 18) }\n }\n },\n defaultIchimokuCloudOptions,\n)\n","import type { Dnum, Numberish } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { div, eq, from, mul, sub } from 'dnum'\nimport { ema } from '../trend/exponentialMovingAverage'\n\nexport interface PercentagePriceOscillatorOptions {\n fastPeriod: number\n slowPeriod: number\n signalPeriod: number\n}\n\nexport const defaultPercentagePriceOscillatorOptions: PercentagePriceOscillatorOptions = {\n fastPeriod: 12,\n slowPeriod: 26,\n signalPeriod: 9,\n}\n\nexport interface PercentagePriceOscillatorPoint {\n ppo: Dnum\n signal: Dnum\n histogram: Dnum\n}\n\n/**\n * Percentage Price Oscillator (PPO)\n *\n * The Percentage Price Oscillator (PPO) is a momentum oscillator that measures the difference\n * between two moving averages as a percentage of the slower moving average. It consists of three components:\n * - PPO Line: ((Fast EMA - Slow EMA) / Slow EMA) * 100\n * - Signal Line: EMA of the PPO line\n * - Histogram: PPO - Signal\n *\n * Formula:\n * - PPO = ((EMA(fastPeriod, prices) - EMA(slowPeriod, prices)) / EMA(slowPeriod, prices)) * 100\n * - Signal = EMA(signalPeriod, PPO)\n * - Histogram = PPO - Signal\n *\n * @param source - Iterable of price values\n * @param options - Configuration options\n * @param options.fastPeriod - Period for the fast EMA (default: 12)\n * @param options.slowPeriod - Period for the slow EMA (default: 26)\n * @param options.signalPeriod - Period for the signal EMA (default: 9)\n * @returns Generator yielding PPO point objects\n */\nexport const ppo = createSignal(\n ({ fastPeriod, slowPeriod, signalPeriod }) => {\n assert(Number.isInteger(fastPeriod) && fastPeriod >= 1, new RangeError(`Expected fastPeriod to be a positive integer, got ${fastPeriod}`))\n assert(Number.isInteger(slowPeriod) && slowPeriod >= 1, new RangeError(`Expected slowPeriod to be a positive integer, got ${slowPeriod}`))\n assert(Number.isInteger(signalPeriod) && signalPeriod >= 1, new RangeError(`Expected signalPeriod to be a positive integer, got ${signalPeriod}`))\n const fastProc = ema.create({ period: fastPeriod })\n const slowProc = ema.create({ period: slowPeriod })\n const signalProc = ema.create({ period: signalPeriod })\n return (value: Numberish) => {\n const fast = fastProc(from(value, 18))\n const slow = slowProc(from(value, 18))\n const ppoVal = eq(slow, 0) ? from(0, 18) : mul(div(sub(fast, slow), slow, 18), 100, 18)\n const sig = signalProc(ppoVal)\n return { ppo: ppoVal, signal: sig, histogram: sub(ppoVal, sig) }\n }\n },\n defaultPercentagePriceOscillatorOptions,\n)\n\nexport { ppo as percentagePriceOscillator }\n","import type { Dnum, Numberish } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { add, div, from, mul } from 'dnum'\n\nexport interface RMAOptions {\n /**\n * period\n */\n period: number\n}\n\nexport const defaultRMAOptions: RMAOptions = {\n period: 4,\n}\n\n/**\n * Rolling moving average (RMA).\n *\n * R[0] to R[p-1] is SMA(values)\n *\n * R[p] and after is R[i] = ((R[i-1]*(p-1)) + v[i]) / p\n */\nexport const rma = createSignal(\n ({ period }) => {\n assert(Number.isInteger(period) && period >= 1, new RangeError(`Expected period to be a positive integer, got ${period}`))\n let count = 0\n let sum: Dnum = from(0, 18)\n let prev: Dnum = from(0, 18)\n\n return (value: Numberish) => {\n if (count < period) {\n sum = add(sum, value)\n count++\n prev = div(sum, count, 18)\n return prev\n }\n prev = div(\n add(mul(prev, period - 1, 18), value),\n period,\n 18,\n )\n return prev\n }\n },\n defaultRMAOptions,\n)\n\nexport { rma as rollingMovingAverage }\n","import type { Dnum, Numberish } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { add, div, eq, from, gt, mul, sub } from 'dnum'\nimport { rma } from '../trend/rollingMovingAverage'\n\nexport interface RSIOptions {\n period: number\n}\n\nexport const defaultRSIOptions: RSIOptions = {\n period: 14,\n}\n\n/**\n * Relative Strength Index (RSI). It is a momentum indicator that measures the magnitude of\n * recent price changes to evaluate overbought and oversold conditions\n * using the given window period.\n *\n * RS = Average Gain / Average Loss\n *\n * RSI = 100 - (100 / (1 + RS))\n */\nexport const rsi = createSignal(\n ({ period }) => {\n assert(Number.isInteger(period) && period >= 1, new RangeError(`Expected period to be a positive integer, got ${period}`))\n const gainProc = rma.create({ period })\n const lossProc = rma.create({ period })\n let prev: Dnum | undefined\n\n return (value: Numberish) => {\n const price = from(value, 18)\n\n if (prev === undefined) {\n prev = price\n gainProc(from(0, 18))\n lossProc(from(0, 18))\n return from(0, 18)\n }\n\n const change = sub(price, prev)\n prev = price\n\n const gain = gt(change, 0) ? change : from(0, 18)\n const loss = gt(change, 0) ? from(0, 18) : mul(change, -1, 18)\n\n const avgGain = gainProc(gain)\n const avgLoss = lossProc(loss)\n\n if (eq(avgLoss, 0)) {\n return from(100, 18)\n }\n\n const rs = div(avgGain, avgLoss, 18)\n return sub(100, div(100, add(1, rs), 18))\n }\n },\n defaultRSIOptions,\n)\n\nexport { rsi as relativeStrengthIndex }\n","import type { CandleData, RequiredProperties } from '@vulcan-js/core'\nimport type { Dnum } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { div, eq, from, mul, sub } from 'dnum'\nimport { mmax } from '../trend/movingMax'\nimport { mmin } from '../trend/movingMin'\nimport { sma } from '../trend/simpleMovingAverage'\n\nexport interface StochasticOscillatorOptions {\n /** The %k period */\n kPeriod: number\n /** The %k slowing period */\n slowingPeriod: number\n /** The %d period */\n dPeriod: number\n}\n\nexport const defaultStochasticOscillatorOptions: StochasticOscillatorOptions = {\n kPeriod: 14,\n slowingPeriod: 1,\n dPeriod: 3,\n}\n\nexport interface StochPoint {\n k: Dnum\n d: Dnum\n}\n\n/**\n * Stochastic Oscillator\n *\n * %K = ((Close - Lowest Low) / (Highest High - Lowest Low)) * 100\n * %D = SMA(%K, dPeriod)\n */\nexport const stoch = createSignal(\n ({ kPeriod, slowingPeriod, dPeriod }) => {\n assert(Number.isInteger(kPeriod) && kPeriod >= 1, new RangeError(`Expected kPeriod to be a positive integer, got ${kPeriod}`))\n assert(Number.isInteger(slowingPeriod) && slowingPeriod >= 1, new RangeError(`Expected slowingPeriod to be a positive integer, got ${slowingPeriod}`))\n assert(Number.isInteger(dPeriod) && dPeriod >= 1, new RangeError(`Expected dPeriod to be a positive integer, got ${dPeriod}`))\n const mmaxProc = mmax.create({ period: kPeriod })\n const mminProc = mmin.create({ period: kPeriod })\n const slowingProc = slowingPeriod > 1 ? sma.create({ period: slowingPeriod }) : null\n const dProc = sma.create({ period: dPeriod })\n return (bar: RequiredProperties<CandleData, 'h' | 'l' | 'c'>) => {\n const h = from(bar.h, 18)\n const l = from(bar.l, 18)\n const c = from(bar.c, 18)\n\n const highestHigh = mmaxProc(h)\n const lowestLow = mminProc(l)\n\n const range = sub(highestHigh, lowestLow, 18)\n const rawK = eq(range, 0) ? from(0, 18) : mul(div(sub(c, lowestLow, 18), range, 18), 100, 18)\n const k = slowingProc ? slowingProc(rawK) : rawK\n return { k, d: dProc(k) }\n }\n },\n defaultStochasticOscillatorOptions,\n)\n\nexport { stoch as stochasticOscillator }\n","import type { CandleData, RequiredProperties } from '@vulcan-js/core'\nimport type { Dnum } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { divide, from, gt, lt, multiply, subtract } from 'dnum'\n\nexport interface AroonOptions {\n period: number\n}\n\nexport const defaultAroonOptions: AroonOptions = {\n period: 25,\n}\n\nexport interface AroonPoint {\n up: Dnum\n down: Dnum\n oscillator: Dnum\n}\n\n/**\n * Aroon Indicator\n *\n * Aroon Up = ((period - days since highest high) / period) * 100\n * Aroon Down = ((period - days since lowest low) / period) * 100\n * Oscillator = Aroon Up - Aroon Down\n */\nexport const aroon = createSignal(\n ({ period }) => {\n assert(Number.isInteger(period) && period >= 1, new RangeError(`Expected period to be a positive integer, got ${period}`))\n const highBuffer: Dnum[] = []\n const lowBuffer: Dnum[] = []\n\n return (bar: RequiredProperties<CandleData, 'h' | 'l'>) => {\n const h = from(bar.h, 18)\n const l = from(bar.l, 18)\n\n highBuffer.push(h)\n lowBuffer.push(l)\n if (highBuffer.length > period + 1)\n highBuffer.shift()\n if (lowBuffer.length > period + 1)\n lowBuffer.shift()\n\n let highestIdx = 0\n let lowestIdx = 0\n for (let j = 1; j < highBuffer.length; j++) {\n if (!gt(highBuffer[highestIdx], highBuffer[j]))\n highestIdx = j\n if (!lt(lowBuffer[lowestIdx], lowBuffer[j]))\n lowestIdx = j\n }\n\n const daysSinceHigh = highBuffer.length - 1 - highestIdx\n const daysSinceLow = lowBuffer.length - 1 - lowestIdx\n\n const periodDnum = from(period, 18)\n const up = divide(multiply(from(period - daysSinceHigh, 18), 100, 18), periodDnum, 18)\n const down = divide(multiply(from(period - daysSinceLow, 18), 100, 18), periodDnum, 18)\n\n return {\n up,\n down,\n oscillator: subtract(up, down),\n }\n }\n },\n defaultAroonOptions,\n)\n","import type { CandleData, RequiredProperties } from '@vulcan-js/core'\nimport { createSignal } from '@vulcan-js/core'\nimport { divide, equal, from, subtract } from 'dnum'\n\nexport const bop = createSignal(\n () => {\n return (bar: RequiredProperties<CandleData, 'o' | 'h' | 'l' | 'c'>) => {\n const o = from(bar.o, 18)\n const h = from(bar.h, 18)\n const l = from(bar.l, 18)\n const c = from(bar.c, 18)\n const range = subtract(h, l)\n if (equal(range, 0)) {\n return from(0, 18)\n }\n return divide(subtract(c, o), range, 18)\n }\n },\n)\n\nexport { bop as balanceOfPower }\n","import type { Dnum, Numberish } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { add, divide, equal, from, multiply, subtract } from 'dnum'\n\nexport interface ChandeForecastOscillatorOptions {\n /**\n * The period for linear regression\n * @default 14\n */\n period: number\n}\n\nexport const defaultCFOOptions: ChandeForecastOscillatorOptions = {\n period: 14,\n}\n\n/**\n * Chande Forecast Oscillator (CFO)\n *\n * Measures the percentage difference between the actual close price and the\n * n-period linear regression forecast price. Positive values indicate bullish\n * momentum (price above forecast), negative values indicate bearish momentum.\n *\n * Formula: CFO = ((Close - Forecast) / Close) * 100\n * Where: Forecast = Linear regression value at current point\n *\n * @param source - Iterable of price values\n * @param options - Configuration options\n * @param options.period - The period for linear regression (default: 14)\n * @returns Generator yielding CFO values as percentages\n */\nexport const cfo = createSignal(\n ({ period }) => {\n assert(Number.isInteger(period) && period >= 1, new RangeError(`Expected period to be a positive integer, got ${period}`))\n const buffer: Dnum[] = []\n\n return (value: Numberish) => {\n buffer.push(from(value, 18))\n if (buffer.length > period)\n buffer.shift()\n\n const n = buffer.length\n if (n < 2) {\n return from(0, 18)\n }\n\n // Precompute X-related sums as plain integers\n const xSum = n * (n + 1) / 2\n const x2Sum = n * (n + 1) * (2 * n + 1) / 6\n const denom = n * x2Sum - xSum * xSum\n\n // Compute Y-dependent sums (keep all Dnum at 18 decimals)\n let sumY: Dnum = from(0, 18)\n let sumXY: Dnum = from(0, 18)\n for (let i = 0; i < n; i++) {\n sumY = add(sumY, buffer[i])\n sumXY = add(sumXY, multiply(buffer[i], i + 1))\n }\n\n // slope = (n * SUM(XY) - SUM(X) * SUM(Y)) / denom\n const num = subtract(multiply(sumXY, n), multiply(sumY, xSum))\n const slope = divide(num, denom, 18)\n\n // intercept = (SUM(Y) - slope * SUM(X)) / n\n const intercept = divide(subtract(sumY, multiply(slope, xSum)), n, 18)\n\n // forecast = slope * n + intercept\n const forecast = add(multiply(slope, n), intercept)\n\n const close = buffer[n - 1]\n if (equal(close, 0)) {\n return from(0, 18)\n }\n\n return divide(multiply(subtract(close, forecast), 100), close, 18)\n }\n },\n defaultCFOOptions,\n)\n\nexport { cfo as chandeForecastOscillator }\n","import type { CandleData, RequiredProperties } from '@vulcan-js/core'\nimport type { Dnum } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { abs, add, divide, equal, from, subtract } from 'dnum'\n\nexport interface CommodityChannelIndexOptions {\n /**\n * The period for CCI calculation\n * @default 20\n */\n period: number\n}\n\nexport const defaultCCIOptions: CommodityChannelIndexOptions = {\n period: 20,\n}\n\n/**\n * Commodity Channel Index (CCI)\n *\n * Developed by Donald Lambert in 1980, CCI measures the deviation of the\n * typical price from its simple moving average, normalized by mean deviation.\n * The constant 0.015 ensures approximately 70-80% of CCI values fall between\n * +100 and -100.\n *\n * Formula:\n * TP = (High + Low + Close) / 3\n * CCI = (TP - SMA(TP, period)) / (0.015 * Mean Deviation)\n * Mean Deviation = SUM(|TP_i - SMA|) / period\n *\n * @param source - Iterable of OHLC candle data\n * @param options - Configuration options\n * @param options.period - The period for CCI calculation (default: 20)\n * @returns Generator yielding CCI values\n */\nexport const cci = createSignal(\n ({ period }) => {\n assert(Number.isInteger(period) && period >= 1, new RangeError(`Expected period to be a positive integer, got ${period}`))\n const buffer: Dnum[] = []\n\n return (bar: RequiredProperties<CandleData, 'h' | 'l' | 'c'>) => {\n const h = from(bar.h, 18)\n const l = from(bar.l, 18)\n const c = from(bar.c, 18)\n const tp = divide(add(add(h, l), c), 3, 18)\n\n buffer.push(tp)\n if (buffer.length > period)\n buffer.shift()\n\n const n = buffer.length\n if (n < period) {\n return from(0, 18)\n }\n\n // SMA and Mean Deviation in a single pass\n let sum: Dnum = from(0, 18)\n for (const v of buffer) {\n sum = add(sum, v)\n }\n const smaVal = divide(sum, n, 18)\n\n let devSum: Dnum = from(0, 18)\n for (const v of buffer) {\n devSum = add(devSum, abs(subtract(v, smaVal)))\n }\n const meanDev = divide(devSum, n, 18)\n\n if (equal(meanDev, 0)) {\n return from(0, 18)\n }\n\n const currentTP = buffer[n - 1]\n const numerator = subtract(currentTP, smaVal)\n // 0.015 = 15/1000, the Lambert constant\n const lambertMeanDev = divide(\n [meanDev[0] * 15n, meanDev[1]],\n [1000n, 0],\n 18,\n )\n return divide(numerator, lambertMeanDev, 18)\n }\n },\n defaultCCIOptions,\n)\n\nexport { cci as commodityChannelIndex }\n","import type { Numberish } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { mul, sub } from 'dnum'\nimport { ema } from './exponentialMovingAverage'\n\nexport interface DoubleExponentialMovingAverageOptions {\n period: number\n}\n\nexport const defaultDoubleExponentialMovingAverageOptions: DoubleExponentialMovingAverageOptions = {\n period: 12,\n}\n\n/**\n * Double Exponential Moving Average (DEMA)\n *\n * DEMA reduces lag compared to a traditional EMA by applying the formula:\n * DEMA = 2 * EMA(data, period) - EMA(EMA(data, period), period)\n *\n * @param source - Iterable of input values\n * @param options - Configuration options\n * @param options.period - The lookback period (default: 12)\n * @returns Generator yielding DEMA values\n */\nexport const dema = createSignal(\n ({ period }) => {\n assert(Number.isInteger(period) && period >= 1, new RangeError(`Expected period to be a positive integer, got ${period}`))\n const ema1 = ema.create({ period })\n const ema2 = ema.create({ period })\n return (value: Numberish) => {\n const e1 = ema1(value)\n const e2 = ema2(e1)\n return sub(mul(e1, 2, 18), e2)\n }\n },\n defaultDoubleExponentialMovingAverageOptions,\n)\n\nexport { dema as doubleExponentialMovingAverage }\n","import type { Dnum, Numberish } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { sub } from 'dnum'\nimport { ema } from './exponentialMovingAverage'\n\nexport interface MACDOptions {\n fastPeriod: number\n slowPeriod: number\n signalPeriod: number\n}\n\nexport const defaultMACDOptions: MACDOptions = {\n fastPeriod: 12,\n slowPeriod: 26,\n signalPeriod: 9,\n}\n\nexport interface MACDPoint {\n macd: Dnum\n signal: Dnum\n histogram: Dnum\n}\n\n/**\n * Moving Average Convergence Divergence (MACD)\n *\n * MACD is a trend-following momentum indicator that shows the relationship\n * between two exponential moving averages of prices. It consists of three components:\n * - MACD Line: Fast EMA - Slow EMA\n * - Signal Line: EMA of the MACD line\n * - Histogram: MACD - Signal\n *\n * Formula:\n * - MACD = EMA(fastPeriod, prices) - EMA(slowPeriod, prices)\n * - Signal = EMA(signalPeriod, MACD)\n * - Histogram = MACD - Signal\n *\n * @param source - Iterable of price values\n * @param options - Configuration options\n * @param options.fastPeriod - Period for the fast EMA (default: 12)\n * @param options.slowPeriod - Period for the slow EMA (default: 26)\n * @param options.signalPeriod - Period for the signal EMA (default: 9)\n * @returns Generator yielding MACDPoint objects\n */\nexport const macd = createSignal(\n ({ fastPeriod, slowPeriod, signalPeriod }) => {\n assert(Number.isInteger(fastPeriod) && fastPeriod >= 1, new RangeError(`Expected fastPeriod to be a positive integer, got ${fastPeriod}`))\n assert(Number.isInteger(slowPeriod) && slowPeriod >= 1, new RangeError(`Expected slowPeriod to be a positive integer, got ${slowPeriod}`))\n assert(Number.isInteger(signalPeriod) && signalPeriod >= 1, new RangeError(`Expected signalPeriod to be a positive integer, got ${signalPeriod}`))\n const fastProc = ema.create({ period: fastPeriod })\n const slowProc = ema.create({ period: slowPeriod })\n const signalProc = ema.create({ period: signalPeriod })\n return (value: Numberish) => {\n const fast = fastProc(value)\n const slow = slowProc(value)\n const m = sub(fast, slow)\n const sig = signalProc(m)\n return { macd: m, signal: sig, histogram: sub(m, sig) }\n }\n },\n defaultMACDOptions,\n)\n\nexport { macd as movingAverageConvergenceDivergence }\n","import type { Dnum, Numberish } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { add, from, subtract } from 'dnum'\n\nexport interface MovingSumOptions {\n period: number\n}\n\nexport const defaultMovingSumOptions: MovingSumOptions = {\n period: 4,\n}\n\n/**\n * Moving Sum\n *\n * Calculates the sum of values in a sliding window of the specified period.\n */\nexport const msum = createSignal(\n ({ period }) => {\n assert(Number.isInteger(period) && period >= 1, new RangeError(`Expected period to be a positive integer, got ${period}`))\n const buffer: Dnum[] = Array.from({ length: period })\n let head = 0\n let count = 0\n let runningSum: Dnum = from(0, 18)\n\n return (value: Numberish) => {\n const v = from(value, 18)\n if (count < period) {\n buffer[count] = v\n runningSum = add(runningSum, v)\n count++\n }\n else {\n runningSum = subtract(runningSum, buffer[head])\n runningSum = add(runningSum, v)\n buffer[head] = v\n head = (head + 1) % period\n }\n return runningSum\n }\n },\n defaultMovingSumOptions,\n)\n","import type { CandleData, RequiredProperties } from '@vulcan-js/core'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { divide, from, subtract } from 'dnum'\nimport { ema } from './exponentialMovingAverage'\nimport { msum } from './movingSum'\n\nexport interface MassIndexOptions {\n /**\n * The period for EMA smoothing of the high-low range\n * @default 9\n */\n emaPeriod: number\n /**\n * The period for the moving sum of the EMA ratio\n * @default 25\n */\n miPeriod: number\n}\n\nexport const defaultMassIndexOptions: MassIndexOptions = {\n emaPeriod: 9,\n miPeriod: 25,\n}\n\n/**\n * Mass Index (MI)\n *\n * Developed by Donald Dorsey, the Mass Index uses the high-low range\n * to identify trend reversals based on range expansions. A \"reversal bulge\"\n * occurs when the Mass Index rises above 27 and then falls below 26.5.\n *\n * Formula:\n * Range = High - Low\n * EMA1 = EMA(Range, emaPeriod)\n * EMA2 = EMA(EMA1, emaPeriod)\n * Ratio = EMA1 / EMA2\n * MI = MovingSum(Ratio, miPeriod)\n *\n * @param source - Iterable of OHLC candle data (requires high and low)\n * @param options - Configuration options\n * @param options.emaPeriod - The EMA smoothing period (default: 9)\n * @param options.miPeriod - The moving sum period (default: 25)\n * @returns Generator yielding Mass Index values\n */\nexport const mi = createSignal(\n ({ emaPeriod, miPeriod }) => {\n assert(Number.isInteger(emaPeriod) && emaPeriod >= 1, new RangeError(`Expected emaPeriod to be a positive integer, got ${emaPeriod}`))\n assert(Number.isInteger(miPeriod) && miPeriod >= 1, new RangeError(`Expected miPeriod to be a positive integer, got ${miPeriod}`))\n const ema1Proc = ema.create({ period: emaPeriod })\n const ema2Proc = ema.create({ period: emaPeriod })\n const msumProc = msum.create({ period: miPeriod })\n\n return (bar: RequiredProperties<CandleData, 'h' | 'l'>) => {\n const range = subtract(from(bar.h, 18), from(bar.l, 18))\n const e1 = ema1Proc(range)\n const e2 = ema2Proc(e1)\n const ratio = divide(e1, e2, 18)\n return msumProc(ratio)\n }\n },\n defaultMassIndexOptions,\n)\n\nexport { mi as massIndex }\n","import type { CandleData, RequiredProperties } from '@vulcan-js/core'\nimport type { Dnum } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { add, from, gt, lt, mul, sub } from 'dnum'\n\nexport interface ParabolicSarOptions {\n start: number\n increment: number\n max: number\n}\n\nexport const defaultParabolicSarOptions: ParabolicSarOptions = {\n start: 0.02,\n increment: 0.02,\n max: 0.2,\n}\n\nexport interface PSARPoint {\n psar: Dnum\n isUptrend: boolean\n}\n\n/**\n * Parabolic SAR (Stop and Reverse)\n *\n * Developed by J. Welles Wilder Jr. in 1978, the Parabolic SAR is a\n * trend-following indicator that provides potential entry and exit points.\n * It appears as a series of dots above or below the price, indicating\n * the current trend direction and potential reversal points.\n *\n * Formula:\n * SAR_new = SAR_prev + AF * (EP - SAR_prev)\n *\n * Where:\n * AF = Acceleration Factor (starts at `start`, increments by `increment`\n * on each new EP, capped at `max`)\n * EP = Extreme Point (highest high in uptrend, lowest low in downtrend)\n *\n * The SAR is clamped to not exceed the two prior bars' price range.\n * A reversal occurs when price penetrates the SAR level.\n *\n * @param source - Iterable of candle data with high and low prices\n * @param options - Configuration options\n * @param options.start - Initial acceleration factor (default: 0.02)\n * @param options.increment - AF increment per new extreme (default: 0.02)\n * @param options.max - Maximum acceleration factor (default: 0.2)\n * @returns Generator yielding PSARPoint objects with `psar` value and `isUptrend` flag\n */\nexport const psar = createSignal(\n ({ start, increment, max }) => {\n assert(start > 0, new RangeError(`Expected start to be positive, got ${start}`))\n assert(increment > 0, new RangeError(`Expected increment to be positive, got ${increment}`))\n assert(max > 0 && max >= start, new RangeError(`Expected max to be positive and >= start, got ${max}`))\n\n let count = 0\n let isUptrend = true\n let sar: Dnum\n let ep: Dnum\n let af: Dnum\n let prevHigh: Dnum\n let prevLow: Dnum\n let prevPrevHigh: Dnum\n let prevPrevLow: Dnum\n\n const afStart = from(start, 18)\n const afIncrement = from(increment, 18)\n const afMax = from(max, 18)\n\n return (bar: RequiredProperties<CandleData, 'h' | 'l'>) => {\n const h = from(bar.h, 18)\n const l = from(bar.l, 18)\n count++\n\n // Bar 1: initialization\n if (count === 1) {\n isUptrend = true\n sar = l\n ep = h\n af = afStart\n prevHigh = h\n prevLow = l\n prevPrevHigh = h\n prevPrevLow = l\n return { psar: sar, isUptrend }\n }\n\n // Bar 2: determine initial trend\n if (count === 2) {\n if (gt(h, prevHigh)) {\n isUptrend = true\n sar = prevLow\n ep = h\n }\n else {\n isUptrend = false\n sar = prevHigh\n ep = l\n }\n af = afStart\n prevPrevHigh = prevHigh\n prevPrevLow = prevLow\n prevHigh = h\n prevLow = l\n return { psar: sar, isUptrend }\n }\n\n // Bar 3+: standard computation\n // Step A: calculate next SAR\n let nextSar = add(sar, mul(af, sub(ep, sar), 18))\n\n // Step B: clamp SAR\n if (isUptrend) {\n if (gt(nextSar, prevLow))\n nextSar = prevLow\n if (gt(nextSar, prevPrevLow))\n nextSar = prevPrevLow\n }\n else {\n if (lt(nextSar, prevHigh))\n nextSar = prevHigh\n if (lt(nextSar, prevPrevHigh))\n nextSar = prevPrevHigh\n }\n\n sar = nextSar\n\n // Step C: check for reversal\n let reversed = false\n if (isUptrend && lt(l, sar)) {\n isUptrend = false\n sar = ep\n ep = l\n af = afStart\n reversed = true\n }\n else if (!isUptrend && gt(h, sar)) {\n isUptrend = true\n sar = ep\n ep = h\n af = afStart\n reversed = true\n }\n\n // Step D: update EP and AF (only if no reversal)\n if (!reversed) {\n if (isUptrend && gt(h, ep)) {\n ep = h\n const newAf = add(af, afIncrement)\n af = gt(newAf, afMax) ? afMax : newAf\n }\n else if (!isUptrend && lt(l, ep)) {\n ep = l\n const newAf = add(af, afIncrement)\n af = gt(newAf, afMax) ? afMax : newAf\n }\n }\n\n // Step E: update prev tracking\n prevPrevHigh = prevHigh\n prevPrevLow = prevLow\n prevHigh = h\n prevLow = l\n\n return { psar: sar, isUptrend }\n }\n },\n defaultParabolicSarOptions,\n)\n\nexport { psar as parabolicSar }\n","import type { Numberish } from 'dnum'\nimport { assert, createSignal } from '@vulcan-js/core'\nimport { sma } from './simpleMovingAverage'\n\nexport interface TriangularMovingAverageOptions {\n period: number\n}\n\nexport const defaultTriangularMovingAverageOptions: TriangularMovingAverageOptions = {\n period: 4,\n}\n\nexport const trima = createSignal(\n ({ period }) => {\n assert(Number.isInteger(period) && period >= 1, new RangeError(`Expected period to be a positive integer, got ${period}`))\n let n1: number\n let n2: number\n\n if (period % 2 === 0) {\n n1 = period / 2\n n2 = n1 + 1\n }\n else {\n n1 = (period + 1) / 2\n n2 = n1\n }\n\n const sma1 = sma.create({ period: n2 })\n const sma2 = sma.create({ period: n1 })\n\n return (value: Numberish) => {\n const s1 = sma1(value)\n return sma2(s1)\n }\n },\n defaultTriangularMovingAverageOptions,\n)\n\nexport { trima as triangularMovingAverage }\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,8 +1,25 @@
1
1
  {
2
2
  "name": "@vulcan-js/indicators",
3
3
  "type": "module",
4
- "version": "0.0.0",
4
+ "version": "0.0.2",
5
+ "description": "Technical analysis indicators with generator-based streaming and high-precision arithmetic",
5
6
  "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/material-tech/vulcan",
10
+ "directory": "packages/indicators"
11
+ },
12
+ "keywords": [
13
+ "technical-analysis",
14
+ "indicators",
15
+ "sma",
16
+ "ema",
17
+ "rsi",
18
+ "macd",
19
+ "trading",
20
+ "generator",
21
+ "streaming"
22
+ ],
6
23
  "exports": {
7
24
  ".": "./dist/index.js",
8
25
  "./package.json": "./package.json"
@@ -13,7 +30,7 @@
13
30
  ],
14
31
  "dependencies": {
15
32
  "dnum": "^2.17.0",
16
- "@vulcan-js/core": "^0.0.0"
33
+ "@vulcan-js/core": "^0.0.2"
17
34
  },
18
35
  "devDependencies": {
19
36
  "defu": "^6.1.4",