@invinite-org/chartlang-runtime 1.1.1 → 1.3.0
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/CHANGELOG.md +307 -0
- package/dist/barPoint.d.ts +20 -0
- package/dist/barPoint.d.ts.map +1 -0
- package/dist/barPoint.js +72 -0
- package/dist/barPoint.js.map +1 -0
- package/dist/bufferSnapshot.d.ts +102 -0
- package/dist/bufferSnapshot.d.ts.map +1 -0
- package/dist/bufferSnapshot.js +119 -0
- package/dist/bufferSnapshot.js.map +1 -0
- package/dist/buildComputeContext.d.ts.map +1 -1
- package/dist/buildComputeContext.js +6 -1
- package/dist/buildComputeContext.js.map +1 -1
- package/dist/createScriptRunner.d.ts +6 -3
- package/dist/createScriptRunner.d.ts.map +1 -1
- package/dist/createScriptRunner.js +65 -11
- package/dist/createScriptRunner.js.map +1 -1
- package/dist/dep/DepRunner.d.ts +7 -0
- package/dist/dep/DepRunner.d.ts.map +1 -1
- package/dist/dep/DepRunner.js +4 -0
- package/dist/dep/DepRunner.js.map +1 -1
- package/dist/emit/barcolor.d.ts +44 -0
- package/dist/emit/barcolor.d.ts.map +1 -0
- package/dist/emit/barcolor.js +40 -0
- package/dist/emit/barcolor.js.map +1 -0
- package/dist/emit/bgcolor.d.ts +44 -0
- package/dist/emit/bgcolor.d.ts.map +1 -0
- package/dist/emit/bgcolor.js +45 -0
- package/dist/emit/bgcolor.js.map +1 -0
- package/dist/emit/draw/boxes/fillBetween.d.ts +45 -0
- package/dist/emit/draw/boxes/fillBetween.d.ts.map +1 -0
- package/dist/emit/draw/boxes/fillBetween.js +36 -0
- package/dist/emit/draw/boxes/fillBetween.js.map +1 -0
- package/dist/emit/draw/handle.d.ts +9 -0
- package/dist/emit/draw/handle.d.ts.map +1 -1
- package/dist/emit/draw/handle.js +65 -10
- package/dist/emit/draw/handle.js.map +1 -1
- package/dist/emit/draw/namespace.d.ts +4 -3
- package/dist/emit/draw/namespace.d.ts.map +1 -1
- package/dist/emit/draw/namespace.js +6 -3
- package/dist/emit/draw/namespace.js.map +1 -1
- package/dist/emit/index.d.ts +2 -0
- package/dist/emit/index.d.ts.map +1 -1
- package/dist/emit/index.js +2 -0
- package/dist/emit/index.js.map +1 -1
- package/dist/emit/plot.d.ts +30 -1
- package/dist/emit/plot.d.ts.map +1 -1
- package/dist/emit/plot.js +45 -1
- package/dist/emit/plot.js.map +1 -1
- package/dist/execution/dispose.d.ts.map +1 -1
- package/dist/execution/dispose.js +18 -0
- package/dist/execution/dispose.js.map +1 -1
- package/dist/execution/runComputeStep.d.ts.map +1 -1
- package/dist/execution/runComputeStep.js +12 -1
- package/dist/execution/runComputeStep.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/inputs/resolveInputs.js +1 -0
- package/dist/inputs/resolveInputs.js.map +1 -1
- package/dist/persistentStateStore.runtime.d.ts.map +1 -1
- package/dist/persistentStateStore.runtime.js +26 -7
- package/dist/persistentStateStore.runtime.js.map +1 -1
- package/dist/primitives.d.ts +1 -1
- package/dist/primitives.d.ts.map +1 -1
- package/dist/primitives.js +7 -1
- package/dist/primitives.js.map +1 -1
- package/dist/request/index.d.ts +2 -1
- package/dist/request/index.d.ts.map +1 -1
- package/dist/request/index.js +2 -1
- package/dist/request/index.js.map +1 -1
- package/dist/request/lowerTf.d.ts.map +1 -1
- package/dist/request/lowerTf.js +6 -0
- package/dist/request/lowerTf.js.map +1 -1
- package/dist/request/requestNamespace.d.ts.map +1 -1
- package/dist/request/requestNamespace.js +30 -3
- package/dist/request/requestNamespace.js.map +1 -1
- package/dist/request/security.d.ts +40 -4
- package/dist/request/security.d.ts.map +1 -1
- package/dist/request/security.js +114 -40
- package/dist/request/security.js.map +1 -1
- package/dist/request/securityExprRunner.d.ts +137 -0
- package/dist/request/securityExprRunner.d.ts.map +1 -0
- package/dist/request/securityExprRunner.js +253 -0
- package/dist/request/securityExprRunner.js.map +1 -0
- package/dist/request/streamBars.d.ts +14 -1
- package/dist/request/streamBars.d.ts.map +1 -1
- package/dist/request/streamBars.js +39 -1
- package/dist/request/streamBars.js.map +1 -1
- package/dist/ringBuffer.d.ts +19 -0
- package/dist/ringBuffer.d.ts.map +1 -1
- package/dist/ringBuffer.js +23 -0
- package/dist/ringBuffer.js.map +1 -1
- package/dist/runtimeContext.d.ts +90 -5
- package/dist/runtimeContext.d.ts.map +1 -1
- package/dist/runtimeContext.js.map +1 -1
- package/dist/seriesView.d.ts +42 -17
- package/dist/seriesView.d.ts.map +1 -1
- package/dist/seriesView.js +65 -42
- package/dist/seriesView.js.map +1 -1
- package/dist/state/arrayPersistence.d.ts +48 -0
- package/dist/state/arrayPersistence.d.ts.map +1 -0
- package/dist/state/arrayPersistence.js +88 -0
- package/dist/state/arrayPersistence.js.map +1 -0
- package/dist/state/arrayStateSlot.d.ts +78 -0
- package/dist/state/arrayStateSlot.d.ts.map +1 -0
- package/dist/state/arrayStateSlot.js +116 -0
- package/dist/state/arrayStateSlot.js.map +1 -0
- package/dist/state/index.d.ts +4 -1
- package/dist/state/index.d.ts.map +1 -1
- package/dist/state/index.js +4 -1
- package/dist/state/index.js.map +1 -1
- package/dist/state/lifecycle.d.ts +68 -0
- package/dist/state/lifecycle.d.ts.map +1 -1
- package/dist/state/lifecycle.js +89 -0
- package/dist/state/lifecycle.js.map +1 -1
- package/dist/state/seriesPersistence.d.ts +48 -0
- package/dist/state/seriesPersistence.d.ts.map +1 -0
- package/dist/state/seriesPersistence.js +87 -0
- package/dist/state/seriesPersistence.js.map +1 -0
- package/dist/state/seriesSlot.d.ts +105 -0
- package/dist/state/seriesSlot.d.ts.map +1 -0
- package/dist/state/seriesSlot.js +123 -0
- package/dist/state/seriesSlot.js.map +1 -0
- package/dist/state/stateNamespace.d.ts.map +1 -1
- package/dist/state/stateNamespace.js +55 -0
- package/dist/state/stateNamespace.js.map +1 -1
- package/dist/streamState.d.ts +25 -19
- package/dist/streamState.d.ts.map +1 -1
- package/dist/streamState.js +40 -66
- package/dist/streamState.js.map +1 -1
- package/dist/ta/adx.d.ts +3 -2
- package/dist/ta/adx.d.ts.map +1 -1
- package/dist/ta/adx.js +3 -2
- package/dist/ta/adx.js.map +1 -1
- package/dist/ta/alma.d.ts +6 -4
- package/dist/ta/alma.d.ts.map +1 -1
- package/dist/ta/alma.js +19 -6
- package/dist/ta/alma.js.map +1 -1
- package/dist/ta/atr.d.ts +3 -2
- package/dist/ta/atr.d.ts.map +1 -1
- package/dist/ta/atr.js +3 -2
- package/dist/ta/atr.js.map +1 -1
- package/dist/ta/bb.d.ts +3 -2
- package/dist/ta/bb.d.ts.map +1 -1
- package/dist/ta/bb.js +3 -2
- package/dist/ta/bb.js.map +1 -1
- package/dist/ta/chaikinOsc.d.ts +3 -2
- package/dist/ta/chaikinOsc.d.ts.map +1 -1
- package/dist/ta/chaikinOsc.js +3 -2
- package/dist/ta/chaikinOsc.js.map +1 -1
- package/dist/ta/crossover.d.ts +3 -2
- package/dist/ta/crossover.d.ts.map +1 -1
- package/dist/ta/crossover.js +3 -2
- package/dist/ta/crossover.js.map +1 -1
- package/dist/ta/crossunder.d.ts +3 -2
- package/dist/ta/crossunder.d.ts.map +1 -1
- package/dist/ta/crossunder.js +3 -2
- package/dist/ta/crossunder.js.map +1 -1
- package/dist/ta/dmi.d.ts +4 -3
- package/dist/ta/dmi.d.ts.map +1 -1
- package/dist/ta/dmi.js +4 -3
- package/dist/ta/dmi.js.map +1 -1
- package/dist/ta/ema.d.ts +3 -2
- package/dist/ta/ema.d.ts.map +1 -1
- package/dist/ta/ema.js +3 -2
- package/dist/ta/ema.js.map +1 -1
- package/dist/ta/eom.d.ts +3 -1
- package/dist/ta/eom.d.ts.map +1 -1
- package/dist/ta/eom.js +3 -1
- package/dist/ta/eom.js.map +1 -1
- package/dist/ta/highestbars.d.ts +25 -0
- package/dist/ta/highestbars.d.ts.map +1 -0
- package/dist/ta/highestbars.js +106 -0
- package/dist/ta/highestbars.js.map +1 -0
- package/dist/ta/historicalVolatility.d.ts +3 -2
- package/dist/ta/historicalVolatility.d.ts.map +1 -1
- package/dist/ta/historicalVolatility.js +3 -2
- package/dist/ta/historicalVolatility.js.map +1 -1
- package/dist/ta/ichimoku.d.ts +3 -1
- package/dist/ta/ichimoku.d.ts.map +1 -1
- package/dist/ta/ichimoku.js +3 -1
- package/dist/ta/ichimoku.js.map +1 -1
- package/dist/ta/lowestbars.d.ts +25 -0
- package/dist/ta/lowestbars.d.ts.map +1 -0
- package/dist/ta/lowestbars.js +102 -0
- package/dist/ta/lowestbars.js.map +1 -0
- package/dist/ta/macd.d.ts +3 -2
- package/dist/ta/macd.d.ts.map +1 -1
- package/dist/ta/macd.js +3 -2
- package/dist/ta/macd.js.map +1 -1
- package/dist/ta/massIndex.d.ts +3 -2
- package/dist/ta/massIndex.d.ts.map +1 -1
- package/dist/ta/massIndex.js +3 -2
- package/dist/ta/massIndex.js.map +1 -1
- package/dist/ta/mfi.d.ts +3 -1
- package/dist/ta/mfi.d.ts.map +1 -1
- package/dist/ta/mfi.js +3 -1
- package/dist/ta/mfi.js.map +1 -1
- package/dist/ta/netVolume.d.ts +3 -1
- package/dist/ta/netVolume.d.ts.map +1 -1
- package/dist/ta/netVolume.js +3 -1
- package/dist/ta/netVolume.js.map +1 -1
- package/dist/ta/nvi.d.ts +3 -1
- package/dist/ta/nvi.d.ts.map +1 -1
- package/dist/ta/nvi.js +3 -1
- package/dist/ta/nvi.js.map +1 -1
- package/dist/ta/persistence.d.ts.map +1 -1
- package/dist/ta/persistence.js +1 -40
- package/dist/ta/persistence.js.map +1 -1
- package/dist/ta/ppo.d.ts +3 -2
- package/dist/ta/ppo.d.ts.map +1 -1
- package/dist/ta/ppo.js +3 -2
- package/dist/ta/ppo.js.map +1 -1
- package/dist/ta/pvi.d.ts +3 -1
- package/dist/ta/pvi.d.ts.map +1 -1
- package/dist/ta/pvi.js +3 -1
- package/dist/ta/pvi.js.map +1 -1
- package/dist/ta/pvo.d.ts +3 -1
- package/dist/ta/pvo.d.ts.map +1 -1
- package/dist/ta/pvo.js +3 -1
- package/dist/ta/pvo.js.map +1 -1
- package/dist/ta/pvt.d.ts +3 -1
- package/dist/ta/pvt.d.ts.map +1 -1
- package/dist/ta/pvt.js +3 -1
- package/dist/ta/pvt.js.map +1 -1
- package/dist/ta/registry.d.ts +7 -1
- package/dist/ta/registry.d.ts.map +1 -1
- package/dist/ta/registry.js +4 -0
- package/dist/ta/registry.js.map +1 -1
- package/dist/ta/rsi.d.ts +3 -2
- package/dist/ta/rsi.d.ts.map +1 -1
- package/dist/ta/rsi.js +3 -2
- package/dist/ta/rsi.js.map +1 -1
- package/dist/ta/rvi.d.ts +3 -2
- package/dist/ta/rvi.d.ts.map +1 -1
- package/dist/ta/rvi.js +3 -2
- package/dist/ta/rvi.js.map +1 -1
- package/dist/ta/sessionVolumeProfile.d.ts.map +1 -1
- package/dist/ta/sessionVolumeProfile.js +1 -17
- package/dist/ta/sessionVolumeProfile.js.map +1 -1
- package/dist/ta/sma.d.ts +6 -3
- package/dist/ta/sma.d.ts.map +1 -1
- package/dist/ta/sma.js +6 -3
- package/dist/ta/sma.js.map +1 -1
- package/dist/ta/stdev.d.ts +3 -2
- package/dist/ta/stdev.d.ts.map +1 -1
- package/dist/ta/stdev.js +3 -2
- package/dist/ta/stdev.js.map +1 -1
- package/dist/ta/trendStrengthIndex.d.ts +3 -2
- package/dist/ta/trendStrengthIndex.d.ts.map +1 -1
- package/dist/ta/trendStrengthIndex.js +3 -2
- package/dist/ta/trendStrengthIndex.js.map +1 -1
- package/dist/ta/trix.d.ts +4 -3
- package/dist/ta/trix.d.ts.map +1 -1
- package/dist/ta/trix.js +4 -3
- package/dist/ta/trix.js.map +1 -1
- package/dist/ta/vortex.d.ts +3 -2
- package/dist/ta/vortex.d.ts.map +1 -1
- package/dist/ta/vortex.js +3 -2
- package/dist/ta/vortex.js.map +1 -1
- package/dist/time-accessors/civil.d.ts +73 -0
- package/dist/time-accessors/civil.d.ts.map +1 -0
- package/dist/time-accessors/civil.js +105 -0
- package/dist/time-accessors/civil.js.map +1 -0
- package/dist/time-accessors/index.d.ts +8 -0
- package/dist/time-accessors/index.d.ts.map +1 -0
- package/dist/time-accessors/index.js +9 -0
- package/dist/time-accessors/index.js.map +1 -0
- package/dist/time-accessors/sessionAccessors.d.ts +50 -0
- package/dist/time-accessors/sessionAccessors.d.ts.map +1 -0
- package/dist/time-accessors/sessionAccessors.js +79 -0
- package/dist/time-accessors/sessionAccessors.js.map +1 -0
- package/dist/time-accessors/sessionWindow.d.ts +17 -0
- package/dist/time-accessors/sessionWindow.d.ts.map +1 -0
- package/dist/time-accessors/sessionWindow.js +41 -0
- package/dist/time-accessors/sessionWindow.js.map +1 -0
- package/dist/time-accessors/timeAccessors.d.ts +54 -0
- package/dist/time-accessors/timeAccessors.d.ts.map +1 -0
- package/dist/time-accessors/timeAccessors.js +132 -0
- package/dist/time-accessors/timeAccessors.js.map +1 -0
- package/dist/time-accessors/tzDiagnostic.d.ts +17 -0
- package/dist/time-accessors/tzDiagnostic.d.ts.map +1 -0
- package/dist/time-accessors/tzDiagnostic.js +34 -0
- package/dist/time-accessors/tzDiagnostic.js.map +1 -0
- package/dist/time-accessors/tzOffset.d.ts +31 -0
- package/dist/time-accessors/tzOffset.d.ts.map +1 -0
- package/dist/time-accessors/tzOffset.js +67 -0
- package/dist/time-accessors/tzOffset.js.map +1 -0
- package/package.json +3 -3
- package/dist/ta/lib/applyOffset.d.ts +0 -19
- package/dist/ta/lib/applyOffset.d.ts.map +0 -1
- package/dist/ta/lib/applyOffset.js +0 -38
- package/dist/ta/lib/applyOffset.js.map +0 -1
package/dist/ta/macd.js
CHANGED
|
@@ -72,8 +72,9 @@ function resultForOffset(slot, offset) {
|
|
|
72
72
|
* @since 0.1
|
|
73
73
|
* @stable
|
|
74
74
|
*
|
|
75
|
-
* `opts.offset`
|
|
76
|
-
* `
|
|
75
|
+
* `opts.offset` is a presentation display shift carried to the plot
|
|
76
|
+
* emission as `xShift` for all three outputs in lockstep (`+n` right / future,
|
|
77
|
+
* `−n` left / past); the series values are unshifted.
|
|
77
78
|
*
|
|
78
79
|
* @example
|
|
79
80
|
* // import { ta } from "@invinite-org/chartlang-runtime";
|
package/dist/ta/macd.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"macd.js","sourceRoot":"","sources":["../../src/ta/macd.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAC/D,EAAE;AACF,uEAAuE;AACvE,wCAAwC;AACxC,mEAAmE;AACnE,qEAAqE;AACrE,gBAAgB;AAChB,qEAAqE;AACrE,4DAA4D;AAC5D,+DAA+D;AAC/D,oEAAoE;AACpE,cAAc;AAId,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,sBAAsB,EAAuB,MAAM,sBAAsB,CAAC;AACnF,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAAuB,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE5E,MAAM,YAAY,GAAG,EAAE,CAAC;AACxB,MAAM,YAAY,GAAG,EAAE,CAAC;AACxB,MAAM,cAAc,GAAG,CAAC,CAAC;AAqBzB,SAAS,MAAM;IACX,MAAM,GAAG,GAAG,sBAAsB,CAAC,OAAO,CAAC;IAC3C,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAED,SAAS,QAAQ,CACb,QAAgB,EAChB,YAA4B,EAC5B,SAA4B;IAE5B,MAAM,OAAO,GAAG,IAAI,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,IAAI,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAChD,OAAO;QACH,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC;YAClB,IAAI,EAAE,cAAc,CAAS,OAAO,CAAC;YACrC,MAAM,EAAE,YAAY;YACpB,IAAI,EAAE,cAAc,CAAS,OAAO,CAAC;SACxC,CAAC;QACF,OAAO;QACP,OAAO;QACP,SAAS;QACT,cAAc,EAAE,IAAI,GAAG,EAAE;KAC5B,CAAC;AACN,CAAC;AAED,SAAS,eAAe,CAAC,IAAc,EAAE,MAAc;IACnD,IAAI,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC,MAAM,CAAC;IACrC,IAAI,MAAM,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC7C,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACvB,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;YACnB,IAAI,EAAE,qBAAqB,CAAS,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC;YACzD,MAAM,EAAE,qBAAqB,CAAS,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC;YAC7D,IAAI,EAAE,qBAAqB,CAAS,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC;SAC5D,CAAC,CAAC;QACH,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,MAAM,CAAC;AAClB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,UAAU,IAAI,CAAC,MAAc,EAAE,MAAsB,EAAE,IAAe;IACxE,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;IACrB,MAAM,UAAU,GAAG,IAAI,EAAE,UAAU,IAAI,YAAY,CAAC;IACpD,MAAM,UAAU,GAAG,IAAI,EAAE,UAAU,IAAI,YAAY,CAAC;IACpD,MAAM,YAAY,GAAG,IAAI,EAAE,YAAY,IAAI,cAAc,CAAC;IAC1D,MAAM,MAAM,GAAG,IAAI,EAAE,MAAM,IAAI,CAAC,CAAC;IACjC,MAAM,YAAY,GAAG,GAAG,MAAM,SAAS,CAAC;IACxC,MAAM,GAAG,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IACpC,MAAM,UAAU,GAAG,GAAG,CAAC,GAAG,MAAM,OAAO,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;IAC1D,MAAM,UAAU,GAAG,GAAG,CAAC,GAAG,MAAM,OAAO,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;IAC1D,MAAM,EAAE,GAAG,UAAU,CAAC,OAAO,CAAC;IAC9B,MAAM,EAAE,GAAG,UAAU,CAAC,OAAO,CAAC;IAC9B,MAAM,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;IACpF,2DAA2D;IAC3D,gEAAgE;IAChE,kEAAkE;IAClE,4DAA4D;IAC5D,MAAM,YAAY,GAAG,GAAG,CAAC,YAAY,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;IAEhE,IAAI,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAyB,CAAC;IAClE,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACrB,0DAA0D;QAC1D,8DAA8D;QAC9D,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAqC,CAAC;QACzF,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,EAAE,YAAY,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;QAClF,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACzC,CAAC;IACD,MAAM,GAAG,GAAG,YAAY,CAAC,OAAO,CAAC;IACjC,MAAM,SAAS,GACX,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;IACtF,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;QACb,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QACpC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;IACxC,CAAC;SAAM,CAAC;QACJ,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC/B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AACzC,CAAC","sourcesContent":["// Copyright (c) 2026 Invinite. Licensed under the MIT License.\n// See the LICENSE file in the repo root for full license text.\n//\n// Ported from invinite/src/components/trading-chart/indicators/macd.ts\n// (folded onto lib/ema-of-float64.ts)\n// (commit d2d1043c1b039f66d2f3674526d303d31cf2f1e0, © Invinite).\n// Re-licensed MIT for chartlang. The math is the reference, the code\n// style is not.\n// Structural choices (callsite-id slot, Series<T> proxy, replaceHead\n// mode) follow chartlang's primitive shape — NOT invinite's\n// IndicatorPlugin shape. The MACD primitive composes three EMA\n// sub-slots and a virtual \"MACD line\" Float64 buffer the signal EMA\n// reads from.\n\nimport type { MacdOpts, MacdResult, Series } from \"@invinite-org/chartlang-core\";\n\nimport { Float64RingBuffer } from \"../ringBuffer.js\";\nimport { ACTIVE_RUNTIME_CONTEXT, type RuntimeContext } from \"../runtimeContext.js\";\nimport { makeSeriesView, makeShiftedSeriesView } from \"../seriesView.js\";\nimport { ema } from \"./ema.js\";\nimport { type ScalarOrSeries, readSourceValue } from \"./lib/sourceValue.js\";\n\nconst DEFAULT_FAST = 12;\nconst DEFAULT_SLOW = 26;\nconst DEFAULT_SIGNAL = 9;\n\ntype MacdSlot = {\n readonly result: MacdResult;\n readonly macdBuf: Float64RingBuffer;\n readonly histBuf: Float64RingBuffer;\n /**\n * Reference to the signal-EMA sub-slot's output ring buffer.\n * Captured at first call so per-offset shifted signal views can be\n * constructed without re-entering `ema()` (which would double-\n * advance the sub-slot's compute on every bar).\n */\n readonly signalBuf: Float64RingBuffer;\n /**\n * Per-offset frozen `MacdResult` cache. `offset === 0` returns\n * `result` directly (identity-preserving). Each cached result\n * proxies the same three underlying outputs via shifted views.\n */\n readonly shiftedResults: Map<number, MacdResult>;\n};\n\nfunction getCtx(): RuntimeContext {\n const ctx = ACTIVE_RUNTIME_CONTEXT.current;\n if (ctx === null) {\n throw new Error(\"ta.macd called outside an active script step\");\n }\n return ctx;\n}\n\nfunction initSlot(\n capacity: number,\n signalSeries: Series<number>,\n signalBuf: Float64RingBuffer,\n): MacdSlot {\n const macdBuf = new Float64RingBuffer(capacity);\n const histBuf = new Float64RingBuffer(capacity);\n return {\n result: Object.freeze({\n macd: makeSeriesView<number>(macdBuf),\n signal: signalSeries,\n hist: makeSeriesView<number>(histBuf),\n }),\n macdBuf,\n histBuf,\n signalBuf,\n shiftedResults: new Map(),\n };\n}\n\nfunction resultForOffset(slot: MacdSlot, offset: number): MacdResult {\n if (offset === 0) return slot.result;\n let cached = slot.shiftedResults.get(offset);\n if (cached === undefined) {\n cached = Object.freeze({\n macd: makeShiftedSeriesView<number>(slot.macdBuf, offset),\n signal: makeShiftedSeriesView<number>(slot.signalBuf, offset),\n hist: makeShiftedSeriesView<number>(slot.histBuf, offset),\n });\n slot.shiftedResults.set(offset, cached);\n }\n return cached;\n}\n\n/**\n * MACD — fast EMA minus slow EMA, with a signal-line EMA over the\n * MACD line and a histogram of their difference. Defaults\n * `{ fastLength: 12, slowLength: 26, signalLength: 9 }`. Composes\n * three EMA primitives at sub-slots `${slotId}/fast`, `${slotId}/slow`,\n * `${slotId}/signal`. The signal EMA reads from an internal MACD\n * Float64 ring; the user-facing `macd` Series wraps the same buffer.\n *\n * @formula fast = ema(source, fastLength) ;\n * slow = ema(source, slowLength) ;\n * macd = fast − slow ;\n * signal = ema(macd, signalLength) ;\n * hist = macd − signal\n * @warmup slowLength + signalLength − 1 (slow EMA seeds at slowLength − 1; signal EMA seeds signalLength − 1 bars after that)\n * @since 0.1\n * @stable\n *\n * `opts.offset` shifts all three outputs in lockstep —\n * `series.current` on each output returns the value `offset` bars ago.\n *\n * @example\n * // import { ta } from \"@invinite-org/chartlang-runtime\";\n * // const m = ta.macd(\"slot\", bar.close);\n * // const h = m.hist.current;\n * // const lagged = ta.macd(\"slot2\", bar.close, { offset: 5 });\n */\nexport function macd(slotId: string, source: ScalarOrSeries, opts?: MacdOpts): MacdResult {\n const ctx = getCtx();\n const fastLength = opts?.fastLength ?? DEFAULT_FAST;\n const slowLength = opts?.slowLength ?? DEFAULT_SLOW;\n const signalLength = opts?.signalLength ?? DEFAULT_SIGNAL;\n const offset = opts?.offset ?? 0;\n const signalSlotId = `${slotId}/signal`;\n const src = readSourceValue(source);\n const fastSeries = ema(`${slotId}/fast`, src, fastLength);\n const slowSeries = ema(`${slotId}/slow`, src, slowLength);\n const fa = fastSeries.current;\n const sa = slowSeries.current;\n const macdValue = Number.isFinite(fa) && Number.isFinite(sa) ? fa - sa : Number.NaN;\n // Feed macdValue into the signal EMA. Always call with the\n // un-shifted (default) view — offset shifting for the composite\n // MacdResult happens via the local `resultForOffset`, which reads\n // directly off the signal-EMA's outBuffer (captured below).\n const signalSeries = ema(signalSlotId, macdValue, signalLength);\n\n let slot = ctx.stream.taSlots.get(slotId) as MacdSlot | undefined;\n if (slot === undefined) {\n // Capture the signal-EMA sub-slot's output ring buffer so\n // future shifted-view lookups don't need to re-enter `ema()`.\n const emaSlot = ctx.stream.taSlots.get(signalSlotId) as { outBuffer: Float64RingBuffer };\n slot = initSlot(ctx.stream.ohlcv.close.capacity, signalSeries, emaSlot.outBuffer);\n ctx.stream.taSlots.set(slotId, slot);\n }\n const sig = signalSeries.current;\n const histValue =\n Number.isFinite(macdValue) && Number.isFinite(sig) ? macdValue - sig : Number.NaN;\n if (ctx.isTick) {\n slot.macdBuf.replaceHead(macdValue);\n slot.histBuf.replaceHead(histValue);\n } else {\n slot.macdBuf.append(macdValue);\n slot.histBuf.append(histValue);\n }\n return resultForOffset(slot, offset);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"macd.js","sourceRoot":"","sources":["../../src/ta/macd.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAC/D,EAAE;AACF,uEAAuE;AACvE,wCAAwC;AACxC,mEAAmE;AACnE,qEAAqE;AACrE,gBAAgB;AAChB,qEAAqE;AACrE,4DAA4D;AAC5D,+DAA+D;AAC/D,oEAAoE;AACpE,cAAc;AAId,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,sBAAsB,EAAuB,MAAM,sBAAsB,CAAC;AACnF,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAAuB,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE5E,MAAM,YAAY,GAAG,EAAE,CAAC;AACxB,MAAM,YAAY,GAAG,EAAE,CAAC;AACxB,MAAM,cAAc,GAAG,CAAC,CAAC;AAqBzB,SAAS,MAAM;IACX,MAAM,GAAG,GAAG,sBAAsB,CAAC,OAAO,CAAC;IAC3C,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAED,SAAS,QAAQ,CACb,QAAgB,EAChB,YAA4B,EAC5B,SAA4B;IAE5B,MAAM,OAAO,GAAG,IAAI,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,IAAI,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAChD,OAAO;QACH,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC;YAClB,IAAI,EAAE,cAAc,CAAS,OAAO,CAAC;YACrC,MAAM,EAAE,YAAY;YACpB,IAAI,EAAE,cAAc,CAAS,OAAO,CAAC;SACxC,CAAC;QACF,OAAO;QACP,OAAO;QACP,SAAS;QACT,cAAc,EAAE,IAAI,GAAG,EAAE;KAC5B,CAAC;AACN,CAAC;AAED,SAAS,eAAe,CAAC,IAAc,EAAE,MAAc;IACnD,IAAI,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC,MAAM,CAAC;IACrC,IAAI,MAAM,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC7C,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACvB,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;YACnB,IAAI,EAAE,qBAAqB,CAAS,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC;YACzD,MAAM,EAAE,qBAAqB,CAAS,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC;YAC7D,IAAI,EAAE,qBAAqB,CAAS,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC;SAC5D,CAAC,CAAC;QACH,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,MAAM,CAAC;AAClB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,UAAU,IAAI,CAAC,MAAc,EAAE,MAAsB,EAAE,IAAe;IACxE,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;IACrB,MAAM,UAAU,GAAG,IAAI,EAAE,UAAU,IAAI,YAAY,CAAC;IACpD,MAAM,UAAU,GAAG,IAAI,EAAE,UAAU,IAAI,YAAY,CAAC;IACpD,MAAM,YAAY,GAAG,IAAI,EAAE,YAAY,IAAI,cAAc,CAAC;IAC1D,MAAM,MAAM,GAAG,IAAI,EAAE,MAAM,IAAI,CAAC,CAAC;IACjC,MAAM,YAAY,GAAG,GAAG,MAAM,SAAS,CAAC;IACxC,MAAM,GAAG,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IACpC,MAAM,UAAU,GAAG,GAAG,CAAC,GAAG,MAAM,OAAO,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;IAC1D,MAAM,UAAU,GAAG,GAAG,CAAC,GAAG,MAAM,OAAO,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;IAC1D,MAAM,EAAE,GAAG,UAAU,CAAC,OAAO,CAAC;IAC9B,MAAM,EAAE,GAAG,UAAU,CAAC,OAAO,CAAC;IAC9B,MAAM,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;IACpF,2DAA2D;IAC3D,gEAAgE;IAChE,kEAAkE;IAClE,4DAA4D;IAC5D,MAAM,YAAY,GAAG,GAAG,CAAC,YAAY,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;IAEhE,IAAI,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAyB,CAAC;IAClE,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACrB,0DAA0D;QAC1D,8DAA8D;QAC9D,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAqC,CAAC;QACzF,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,EAAE,YAAY,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;QAClF,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACzC,CAAC;IACD,MAAM,GAAG,GAAG,YAAY,CAAC,OAAO,CAAC;IACjC,MAAM,SAAS,GACX,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;IACtF,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;QACb,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QACpC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;IACxC,CAAC;SAAM,CAAC;QACJ,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC/B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AACzC,CAAC","sourcesContent":["// Copyright (c) 2026 Invinite. Licensed under the MIT License.\n// See the LICENSE file in the repo root for full license text.\n//\n// Ported from invinite/src/components/trading-chart/indicators/macd.ts\n// (folded onto lib/ema-of-float64.ts)\n// (commit d2d1043c1b039f66d2f3674526d303d31cf2f1e0, © Invinite).\n// Re-licensed MIT for chartlang. The math is the reference, the code\n// style is not.\n// Structural choices (callsite-id slot, Series<T> proxy, replaceHead\n// mode) follow chartlang's primitive shape — NOT invinite's\n// IndicatorPlugin shape. The MACD primitive composes three EMA\n// sub-slots and a virtual \"MACD line\" Float64 buffer the signal EMA\n// reads from.\n\nimport type { MacdOpts, MacdResult, Series } from \"@invinite-org/chartlang-core\";\n\nimport { Float64RingBuffer } from \"../ringBuffer.js\";\nimport { ACTIVE_RUNTIME_CONTEXT, type RuntimeContext } from \"../runtimeContext.js\";\nimport { makeSeriesView, makeShiftedSeriesView } from \"../seriesView.js\";\nimport { ema } from \"./ema.js\";\nimport { type ScalarOrSeries, readSourceValue } from \"./lib/sourceValue.js\";\n\nconst DEFAULT_FAST = 12;\nconst DEFAULT_SLOW = 26;\nconst DEFAULT_SIGNAL = 9;\n\ntype MacdSlot = {\n readonly result: MacdResult;\n readonly macdBuf: Float64RingBuffer;\n readonly histBuf: Float64RingBuffer;\n /**\n * Reference to the signal-EMA sub-slot's output ring buffer.\n * Captured at first call so per-offset shifted signal views can be\n * constructed without re-entering `ema()` (which would double-\n * advance the sub-slot's compute on every bar).\n */\n readonly signalBuf: Float64RingBuffer;\n /**\n * Per-offset frozen `MacdResult` cache. `offset === 0` returns\n * `result` directly (identity-preserving). Each cached result\n * proxies the same three underlying outputs via shifted views.\n */\n readonly shiftedResults: Map<number, MacdResult>;\n};\n\nfunction getCtx(): RuntimeContext {\n const ctx = ACTIVE_RUNTIME_CONTEXT.current;\n if (ctx === null) {\n throw new Error(\"ta.macd called outside an active script step\");\n }\n return ctx;\n}\n\nfunction initSlot(\n capacity: number,\n signalSeries: Series<number>,\n signalBuf: Float64RingBuffer,\n): MacdSlot {\n const macdBuf = new Float64RingBuffer(capacity);\n const histBuf = new Float64RingBuffer(capacity);\n return {\n result: Object.freeze({\n macd: makeSeriesView<number>(macdBuf),\n signal: signalSeries,\n hist: makeSeriesView<number>(histBuf),\n }),\n macdBuf,\n histBuf,\n signalBuf,\n shiftedResults: new Map(),\n };\n}\n\nfunction resultForOffset(slot: MacdSlot, offset: number): MacdResult {\n if (offset === 0) return slot.result;\n let cached = slot.shiftedResults.get(offset);\n if (cached === undefined) {\n cached = Object.freeze({\n macd: makeShiftedSeriesView<number>(slot.macdBuf, offset),\n signal: makeShiftedSeriesView<number>(slot.signalBuf, offset),\n hist: makeShiftedSeriesView<number>(slot.histBuf, offset),\n });\n slot.shiftedResults.set(offset, cached);\n }\n return cached;\n}\n\n/**\n * MACD — fast EMA minus slow EMA, with a signal-line EMA over the\n * MACD line and a histogram of their difference. Defaults\n * `{ fastLength: 12, slowLength: 26, signalLength: 9 }`. Composes\n * three EMA primitives at sub-slots `${slotId}/fast`, `${slotId}/slow`,\n * `${slotId}/signal`. The signal EMA reads from an internal MACD\n * Float64 ring; the user-facing `macd` Series wraps the same buffer.\n *\n * @formula fast = ema(source, fastLength) ;\n * slow = ema(source, slowLength) ;\n * macd = fast − slow ;\n * signal = ema(macd, signalLength) ;\n * hist = macd − signal\n * @warmup slowLength + signalLength − 1 (slow EMA seeds at slowLength − 1; signal EMA seeds signalLength − 1 bars after that)\n * @since 0.1\n * @stable\n *\n * `opts.offset` is a presentation display shift carried to the plot\n * emission as `xShift` for all three outputs in lockstep (`+n` right / future,\n * `−n` left / past); the series values are unshifted.\n *\n * @example\n * // import { ta } from \"@invinite-org/chartlang-runtime\";\n * // const m = ta.macd(\"slot\", bar.close);\n * // const h = m.hist.current;\n * // const lagged = ta.macd(\"slot2\", bar.close, { offset: 5 });\n */\nexport function macd(slotId: string, source: ScalarOrSeries, opts?: MacdOpts): MacdResult {\n const ctx = getCtx();\n const fastLength = opts?.fastLength ?? DEFAULT_FAST;\n const slowLength = opts?.slowLength ?? DEFAULT_SLOW;\n const signalLength = opts?.signalLength ?? DEFAULT_SIGNAL;\n const offset = opts?.offset ?? 0;\n const signalSlotId = `${slotId}/signal`;\n const src = readSourceValue(source);\n const fastSeries = ema(`${slotId}/fast`, src, fastLength);\n const slowSeries = ema(`${slotId}/slow`, src, slowLength);\n const fa = fastSeries.current;\n const sa = slowSeries.current;\n const macdValue = Number.isFinite(fa) && Number.isFinite(sa) ? fa - sa : Number.NaN;\n // Feed macdValue into the signal EMA. Always call with the\n // un-shifted (default) view — offset shifting for the composite\n // MacdResult happens via the local `resultForOffset`, which reads\n // directly off the signal-EMA's outBuffer (captured below).\n const signalSeries = ema(signalSlotId, macdValue, signalLength);\n\n let slot = ctx.stream.taSlots.get(slotId) as MacdSlot | undefined;\n if (slot === undefined) {\n // Capture the signal-EMA sub-slot's output ring buffer so\n // future shifted-view lookups don't need to re-enter `ema()`.\n const emaSlot = ctx.stream.taSlots.get(signalSlotId) as { outBuffer: Float64RingBuffer };\n slot = initSlot(ctx.stream.ohlcv.close.capacity, signalSeries, emaSlot.outBuffer);\n ctx.stream.taSlots.set(slotId, slot);\n }\n const sig = signalSeries.current;\n const histValue =\n Number.isFinite(macdValue) && Number.isFinite(sig) ? macdValue - sig : Number.NaN;\n if (ctx.isTick) {\n slot.macdBuf.replaceHead(macdValue);\n slot.histBuf.replaceHead(histValue);\n } else {\n slot.macdBuf.append(macdValue);\n slot.histBuf.append(histValue);\n }\n return resultForOffset(slot, offset);\n}\n"]}
|
package/dist/ta/massIndex.d.ts
CHANGED
|
@@ -17,8 +17,9 @@ import type { MassIndexOpts, Series } from "@invinite-org/chartlang-core";
|
|
|
17
17
|
* @since 0.2
|
|
18
18
|
* @stable
|
|
19
19
|
*
|
|
20
|
-
* `opts.offset`
|
|
21
|
-
*
|
|
20
|
+
* `opts.offset` is a presentation display shift carried to the plot
|
|
21
|
+
* emission as `xShift` (`+n` right / future, `−n` left / past); the
|
|
22
|
+
* series value is unshifted.
|
|
22
23
|
*
|
|
23
24
|
* @example
|
|
24
25
|
* // import { ta } from "@invinite-org/chartlang-core";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"massIndex.d.ts","sourceRoot":"","sources":["../../src/ta/massIndex.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAqG1E
|
|
1
|
+
{"version":3,"file":"massIndex.d.ts","sourceRoot":"","sources":["../../src/ta/massIndex.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAqG1E;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,CAqB9E"}
|
package/dist/ta/massIndex.js
CHANGED
|
@@ -115,8 +115,9 @@ function tickValue(slot, ratio) {
|
|
|
115
115
|
* @since 0.2
|
|
116
116
|
* @stable
|
|
117
117
|
*
|
|
118
|
-
* `opts.offset`
|
|
119
|
-
*
|
|
118
|
+
* `opts.offset` is a presentation display shift carried to the plot
|
|
119
|
+
* emission as `xShift` (`+n` right / future, `−n` left / past); the
|
|
120
|
+
* series value is unshifted.
|
|
120
121
|
*
|
|
121
122
|
* @example
|
|
122
123
|
* // import { ta } from "@invinite-org/chartlang-core";
|
package/dist/ta/massIndex.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"massIndex.js","sourceRoot":"","sources":["../../src/ta/massIndex.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Invinite. Licensed under the MIT License.\n// See the LICENSE file in the repo root for full license text.\n//\n// Ported from invinite/src/components/trading-chart/indicators/mass-index.ts\n// (commit 078f41fe2569d659d5aba726da8bcb5d3e2ced02, © Invinite).\n// Re-licensed MIT for chartlang. The math is the reference, the code\n// style is not.\n// Structural choices (callsite-id slot, Series<T> proxy, replaceHead\n// mode) follow chartlang's primitive shape — NOT invinite's\n// IndicatorPlugin shape. The chained EMA-of-range and EMA-of-EMA\n// arms compose `ta.ema` via sub-slots `${slotId}/ema1` /\n// `${slotId}/ema2` so the EMA recurrence + warmup semantics flow in\n// by reference. The rolling-sum-of-ratio window is folded\n// incrementally per bar.\n\nimport type { MassIndexOpts, Series } from \"@invinite-org/chartlang-core\";\n\nimport { Float64RingBuffer } from \"../ringBuffer.js\";\nimport { ACTIVE_RUNTIME_CONTEXT, type RuntimeContext } from \"../runtimeContext.js\";\nimport { makeSeriesView, makeShiftedSeriesView } from \"../seriesView.js\";\nimport { ema } from \"./ema.js\";\n\nconst DEFAULT_EMA_LENGTH = 9;\nconst DEFAULT_SUM_LENGTH = 25;\n\ntype MassIndexSlot = {\n readonly outBuffer: Float64RingBuffer;\n readonly series: Series<number>;\n readonly emaLength: number;\n readonly sumLength: number;\n readonly ratioWindow: Float64RingBuffer;\n sumRatio: number;\n /** Per-offset Series-view cache; see `sma.ts` for the convention. */\n readonly shiftedViews: Map<number, Series<number>>;\n};\n\nfunction getCtx(): RuntimeContext {\n const ctx = ACTIVE_RUNTIME_CONTEXT.current;\n if (ctx === null) {\n throw new Error(\"ta.massIndex called outside an active script step\");\n }\n return ctx;\n}\n\nfunction initSlot(emaLength: number, sumLength: number, capacity: number): MassIndexSlot {\n const outBuffer = new Float64RingBuffer(capacity);\n return {\n outBuffer,\n series: makeSeriesView<number>(outBuffer),\n emaLength,\n sumLength,\n ratioWindow: new Float64RingBuffer(sumLength),\n sumRatio: 0,\n shiftedViews: new Map(),\n };\n}\n\nfunction viewForOffset(slot: MassIndexSlot, offset: number): Series<number> {\n if (offset === 0) return slot.series;\n let view = slot.shiftedViews.get(offset);\n if (view === undefined) {\n view = makeShiftedSeriesView<number>(slot.outBuffer, offset);\n slot.shiftedViews.set(offset, view);\n }\n return view;\n}\n\nfunction ratioValue(e1: number, e2: number): number {\n if (!Number.isFinite(e1) || !Number.isFinite(e2) || e2 === 0) return Number.NaN;\n return e1 / e2;\n}\n\nfunction recomputeSum(slot: MassIndexSlot): void {\n let sum = 0;\n let anyNaN = false;\n for (let i = 0; i < slot.ratioWindow.length; i += 1) {\n const v = slot.ratioWindow.at(i);\n if (!Number.isFinite(v)) {\n anyNaN = true;\n break;\n }\n sum += v;\n }\n slot.sumRatio = anyNaN ? Number.NaN : sum;\n}\n\nfunction closeValue(slot: MassIndexSlot, ratio: number): number {\n if (slot.ratioWindow.length < slot.ratioWindow.capacity) {\n slot.ratioWindow.append(ratio);\n if (Number.isFinite(ratio)) {\n slot.sumRatio += ratio;\n } else {\n slot.sumRatio = Number.NaN;\n }\n if (slot.ratioWindow.length < slot.ratioWindow.capacity) return Number.NaN;\n return slot.sumRatio;\n }\n const outgoing = slot.ratioWindow.at(slot.ratioWindow.length - 1);\n slot.ratioWindow.append(ratio);\n if (Number.isFinite(outgoing) && Number.isFinite(ratio) && Number.isFinite(slot.sumRatio)) {\n slot.sumRatio = slot.sumRatio - outgoing + ratio;\n } else {\n recomputeSum(slot);\n }\n return slot.sumRatio;\n}\n\nfunction tickValue(slot: MassIndexSlot, ratio: number): number {\n if (slot.ratioWindow.length < slot.ratioWindow.capacity) return Number.NaN;\n const oldestInHead = slot.ratioWindow.at(0);\n // NaN propagates through subtraction/addition — Infinity does too —\n // so a poisoned sum, NaN ratio, or NaN oldestInHead all surface as\n // a non-finite value at the head.\n return slot.sumRatio - oldestInHead + ratio;\n}\n\n/**\n * Mass Index — sub-pane volatility line tracking the range-EMA\n * \"bulge\" ratio to flag trend-reversal setups via the canonical\n * 27 threshold. Built on EMA-of-EMA-of-range via two chained\n * sub-slots (`${slotId}/ema1`, `${slotId}/ema2`) — a fix to EMA's\n * recurrence flows in for free. Reads `bar.high − bar.low` directly\n * (no source param). NaN when either chained EMA is NaN or when\n * the inner EMA of EMA is zero (degenerate ratio).\n *\n * @formula range[t] = high[t] − low[t] ;\n * ema1 = EMA(emaLength)(range) ;\n * ema2 = EMA(emaLength)(ema1) ;\n * ratio[t] = ema1[t] / ema2[t] ;\n * mi[t] = sum(ratio[t − sumLength + 1..= t])\n * @warmup emaLength + emaLength + sumLength − 3\n * @since 0.2\n * @stable\n *\n * `opts.offset` shifts the returned series so `series.current` reads\n * the value `offset` bars ago.\n *\n * @example\n * // import { ta } from \"@invinite-org/chartlang-core\";\n * // const mi = ta.massIndex();\n * // plot(mi);\n */\nexport function massIndex(slotId: string, opts?: MassIndexOpts): Series<number> {\n const ctx = getCtx();\n let slot = ctx.stream.taSlots.get(slotId) as MassIndexSlot | undefined;\n if (slot === undefined) {\n const emaLength = opts?.emaLength ?? DEFAULT_EMA_LENGTH;\n const sumLength = opts?.sumLength ?? DEFAULT_SUM_LENGTH;\n slot = initSlot(emaLength, sumLength, ctx.stream.ohlcv.close.capacity);\n ctx.stream.taSlots.set(slotId, slot);\n }\n const range = ctx.stream.bar.high - ctx.stream.bar.low;\n const ema1Series = ema(`${slotId}/ema1`, range, slot.emaLength);\n const e1 = ema1Series.current;\n const ema2Series = ema(`${slotId}/ema2`, e1, slot.emaLength);\n const e2 = ema2Series.current;\n const ratio = ratioValue(e1, e2);\n if (ctx.isTick) {\n slot.outBuffer.replaceHead(tickValue(slot, ratio));\n } else {\n slot.outBuffer.append(closeValue(slot, ratio));\n }\n return viewForOffset(slot, opts?.offset ?? 0);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"massIndex.js","sourceRoot":"","sources":["../../src/ta/massIndex.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Invinite. Licensed under the MIT License.\n// See the LICENSE file in the repo root for full license text.\n//\n// Ported from invinite/src/components/trading-chart/indicators/mass-index.ts\n// (commit 078f41fe2569d659d5aba726da8bcb5d3e2ced02, © Invinite).\n// Re-licensed MIT for chartlang. The math is the reference, the code\n// style is not.\n// Structural choices (callsite-id slot, Series<T> proxy, replaceHead\n// mode) follow chartlang's primitive shape — NOT invinite's\n// IndicatorPlugin shape. The chained EMA-of-range and EMA-of-EMA\n// arms compose `ta.ema` via sub-slots `${slotId}/ema1` /\n// `${slotId}/ema2` so the EMA recurrence + warmup semantics flow in\n// by reference. The rolling-sum-of-ratio window is folded\n// incrementally per bar.\n\nimport type { MassIndexOpts, Series } from \"@invinite-org/chartlang-core\";\n\nimport { Float64RingBuffer } from \"../ringBuffer.js\";\nimport { ACTIVE_RUNTIME_CONTEXT, type RuntimeContext } from \"../runtimeContext.js\";\nimport { makeSeriesView, makeShiftedSeriesView } from \"../seriesView.js\";\nimport { ema } from \"./ema.js\";\n\nconst DEFAULT_EMA_LENGTH = 9;\nconst DEFAULT_SUM_LENGTH = 25;\n\ntype MassIndexSlot = {\n readonly outBuffer: Float64RingBuffer;\n readonly series: Series<number>;\n readonly emaLength: number;\n readonly sumLength: number;\n readonly ratioWindow: Float64RingBuffer;\n sumRatio: number;\n /** Per-offset Series-view cache; see `sma.ts` for the convention. */\n readonly shiftedViews: Map<number, Series<number>>;\n};\n\nfunction getCtx(): RuntimeContext {\n const ctx = ACTIVE_RUNTIME_CONTEXT.current;\n if (ctx === null) {\n throw new Error(\"ta.massIndex called outside an active script step\");\n }\n return ctx;\n}\n\nfunction initSlot(emaLength: number, sumLength: number, capacity: number): MassIndexSlot {\n const outBuffer = new Float64RingBuffer(capacity);\n return {\n outBuffer,\n series: makeSeriesView<number>(outBuffer),\n emaLength,\n sumLength,\n ratioWindow: new Float64RingBuffer(sumLength),\n sumRatio: 0,\n shiftedViews: new Map(),\n };\n}\n\nfunction viewForOffset(slot: MassIndexSlot, offset: number): Series<number> {\n if (offset === 0) return slot.series;\n let view = slot.shiftedViews.get(offset);\n if (view === undefined) {\n view = makeShiftedSeriesView<number>(slot.outBuffer, offset);\n slot.shiftedViews.set(offset, view);\n }\n return view;\n}\n\nfunction ratioValue(e1: number, e2: number): number {\n if (!Number.isFinite(e1) || !Number.isFinite(e2) || e2 === 0) return Number.NaN;\n return e1 / e2;\n}\n\nfunction recomputeSum(slot: MassIndexSlot): void {\n let sum = 0;\n let anyNaN = false;\n for (let i = 0; i < slot.ratioWindow.length; i += 1) {\n const v = slot.ratioWindow.at(i);\n if (!Number.isFinite(v)) {\n anyNaN = true;\n break;\n }\n sum += v;\n }\n slot.sumRatio = anyNaN ? Number.NaN : sum;\n}\n\nfunction closeValue(slot: MassIndexSlot, ratio: number): number {\n if (slot.ratioWindow.length < slot.ratioWindow.capacity) {\n slot.ratioWindow.append(ratio);\n if (Number.isFinite(ratio)) {\n slot.sumRatio += ratio;\n } else {\n slot.sumRatio = Number.NaN;\n }\n if (slot.ratioWindow.length < slot.ratioWindow.capacity) return Number.NaN;\n return slot.sumRatio;\n }\n const outgoing = slot.ratioWindow.at(slot.ratioWindow.length - 1);\n slot.ratioWindow.append(ratio);\n if (Number.isFinite(outgoing) && Number.isFinite(ratio) && Number.isFinite(slot.sumRatio)) {\n slot.sumRatio = slot.sumRatio - outgoing + ratio;\n } else {\n recomputeSum(slot);\n }\n return slot.sumRatio;\n}\n\nfunction tickValue(slot: MassIndexSlot, ratio: number): number {\n if (slot.ratioWindow.length < slot.ratioWindow.capacity) return Number.NaN;\n const oldestInHead = slot.ratioWindow.at(0);\n // NaN propagates through subtraction/addition — Infinity does too —\n // so a poisoned sum, NaN ratio, or NaN oldestInHead all surface as\n // a non-finite value at the head.\n return slot.sumRatio - oldestInHead + ratio;\n}\n\n/**\n * Mass Index — sub-pane volatility line tracking the range-EMA\n * \"bulge\" ratio to flag trend-reversal setups via the canonical\n * 27 threshold. Built on EMA-of-EMA-of-range via two chained\n * sub-slots (`${slotId}/ema1`, `${slotId}/ema2`) — a fix to EMA's\n * recurrence flows in for free. Reads `bar.high − bar.low` directly\n * (no source param). NaN when either chained EMA is NaN or when\n * the inner EMA of EMA is zero (degenerate ratio).\n *\n * @formula range[t] = high[t] − low[t] ;\n * ema1 = EMA(emaLength)(range) ;\n * ema2 = EMA(emaLength)(ema1) ;\n * ratio[t] = ema1[t] / ema2[t] ;\n * mi[t] = sum(ratio[t − sumLength + 1..= t])\n * @warmup emaLength + emaLength + sumLength − 3\n * @since 0.2\n * @stable\n *\n * `opts.offset` is a presentation display shift carried to the plot\n * emission as `xShift` (`+n` right / future, `−n` left / past); the\n * series value is unshifted.\n *\n * @example\n * // import { ta } from \"@invinite-org/chartlang-core\";\n * // const mi = ta.massIndex();\n * // plot(mi);\n */\nexport function massIndex(slotId: string, opts?: MassIndexOpts): Series<number> {\n const ctx = getCtx();\n let slot = ctx.stream.taSlots.get(slotId) as MassIndexSlot | undefined;\n if (slot === undefined) {\n const emaLength = opts?.emaLength ?? DEFAULT_EMA_LENGTH;\n const sumLength = opts?.sumLength ?? DEFAULT_SUM_LENGTH;\n slot = initSlot(emaLength, sumLength, ctx.stream.ohlcv.close.capacity);\n ctx.stream.taSlots.set(slotId, slot);\n }\n const range = ctx.stream.bar.high - ctx.stream.bar.low;\n const ema1Series = ema(`${slotId}/ema1`, range, slot.emaLength);\n const e1 = ema1Series.current;\n const ema2Series = ema(`${slotId}/ema2`, e1, slot.emaLength);\n const e2 = ema2Series.current;\n const ratio = ratioValue(e1, e2);\n if (ctx.isTick) {\n slot.outBuffer.replaceHead(tickValue(slot, ratio));\n } else {\n slot.outBuffer.append(closeValue(slot, ratio));\n }\n return viewForOffset(slot, opts?.offset ?? 0);\n}\n"]}
|
package/dist/ta/mfi.d.ts
CHANGED
|
@@ -27,7 +27,9 @@ import type { MfiOpts, Series } from "@invinite-org/chartlang-core";
|
|
|
27
27
|
* @since 0.2
|
|
28
28
|
* @stable
|
|
29
29
|
*
|
|
30
|
-
* `opts.offset`
|
|
30
|
+
* `opts.offset` is a presentation display shift carried to the plot
|
|
31
|
+
* emission as `xShift` (`+n` right / future, `−n` left / past); the
|
|
32
|
+
* series value is unshifted.
|
|
31
33
|
*
|
|
32
34
|
* @example
|
|
33
35
|
* // import { ta, plot } from "@invinite-org/chartlang-core";
|
package/dist/ta/mfi.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mfi.d.ts","sourceRoot":"","sources":["../../src/ta/mfi.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AA+EpE
|
|
1
|
+
{"version":3,"file":"mfi.d.ts","sourceRoot":"","sources":["../../src/ta/mfi.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AA+EpE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,CA+ClF"}
|
package/dist/ta/mfi.js
CHANGED
|
@@ -98,7 +98,9 @@ function emitMfi(sumPos, sumNeg, ready) {
|
|
|
98
98
|
* @since 0.2
|
|
99
99
|
* @stable
|
|
100
100
|
*
|
|
101
|
-
* `opts.offset`
|
|
101
|
+
* `opts.offset` is a presentation display shift carried to the plot
|
|
102
|
+
* emission as `xShift` (`+n` right / future, `−n` left / past); the
|
|
103
|
+
* series value is unshifted.
|
|
102
104
|
*
|
|
103
105
|
* @example
|
|
104
106
|
* // import { ta, plot } from "@invinite-org/chartlang-core";
|
package/dist/ta/mfi.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mfi.js","sourceRoot":"","sources":["../../src/ta/mfi.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Invinite. Licensed under the MIT License.\n// See the LICENSE file in the repo root for full license text.\n//\n// Ported from invinite/src/components/trading-chart/indicators/mfi.ts\n// (commit 078f41fe2569d659d5aba726da8bcb5d3e2ced02, © Invinite).\n// Re-licensed MIT for chartlang. The math is the reference, the code\n// style is not.\n// Structural choices (callsite-id slot, Series<T> proxy, replaceHead\n// mode) follow chartlang's primitive shape — NOT invinite's\n// IndicatorPlugin shape. The rolling-window pos / neg money-flow sums\n// follow the `cmf.ts` / `ulcerIndex.ts` \"subtract head + add tick\"\n// tick-mode shape (no window mutation on tick).\n\nimport type { MfiOpts, Series } from \"@invinite-org/chartlang-core\";\n\nimport { Float64RingBuffer } from \"../ringBuffer.js\";\nimport { ACTIVE_RUNTIME_CONTEXT, type RuntimeContext } from \"../runtimeContext.js\";\nimport { makeSeriesView, makeShiftedSeriesView } from \"../seriesView.js\";\n\ntype MfiSlot = {\n readonly outBuffer: Float64RingBuffer;\n readonly series: Series<number>;\n readonly shiftedViews: Map<number, Series<number>>;\n readonly length: number;\n /** Closed-bar positive money-flow contributions (capacity `length`). */\n readonly posMfWindow: Float64RingBuffer;\n /** Closed-bar negative money-flow contributions. */\n readonly negMfWindow: Float64RingBuffer;\n sumPosMf: number;\n sumNegMf: number;\n /** Most recent finite typical price (lookback target for the next bar). */\n prevTp: number;\n};\n\nfunction getCtx(): RuntimeContext {\n const ctx = ACTIVE_RUNTIME_CONTEXT.current;\n if (ctx === null) {\n throw new Error(\"ta.mfi called outside an active script step\");\n }\n return ctx;\n}\n\nfunction initSlot(length: number, capacity: number): MfiSlot {\n const outBuffer = new Float64RingBuffer(capacity);\n return {\n outBuffer,\n series: makeSeriesView<number>(outBuffer),\n shiftedViews: new Map(),\n length,\n posMfWindow: new Float64RingBuffer(length),\n negMfWindow: new Float64RingBuffer(length),\n sumPosMf: 0,\n sumNegMf: 0,\n prevTp: Number.NaN,\n };\n}\n\nfunction viewForOffset(slot: MfiSlot, offset: number): Series<number> {\n if (offset === 0) return slot.series;\n let view = slot.shiftedViews.get(offset);\n if (view === undefined) {\n view = makeShiftedSeriesView<number>(slot.outBuffer, offset);\n slot.shiftedViews.set(offset, view);\n }\n return view;\n}\n\n/**\n * Per-bar typical-price contributions to the (posMF, negMF) split,\n * given the prior typical price. NaN OHLC / volume → (0, 0). Equal\n * `tp === prevTp` or first bar (`prevTp` NaN) → (0, 0) per Pine\n * convention.\n */\nfunction bucketMf(tp: number, prevTp: number, volume: number): { posMf: number; negMf: number } {\n if (!Number.isFinite(tp) || !Number.isFinite(volume)) {\n return { posMf: 0, negMf: 0 };\n }\n if (!Number.isFinite(prevTp) || tp === prevTp) {\n return { posMf: 0, negMf: 0 };\n }\n const mf = tp * volume;\n if (tp > prevTp) return { posMf: mf, negMf: 0 };\n return { posMf: 0, negMf: mf };\n}\n\nfunction emitMfi(sumPos: number, sumNeg: number, ready: boolean): number {\n if (!ready) return Number.NaN;\n const total = sumPos + sumNeg;\n if (total === 0) return Number.NaN;\n return (100 * sumPos) / total;\n}\n\n/**\n * Money Flow Index — volume-weighted RSI over a trailing window of\n * `length` typical-price comparisons. Per-bar typical price `tp = (H +\n * L + C) / 3`; per-bar money flow `mf = tp · volume` lands in the\n * positive bucket when `tp > prevTp`, in the negative bucket when\n * `tp < prevTp`, and is dropped on equality / first bar. The emit is\n * `100 · sumPos / (sumPos + sumNeg)` once `length` such comparisons\n * have accumulated; NaN before warmup and when `sumPos + sumNeg ===\n * 0` (no flow either way — invinite's zero-denominator guard).\n *\n * Range `[0, 100]` when defined: `sumPos === 0` → 0 (perfect\n * downflow); `sumNeg === 0` with `sumPos > 0` → 100 (perfect\n * upflow). NaN OHLC / volume contributes 0 to both buckets (matches\n * `cmf.ts:62-75`'s defensive shape).\n *\n * **Tick mode.** Substitutes the tick's (posMf, negMf) contribution\n * for the head slot's stored values without mutating the trailing-\n * window rings or advancing `prevTp` — mirrors `cmf.ts:125-138`.\n *\n * @formula tp = (high + low + close) / 3 ;\n * mf = tp · volume ;\n * pos = mf when tp > prevTp else 0 ;\n * neg = mf when tp < prevTp else 0 ;\n * mfi = 100 · Σ pos / (Σ pos + Σ neg) over the trailing `length` window\n * @warmup length + 1 (one bar to seed prevTp + `length` comparisons)\n * @since 0.2\n * @stable\n *\n * `opts.offset` shifts the returned series.\n *\n * @example\n * // import { ta, plot } from \"@invinite-org/chartlang-core\";\n * // const m = ta.mfi(14);\n * // plot(m);\n */\nexport function mfi(slotId: string, length: number, opts?: MfiOpts): Series<number> {\n const ctx = getCtx();\n let slot = ctx.stream.taSlots.get(slotId) as MfiSlot | undefined;\n if (slot === undefined) {\n slot = initSlot(length, ctx.stream.ohlcv.close.capacity);\n ctx.stream.taSlots.set(slotId, slot);\n }\n const offset = opts?.offset ?? 0;\n const { high, low, close, volume } = ctx.stream.bar;\n const tp = (high + low + close) / 3;\n const { posMf, negMf } = bucketMf(tp, slot.prevTp, volume);\n\n // The seed bar (prevTp NaN) contributes no real comparison — we\n // skip the window append so the trailing window only ever holds\n // `length` REAL (prevTp-defined) comparisons. First finite emit\n // lands at bar `length` (warmup `length + 1` per the JSDoc).\n const hasComparison = Number.isFinite(slot.prevTp);\n\n if (ctx.isTick) {\n const ready = slot.posMfWindow.length === slot.length;\n if (!ready || !hasComparison) {\n slot.outBuffer.replaceHead(Number.NaN);\n return viewForOffset(slot, offset);\n }\n const headPos = slot.posMfWindow.at(0);\n const headNeg = slot.negMfWindow.at(0);\n const hypPos = slot.sumPosMf - headPos + posMf;\n const hypNeg = slot.sumNegMf - headNeg + negMf;\n slot.outBuffer.replaceHead(emitMfi(hypPos, hypNeg, true));\n return viewForOffset(slot, offset);\n }\n\n if (hasComparison) {\n if (slot.posMfWindow.length === slot.length) {\n slot.sumPosMf -= slot.posMfWindow.at(slot.length - 1);\n slot.sumNegMf -= slot.negMfWindow.at(slot.length - 1);\n }\n slot.posMfWindow.append(posMf);\n slot.negMfWindow.append(negMf);\n slot.sumPosMf += posMf;\n slot.sumNegMf += negMf;\n }\n if (Number.isFinite(tp)) slot.prevTp = tp;\n slot.outBuffer.append(\n emitMfi(slot.sumPosMf, slot.sumNegMf, slot.posMfWindow.length === slot.length),\n );\n return viewForOffset(slot, offset);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"mfi.js","sourceRoot":"","sources":["../../src/ta/mfi.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Invinite. Licensed under the MIT License.\n// See the LICENSE file in the repo root for full license text.\n//\n// Ported from invinite/src/components/trading-chart/indicators/mfi.ts\n// (commit 078f41fe2569d659d5aba726da8bcb5d3e2ced02, © Invinite).\n// Re-licensed MIT for chartlang. The math is the reference, the code\n// style is not.\n// Structural choices (callsite-id slot, Series<T> proxy, replaceHead\n// mode) follow chartlang's primitive shape — NOT invinite's\n// IndicatorPlugin shape. The rolling-window pos / neg money-flow sums\n// follow the `cmf.ts` / `ulcerIndex.ts` \"subtract head + add tick\"\n// tick-mode shape (no window mutation on tick).\n\nimport type { MfiOpts, Series } from \"@invinite-org/chartlang-core\";\n\nimport { Float64RingBuffer } from \"../ringBuffer.js\";\nimport { ACTIVE_RUNTIME_CONTEXT, type RuntimeContext } from \"../runtimeContext.js\";\nimport { makeSeriesView, makeShiftedSeriesView } from \"../seriesView.js\";\n\ntype MfiSlot = {\n readonly outBuffer: Float64RingBuffer;\n readonly series: Series<number>;\n readonly shiftedViews: Map<number, Series<number>>;\n readonly length: number;\n /** Closed-bar positive money-flow contributions (capacity `length`). */\n readonly posMfWindow: Float64RingBuffer;\n /** Closed-bar negative money-flow contributions. */\n readonly negMfWindow: Float64RingBuffer;\n sumPosMf: number;\n sumNegMf: number;\n /** Most recent finite typical price (lookback target for the next bar). */\n prevTp: number;\n};\n\nfunction getCtx(): RuntimeContext {\n const ctx = ACTIVE_RUNTIME_CONTEXT.current;\n if (ctx === null) {\n throw new Error(\"ta.mfi called outside an active script step\");\n }\n return ctx;\n}\n\nfunction initSlot(length: number, capacity: number): MfiSlot {\n const outBuffer = new Float64RingBuffer(capacity);\n return {\n outBuffer,\n series: makeSeriesView<number>(outBuffer),\n shiftedViews: new Map(),\n length,\n posMfWindow: new Float64RingBuffer(length),\n negMfWindow: new Float64RingBuffer(length),\n sumPosMf: 0,\n sumNegMf: 0,\n prevTp: Number.NaN,\n };\n}\n\nfunction viewForOffset(slot: MfiSlot, offset: number): Series<number> {\n if (offset === 0) return slot.series;\n let view = slot.shiftedViews.get(offset);\n if (view === undefined) {\n view = makeShiftedSeriesView<number>(slot.outBuffer, offset);\n slot.shiftedViews.set(offset, view);\n }\n return view;\n}\n\n/**\n * Per-bar typical-price contributions to the (posMF, negMF) split,\n * given the prior typical price. NaN OHLC / volume → (0, 0). Equal\n * `tp === prevTp` or first bar (`prevTp` NaN) → (0, 0) per Pine\n * convention.\n */\nfunction bucketMf(tp: number, prevTp: number, volume: number): { posMf: number; negMf: number } {\n if (!Number.isFinite(tp) || !Number.isFinite(volume)) {\n return { posMf: 0, negMf: 0 };\n }\n if (!Number.isFinite(prevTp) || tp === prevTp) {\n return { posMf: 0, negMf: 0 };\n }\n const mf = tp * volume;\n if (tp > prevTp) return { posMf: mf, negMf: 0 };\n return { posMf: 0, negMf: mf };\n}\n\nfunction emitMfi(sumPos: number, sumNeg: number, ready: boolean): number {\n if (!ready) return Number.NaN;\n const total = sumPos + sumNeg;\n if (total === 0) return Number.NaN;\n return (100 * sumPos) / total;\n}\n\n/**\n * Money Flow Index — volume-weighted RSI over a trailing window of\n * `length` typical-price comparisons. Per-bar typical price `tp = (H +\n * L + C) / 3`; per-bar money flow `mf = tp · volume` lands in the\n * positive bucket when `tp > prevTp`, in the negative bucket when\n * `tp < prevTp`, and is dropped on equality / first bar. The emit is\n * `100 · sumPos / (sumPos + sumNeg)` once `length` such comparisons\n * have accumulated; NaN before warmup and when `sumPos + sumNeg ===\n * 0` (no flow either way — invinite's zero-denominator guard).\n *\n * Range `[0, 100]` when defined: `sumPos === 0` → 0 (perfect\n * downflow); `sumNeg === 0` with `sumPos > 0` → 100 (perfect\n * upflow). NaN OHLC / volume contributes 0 to both buckets (matches\n * `cmf.ts:62-75`'s defensive shape).\n *\n * **Tick mode.** Substitutes the tick's (posMf, negMf) contribution\n * for the head slot's stored values without mutating the trailing-\n * window rings or advancing `prevTp` — mirrors `cmf.ts:125-138`.\n *\n * @formula tp = (high + low + close) / 3 ;\n * mf = tp · volume ;\n * pos = mf when tp > prevTp else 0 ;\n * neg = mf when tp < prevTp else 0 ;\n * mfi = 100 · Σ pos / (Σ pos + Σ neg) over the trailing `length` window\n * @warmup length + 1 (one bar to seed prevTp + `length` comparisons)\n * @since 0.2\n * @stable\n *\n * `opts.offset` is a presentation display shift carried to the plot\n * emission as `xShift` (`+n` right / future, `−n` left / past); the\n * series value is unshifted.\n *\n * @example\n * // import { ta, plot } from \"@invinite-org/chartlang-core\";\n * // const m = ta.mfi(14);\n * // plot(m);\n */\nexport function mfi(slotId: string, length: number, opts?: MfiOpts): Series<number> {\n const ctx = getCtx();\n let slot = ctx.stream.taSlots.get(slotId) as MfiSlot | undefined;\n if (slot === undefined) {\n slot = initSlot(length, ctx.stream.ohlcv.close.capacity);\n ctx.stream.taSlots.set(slotId, slot);\n }\n const offset = opts?.offset ?? 0;\n const { high, low, close, volume } = ctx.stream.bar;\n const tp = (high + low + close) / 3;\n const { posMf, negMf } = bucketMf(tp, slot.prevTp, volume);\n\n // The seed bar (prevTp NaN) contributes no real comparison — we\n // skip the window append so the trailing window only ever holds\n // `length` REAL (prevTp-defined) comparisons. First finite emit\n // lands at bar `length` (warmup `length + 1` per the JSDoc).\n const hasComparison = Number.isFinite(slot.prevTp);\n\n if (ctx.isTick) {\n const ready = slot.posMfWindow.length === slot.length;\n if (!ready || !hasComparison) {\n slot.outBuffer.replaceHead(Number.NaN);\n return viewForOffset(slot, offset);\n }\n const headPos = slot.posMfWindow.at(0);\n const headNeg = slot.negMfWindow.at(0);\n const hypPos = slot.sumPosMf - headPos + posMf;\n const hypNeg = slot.sumNegMf - headNeg + negMf;\n slot.outBuffer.replaceHead(emitMfi(hypPos, hypNeg, true));\n return viewForOffset(slot, offset);\n }\n\n if (hasComparison) {\n if (slot.posMfWindow.length === slot.length) {\n slot.sumPosMf -= slot.posMfWindow.at(slot.length - 1);\n slot.sumNegMf -= slot.negMfWindow.at(slot.length - 1);\n }\n slot.posMfWindow.append(posMf);\n slot.negMfWindow.append(negMf);\n slot.sumPosMf += posMf;\n slot.sumNegMf += negMf;\n }\n if (Number.isFinite(tp)) slot.prevTp = tp;\n slot.outBuffer.append(\n emitMfi(slot.sumPosMf, slot.sumNegMf, slot.posMfWindow.length === slot.length),\n );\n return viewForOffset(slot, offset);\n}\n"]}
|
package/dist/ta/netVolume.d.ts
CHANGED
|
@@ -20,7 +20,9 @@ import type { NetVolumeOpts, Series } from "@invinite-org/chartlang-core";
|
|
|
20
20
|
* @since 0.2
|
|
21
21
|
* @stable
|
|
22
22
|
*
|
|
23
|
-
* `opts.offset`
|
|
23
|
+
* `opts.offset` is a presentation display shift carried to the plot
|
|
24
|
+
* emission as `xShift` (`+n` right / future, `−n` left / past); the
|
|
25
|
+
* series value is unshifted.
|
|
24
26
|
*
|
|
25
27
|
* @example
|
|
26
28
|
* // import { ta, plot } from "@invinite-org/chartlang-core";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"netVolume.d.ts","sourceRoot":"","sources":["../../src/ta/netVolume.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAwE1E
|
|
1
|
+
{"version":3,"file":"netVolume.d.ts","sourceRoot":"","sources":["../../src/ta/netVolume.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAwE1E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,CAuB9E"}
|
package/dist/ta/netVolume.js
CHANGED
|
@@ -86,7 +86,9 @@ function fold(inCum, inPrevClose, close, volume) {
|
|
|
86
86
|
* @since 0.2
|
|
87
87
|
* @stable
|
|
88
88
|
*
|
|
89
|
-
* `opts.offset`
|
|
89
|
+
* `opts.offset` is a presentation display shift carried to the plot
|
|
90
|
+
* emission as `xShift` (`+n` right / future, `−n` left / past); the
|
|
91
|
+
* series value is unshifted.
|
|
90
92
|
*
|
|
91
93
|
* @example
|
|
92
94
|
* // import { ta, plot } from "@invinite-org/chartlang-core";
|
package/dist/ta/netVolume.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"netVolume.js","sourceRoot":"","sources":["../../src/ta/netVolume.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAC/D,EAAE;AACF,6EAA6E;AAC7E,mEAAmE;AACnE,qEAAqE;AACrE,gBAAgB;AAChB,qEAAqE;AACrE,4DAA4D;AAC5D,iEAAiE;AACjE,6DAA6D;AAC7D,kEAAkE;AAClE,oEAAoE;AACpE,oEAAoE;AACpE,kEAAkE;AAIlE,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,sBAAsB,EAAuB,MAAM,sBAAsB,CAAC;AACnF,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAYzE,SAAS,MAAM;IACX,MAAM,GAAG,GAAG,sBAAsB,CAAC,OAAO,CAAC;IAC3C,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACzE,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAED,SAAS,QAAQ,CAAC,QAAgB;IAC9B,MAAM,SAAS,GAAG,IAAI,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAClD,OAAO;QACH,SAAS;QACT,MAAM,EAAE,cAAc,CAAS,SAAS,CAAC;QACzC,YAAY,EAAE,IAAI,GAAG,EAAE;QACvB,SAAS,EAAE,CAAC;QACZ,SAAS,EAAE,MAAM,CAAC,GAAG;QACrB,mBAAmB,EAAE,CAAC;QACtB,mBAAmB,EAAE,MAAM,CAAC,GAAG;KAClC,CAAC;AACN,CAAC;AAED,SAAS,aAAa,CAAC,IAAmB,EAAE,MAAc;IACtD,IAAI,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC,MAAM,CAAC;IACrC,IAAI,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACzC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACrB,IAAI,GAAG,qBAAqB,CAAS,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QAC7D,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACxC,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,SAAS,WAAW,CAAC,KAAa;IAC9B,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,CAAC,CAAC;IACxB,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,CAAC,CAAC,CAAC;IACzB,OAAO,CAAC,CAAC;AACb,CAAC;AAED,SAAS,IAAI,CACT,KAAa,EACb,WAAmB,EACnB,KAAa,EACb,MAAc;IAEd,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC;IAClD,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QAChC,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IAC5C,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3B,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IAC5C,CAAC;IACD,MAAM,SAAS,GAAG,WAAW,CAAC,KAAK,GAAG,WAAW,CAAC,CAAC;IACnD,OAAO,EAAE,GAAG,EAAE,KAAK,GAAG,SAAS,GAAG,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;AACjE,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"netVolume.js","sourceRoot":"","sources":["../../src/ta/netVolume.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Invinite. Licensed under the MIT License.\n// See the LICENSE file in the repo root for full license text.\n//\n// Ported from invinite/src/components/trading-chart/indicators/net-volume.ts\n// (commit 078f41fe2569d659d5aba726da8bcb5d3e2ced02, © Invinite).\n// Re-licensed MIT for chartlang. The math is the reference, the code\n// style is not.\n// Structural choices (callsite-id slot, Series<T> proxy, replaceHead\n// mode) follow chartlang's primitive shape — NOT invinite's\n// IndicatorPlugin shape. The math is identical to `ta.obv`; both\n// primitives exist in invinite under their own names, so the\n// chartlang surface mirrors the public API for naming parity. See\n// `obv.ts` for the canonical commentary on `fold`, snapshot fields,\n// and NaN handling — this file repeats the shape verbatim under its\n// own slot type so each primitive has its own per-callsite state.\n\nimport type { NetVolumeOpts, Series } from \"@invinite-org/chartlang-core\";\n\nimport { Float64RingBuffer } from \"../ringBuffer.js\";\nimport { ACTIVE_RUNTIME_CONTEXT, type RuntimeContext } from \"../runtimeContext.js\";\nimport { makeSeriesView, makeShiftedSeriesView } from \"../seriesView.js\";\n\ntype NetVolumeSlot = {\n readonly outBuffer: Float64RingBuffer;\n readonly series: Series<number>;\n readonly shiftedViews: Map<number, Series<number>>;\n cumNetVol: number;\n prevClose: number;\n prevClosedCumNetVol: number;\n prevClosedPrevClose: number;\n};\n\nfunction getCtx(): RuntimeContext {\n const ctx = ACTIVE_RUNTIME_CONTEXT.current;\n if (ctx === null) {\n throw new Error(\"ta.netVolume called outside an active script step\");\n }\n return ctx;\n}\n\nfunction initSlot(capacity: number): NetVolumeSlot {\n const outBuffer = new Float64RingBuffer(capacity);\n return {\n outBuffer,\n series: makeSeriesView<number>(outBuffer),\n shiftedViews: new Map(),\n cumNetVol: 0,\n prevClose: Number.NaN,\n prevClosedCumNetVol: 0,\n prevClosedPrevClose: Number.NaN,\n };\n}\n\nfunction viewForOffset(slot: NetVolumeSlot, offset: number): Series<number> {\n if (offset === 0) return slot.series;\n let view = slot.shiftedViews.get(offset);\n if (view === undefined) {\n view = makeShiftedSeriesView<number>(slot.outBuffer, offset);\n slot.shiftedViews.set(offset, view);\n }\n return view;\n}\n\nfunction signOfDelta(delta: number): number {\n if (delta > 0) return 1;\n if (delta < 0) return -1;\n return 0;\n}\n\nfunction fold(\n inCum: number,\n inPrevClose: number,\n close: number,\n volume: number,\n): { cum: number; prevClose: number } {\n if (!Number.isFinite(close)) {\n return { cum: inCum, prevClose: inPrevClose };\n }\n if (!Number.isFinite(inPrevClose)) {\n return { cum: inCum, prevClose: close };\n }\n if (!Number.isFinite(volume)) {\n return { cum: inCum, prevClose: close };\n }\n const direction = signOfDelta(close - inPrevClose);\n return { cum: inCum + direction * volume, prevClose: close };\n}\n\n/**\n * Net Volume — cumulative `sign(close − prevClose) · volume`. **The\n * math is identical to `ta.obv`** (both primitives ship in invinite\n * under their own names; chartlang mirrors the public surface for\n * naming parity). Prefer `ta.obv` when writing new scripts — this\n * primitive exists to satisfy translation of Pine / invinite\n * indicators that call `netVolume()` directly. The cross-equivalence\n * is property-tested (`netVolume.property.test.ts`).\n *\n * First bar emits `0` (no prior close to difference against — Pine\n * convention). NaN volume carries the accumulator forward without an\n * update; NaN close holds `prevClose` at its prior value.\n *\n * **Tick mode.** Replays the head bar's contribution against a\n * snapshot of the prior-close `(cumNetVol, prevClose)` tuple.\n *\n * @formula netVolume[t] = netVolume[t − 1] + sign(close[t] − close[t − 1]) · volume[t]\n * @warmup 1 (needs a prior close to compute the delta; bar 0 emits 0)\n * @since 0.2\n * @stable\n *\n * `opts.offset` is a presentation display shift carried to the plot\n * emission as `xShift` (`+n` right / future, `−n` left / past); the\n * series value is unshifted.\n *\n * @example\n * // import { ta, plot } from \"@invinite-org/chartlang-core\";\n * // const nv = ta.netVolume();\n * // plot(nv);\n */\nexport function netVolume(slotId: string, opts?: NetVolumeOpts): Series<number> {\n const ctx = getCtx();\n let slot = ctx.stream.taSlots.get(slotId) as NetVolumeSlot | undefined;\n if (slot === undefined) {\n slot = initSlot(ctx.stream.ohlcv.close.capacity);\n ctx.stream.taSlots.set(slotId, slot);\n }\n const offset = opts?.offset ?? 0;\n const { close, volume } = ctx.stream.bar;\n\n if (ctx.isTick) {\n const next = fold(slot.prevClosedCumNetVol, slot.prevClosedPrevClose, close, volume);\n slot.outBuffer.replaceHead(next.cum);\n return viewForOffset(slot, offset);\n }\n\n slot.prevClosedCumNetVol = slot.cumNetVol;\n slot.prevClosedPrevClose = slot.prevClose;\n const next = fold(slot.cumNetVol, slot.prevClose, close, volume);\n slot.cumNetVol = next.cum;\n slot.prevClose = next.prevClose;\n slot.outBuffer.append(slot.cumNetVol);\n return viewForOffset(slot, offset);\n}\n"]}
|
package/dist/ta/nvi.d.ts
CHANGED
|
@@ -24,7 +24,9 @@ import type { NviOpts, Series } from "@invinite-org/chartlang-core";
|
|
|
24
24
|
* @since 0.2
|
|
25
25
|
* @stable
|
|
26
26
|
*
|
|
27
|
-
* `opts.offset`
|
|
27
|
+
* `opts.offset` is a presentation display shift carried to the plot
|
|
28
|
+
* emission as `xShift` (`+n` right / future, `−n` left / past); the
|
|
29
|
+
* series value is unshifted.
|
|
28
30
|
*
|
|
29
31
|
* @example
|
|
30
32
|
* // import { ta, plot } from "@invinite-org/chartlang-core";
|
package/dist/ta/nvi.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nvi.d.ts","sourceRoot":"","sources":["../../src/ta/nvi.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAoGpE
|
|
1
|
+
{"version":3,"file":"nvi.d.ts","sourceRoot":"","sources":["../../src/ta/nvi.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAoGpE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,CA+BlE"}
|
package/dist/ta/nvi.js
CHANGED
|
@@ -106,7 +106,9 @@ function fold(inValue, inPrevClose, inPrevVolume, close, volume) {
|
|
|
106
106
|
* @since 0.2
|
|
107
107
|
* @stable
|
|
108
108
|
*
|
|
109
|
-
* `opts.offset`
|
|
109
|
+
* `opts.offset` is a presentation display shift carried to the plot
|
|
110
|
+
* emission as `xShift` (`+n` right / future, `−n` left / past); the
|
|
111
|
+
* series value is unshifted.
|
|
110
112
|
*
|
|
111
113
|
* @example
|
|
112
114
|
* // import { ta, plot } from "@invinite-org/chartlang-core";
|
package/dist/ta/nvi.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nvi.js","sourceRoot":"","sources":["../../src/ta/nvi.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Invinite. Licensed under the MIT License.\n// See the LICENSE file in the repo root for full license text.\n//\n// Ported from invinite/src/components/trading-chart/indicators/nvi.ts\n// (commit 078f41fe2569d659d5aba726da8bcb5d3e2ced02, © Invinite).\n// Re-licensed MIT for chartlang. The math is the reference, the code\n// style is not.\n// Structural choices (callsite-id slot, Series<T> proxy, replaceHead\n// mode) follow chartlang's primitive shape — NOT invinite's\n// IndicatorPlugin shape. The runtime emits the RAW NVI line only;\n// invinite's optional smoothing block is left to the script author\n// (`ta.ema(ta.nvi(), 255)` etc.). The mirror primitive `ta.pvi`\n// re-uses this file's `nviLikeFold` shape with a flipped predicate.\n\nimport type { NviOpts, Series } from \"@invinite-org/chartlang-core\";\n\nimport { Float64RingBuffer } from \"../ringBuffer.js\";\nimport { ACTIVE_RUNTIME_CONTEXT, type RuntimeContext } from \"../runtimeContext.js\";\nimport { makeSeriesView, makeShiftedSeriesView } from \"../seriesView.js\";\n\n/**\n * Anchored seed value at bar 0 (and after any bar where the\n * accumulator has been carried-forward through NaN inputs).\n *\n * @anchors seedValue\n */\nconst SEED_VALUE = 1000;\n\ntype NviSlot = {\n readonly outBuffer: Float64RingBuffer;\n readonly series: Series<number>;\n readonly shiftedViews: Map<number, Series<number>>;\n /** Active NVI value across the closed bars so far. */\n value: number;\n /** Most recent finite close. */\n prevClose: number;\n /** Most recent volume (treated as 0 on NaN). */\n prevVolume: number;\n /** Snapshot of `value` BEFORE the most recent close-side update. */\n prevClosedValue: number;\n prevClosedPrevClose: number;\n prevClosedPrevVolume: number;\n};\n\nfunction getCtx(): RuntimeContext {\n const ctx = ACTIVE_RUNTIME_CONTEXT.current;\n if (ctx === null) {\n throw new Error(\"ta.nvi called outside an active script step\");\n }\n return ctx;\n}\n\nfunction initSlot(capacity: number): NviSlot {\n const outBuffer = new Float64RingBuffer(capacity);\n return {\n outBuffer,\n series: makeSeriesView<number>(outBuffer),\n shiftedViews: new Map(),\n value: SEED_VALUE,\n prevClose: Number.NaN,\n prevVolume: Number.NaN,\n prevClosedValue: SEED_VALUE,\n prevClosedPrevClose: Number.NaN,\n prevClosedPrevVolume: Number.NaN,\n };\n}\n\nfunction viewForOffset(slot: NviSlot, offset: number): Series<number> {\n if (offset === 0) return slot.series;\n let view = slot.shiftedViews.get(offset);\n if (view === undefined) {\n view = makeShiftedSeriesView<number>(slot.outBuffer, offset);\n slot.shiftedViews.set(offset, view);\n }\n return view;\n}\n\nfunction safeVol(volume: number): number {\n return Number.isFinite(volume) ? volume : 0;\n}\n\n/**\n * Fold the bar's `(close, volume)` into the prior NVI state. NaN close\n * carries every field forward (don't advance `prevClose` — the next\n * finite close differences against the last finite one). NaN volume\n * is treated as 0 (matches invinite's `safeVolume` shape). The\n * comparison runs `currV < prevV` (NVI) and only updates when the\n * comparison holds AND `prevClose !== 0`.\n */\nfunction fold(\n inValue: number,\n inPrevClose: number,\n inPrevVolume: number,\n close: number,\n volume: number,\n): { value: number; prevClose: number; prevVolume: number } {\n if (!Number.isFinite(close)) {\n return { value: inValue, prevClose: inPrevClose, prevVolume: inPrevVolume };\n }\n const v = safeVol(volume);\n if (!Number.isFinite(inPrevClose)) {\n // First defined close — seed `prevClose` + `prevVolume`,\n // leave `value` at its 1000 seed.\n return { value: inValue, prevClose: close, prevVolume: v };\n }\n const pv = safeVol(inPrevVolume);\n const shouldUpdate = v < pv;\n if (!shouldUpdate || inPrevClose === 0) {\n return { value: inValue, prevClose: close, prevVolume: v };\n }\n const next = inValue * (1 + (close - inPrevClose) / inPrevClose);\n return { value: next, prevClose: close, prevVolume: v };\n}\n\n/**\n * Negative Volume Index — cumulative percentage-change in close on\n * bars whose volume is strictly LOWER than the prior bar's; bars with\n * equal-or-higher volume carry the prior NVI value unchanged. Seeded\n * at 1000 (anchor — see `SEED_VALUE`); the property tests pin the\n * seed at bar 0.\n *\n * NaN volume is treated as 0 (matches invinite's `safeVolume` shape) —\n * a NaN-volume bar is \"lower\" than any positive-volume bar, so the\n * comparison is exercised. NaN close carries every accumulator field\n * forward without advancing — the next finite close differences\n * against the last finite one.\n *\n * **Tick mode.** Replays the head bar's contribution against a\n * snapshot of the prior-close `(value, prevClose, prevVolume)` tuple.\n *\n * @formula nvi[0] = 1000 ;\n * nvi[t] = (volume[t] < volume[t − 1] && prevClose != 0)\n * ? nvi[t − 1] · (1 + (close[t] − close[t − 1]) / close[t − 1])\n * : nvi[t − 1]\n * @warmup 1 (bar 0 emits the 1000 seed)\n * @anchors seedValue\n * @since 0.2\n * @stable\n *\n * `opts.offset` shifts the returned series.\n *\n * @example\n * // import { ta, plot } from \"@invinite-org/chartlang-core\";\n * // const n = ta.nvi();\n * // plot(n);\n */\nexport function nvi(slotId: string, opts?: NviOpts): Series<number> {\n const ctx = getCtx();\n let slot = ctx.stream.taSlots.get(slotId) as NviSlot | undefined;\n if (slot === undefined) {\n slot = initSlot(ctx.stream.ohlcv.close.capacity);\n ctx.stream.taSlots.set(slotId, slot);\n }\n const offset = opts?.offset ?? 0;\n const { close, volume } = ctx.stream.bar;\n\n if (ctx.isTick) {\n const next = fold(\n slot.prevClosedValue,\n slot.prevClosedPrevClose,\n slot.prevClosedPrevVolume,\n close,\n volume,\n );\n slot.outBuffer.replaceHead(next.value);\n return viewForOffset(slot, offset);\n }\n\n slot.prevClosedValue = slot.value;\n slot.prevClosedPrevClose = slot.prevClose;\n slot.prevClosedPrevVolume = slot.prevVolume;\n const next = fold(slot.value, slot.prevClose, slot.prevVolume, close, volume);\n slot.value = next.value;\n slot.prevClose = next.prevClose;\n slot.prevVolume = next.prevVolume;\n slot.outBuffer.append(slot.value);\n return viewForOffset(slot, offset);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"nvi.js","sourceRoot":"","sources":["../../src/ta/nvi.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Invinite. Licensed under the MIT License.\n// See the LICENSE file in the repo root for full license text.\n//\n// Ported from invinite/src/components/trading-chart/indicators/nvi.ts\n// (commit 078f41fe2569d659d5aba726da8bcb5d3e2ced02, © Invinite).\n// Re-licensed MIT for chartlang. The math is the reference, the code\n// style is not.\n// Structural choices (callsite-id slot, Series<T> proxy, replaceHead\n// mode) follow chartlang's primitive shape — NOT invinite's\n// IndicatorPlugin shape. The runtime emits the RAW NVI line only;\n// invinite's optional smoothing block is left to the script author\n// (`ta.ema(ta.nvi(), 255)` etc.). The mirror primitive `ta.pvi`\n// re-uses this file's `nviLikeFold` shape with a flipped predicate.\n\nimport type { NviOpts, Series } from \"@invinite-org/chartlang-core\";\n\nimport { Float64RingBuffer } from \"../ringBuffer.js\";\nimport { ACTIVE_RUNTIME_CONTEXT, type RuntimeContext } from \"../runtimeContext.js\";\nimport { makeSeriesView, makeShiftedSeriesView } from \"../seriesView.js\";\n\n/**\n * Anchored seed value at bar 0 (and after any bar where the\n * accumulator has been carried-forward through NaN inputs).\n *\n * @anchors seedValue\n */\nconst SEED_VALUE = 1000;\n\ntype NviSlot = {\n readonly outBuffer: Float64RingBuffer;\n readonly series: Series<number>;\n readonly shiftedViews: Map<number, Series<number>>;\n /** Active NVI value across the closed bars so far. */\n value: number;\n /** Most recent finite close. */\n prevClose: number;\n /** Most recent volume (treated as 0 on NaN). */\n prevVolume: number;\n /** Snapshot of `value` BEFORE the most recent close-side update. */\n prevClosedValue: number;\n prevClosedPrevClose: number;\n prevClosedPrevVolume: number;\n};\n\nfunction getCtx(): RuntimeContext {\n const ctx = ACTIVE_RUNTIME_CONTEXT.current;\n if (ctx === null) {\n throw new Error(\"ta.nvi called outside an active script step\");\n }\n return ctx;\n}\n\nfunction initSlot(capacity: number): NviSlot {\n const outBuffer = new Float64RingBuffer(capacity);\n return {\n outBuffer,\n series: makeSeriesView<number>(outBuffer),\n shiftedViews: new Map(),\n value: SEED_VALUE,\n prevClose: Number.NaN,\n prevVolume: Number.NaN,\n prevClosedValue: SEED_VALUE,\n prevClosedPrevClose: Number.NaN,\n prevClosedPrevVolume: Number.NaN,\n };\n}\n\nfunction viewForOffset(slot: NviSlot, offset: number): Series<number> {\n if (offset === 0) return slot.series;\n let view = slot.shiftedViews.get(offset);\n if (view === undefined) {\n view = makeShiftedSeriesView<number>(slot.outBuffer, offset);\n slot.shiftedViews.set(offset, view);\n }\n return view;\n}\n\nfunction safeVol(volume: number): number {\n return Number.isFinite(volume) ? volume : 0;\n}\n\n/**\n * Fold the bar's `(close, volume)` into the prior NVI state. NaN close\n * carries every field forward (don't advance `prevClose` — the next\n * finite close differences against the last finite one). NaN volume\n * is treated as 0 (matches invinite's `safeVolume` shape). The\n * comparison runs `currV < prevV` (NVI) and only updates when the\n * comparison holds AND `prevClose !== 0`.\n */\nfunction fold(\n inValue: number,\n inPrevClose: number,\n inPrevVolume: number,\n close: number,\n volume: number,\n): { value: number; prevClose: number; prevVolume: number } {\n if (!Number.isFinite(close)) {\n return { value: inValue, prevClose: inPrevClose, prevVolume: inPrevVolume };\n }\n const v = safeVol(volume);\n if (!Number.isFinite(inPrevClose)) {\n // First defined close — seed `prevClose` + `prevVolume`,\n // leave `value` at its 1000 seed.\n return { value: inValue, prevClose: close, prevVolume: v };\n }\n const pv = safeVol(inPrevVolume);\n const shouldUpdate = v < pv;\n if (!shouldUpdate || inPrevClose === 0) {\n return { value: inValue, prevClose: close, prevVolume: v };\n }\n const next = inValue * (1 + (close - inPrevClose) / inPrevClose);\n return { value: next, prevClose: close, prevVolume: v };\n}\n\n/**\n * Negative Volume Index — cumulative percentage-change in close on\n * bars whose volume is strictly LOWER than the prior bar's; bars with\n * equal-or-higher volume carry the prior NVI value unchanged. Seeded\n * at 1000 (anchor — see `SEED_VALUE`); the property tests pin the\n * seed at bar 0.\n *\n * NaN volume is treated as 0 (matches invinite's `safeVolume` shape) —\n * a NaN-volume bar is \"lower\" than any positive-volume bar, so the\n * comparison is exercised. NaN close carries every accumulator field\n * forward without advancing — the next finite close differences\n * against the last finite one.\n *\n * **Tick mode.** Replays the head bar's contribution against a\n * snapshot of the prior-close `(value, prevClose, prevVolume)` tuple.\n *\n * @formula nvi[0] = 1000 ;\n * nvi[t] = (volume[t] < volume[t − 1] && prevClose != 0)\n * ? nvi[t − 1] · (1 + (close[t] − close[t − 1]) / close[t − 1])\n * : nvi[t − 1]\n * @warmup 1 (bar 0 emits the 1000 seed)\n * @anchors seedValue\n * @since 0.2\n * @stable\n *\n * `opts.offset` is a presentation display shift carried to the plot\n * emission as `xShift` (`+n` right / future, `−n` left / past); the\n * series value is unshifted.\n *\n * @example\n * // import { ta, plot } from \"@invinite-org/chartlang-core\";\n * // const n = ta.nvi();\n * // plot(n);\n */\nexport function nvi(slotId: string, opts?: NviOpts): Series<number> {\n const ctx = getCtx();\n let slot = ctx.stream.taSlots.get(slotId) as NviSlot | undefined;\n if (slot === undefined) {\n slot = initSlot(ctx.stream.ohlcv.close.capacity);\n ctx.stream.taSlots.set(slotId, slot);\n }\n const offset = opts?.offset ?? 0;\n const { close, volume } = ctx.stream.bar;\n\n if (ctx.isTick) {\n const next = fold(\n slot.prevClosedValue,\n slot.prevClosedPrevClose,\n slot.prevClosedPrevVolume,\n close,\n volume,\n );\n slot.outBuffer.replaceHead(next.value);\n return viewForOffset(slot, offset);\n }\n\n slot.prevClosedValue = slot.value;\n slot.prevClosedPrevClose = slot.prevClose;\n slot.prevClosedPrevVolume = slot.prevVolume;\n const next = fold(slot.value, slot.prevClose, slot.prevVolume, close, volume);\n slot.value = next.value;\n slot.prevClose = next.prevClose;\n slot.prevVolume = next.prevVolume;\n slot.outBuffer.append(slot.value);\n return viewForOffset(slot, offset);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persistence.d.ts","sourceRoot":"","sources":["../../src/ta/persistence.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAU,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"persistence.d.ts","sourceRoot":"","sources":["../../src/ta/persistence.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAU,MAAM,8BAA8B,CAAC;AAatE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AA6PrD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAExD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CASzF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAC1B,MAAM,EAAE,WAAW,EACnB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACzC,IAAI,CASN"}
|
package/dist/ta/persistence.js
CHANGED
|
@@ -1,19 +1,9 @@
|
|
|
1
1
|
// Copyright (c) 2026 Invinite. Licensed under the MIT License.
|
|
2
2
|
// See the LICENSE file in the repo root for full license text.
|
|
3
|
+
import { finiteOrNull, isBufferSnapshot, isInteger, isRecord, restoreBuffer, restoreNumber, serialiseBuffer, } from "../bufferSnapshot.js";
|
|
3
4
|
import { Float64RingBuffer } from "../ringBuffer.js";
|
|
4
5
|
import { makeSeriesView } from "../seriesView.js";
|
|
5
6
|
const TA_SLOT_PREFIX = "ta:";
|
|
6
|
-
function isRecord(value) {
|
|
7
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8
|
-
}
|
|
9
|
-
function finiteOrNull(value) {
|
|
10
|
-
return Number.isFinite(value) ? value : null;
|
|
11
|
-
}
|
|
12
|
-
function restoreNumber(value) {
|
|
13
|
-
if (value === null)
|
|
14
|
-
return Number.NaN;
|
|
15
|
-
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
16
|
-
}
|
|
17
7
|
/**
|
|
18
8
|
* Restore a record of named numeric fields, returning `null` if any one
|
|
19
9
|
* field fails {@link restoreNumber}. Lets restore functions read several
|
|
@@ -32,35 +22,6 @@ function restoreNumbers(fields) {
|
|
|
32
22
|
}
|
|
33
23
|
return out;
|
|
34
24
|
}
|
|
35
|
-
function isInteger(value) {
|
|
36
|
-
return typeof value === "number" && Number.isInteger(value);
|
|
37
|
-
}
|
|
38
|
-
function isBufferSnapshot(value) {
|
|
39
|
-
if (!isRecord(value))
|
|
40
|
-
return false;
|
|
41
|
-
if (!isInteger(value.headIndex) || !isInteger(value.filled))
|
|
42
|
-
return false;
|
|
43
|
-
return (Array.isArray(value.values) &&
|
|
44
|
-
value.values.every((entry) => entry === null || (typeof entry === "number" && Number.isFinite(entry))));
|
|
45
|
-
}
|
|
46
|
-
function serialiseBuffer(buffer) {
|
|
47
|
-
const snapshot = buffer.serialiseSnapshotBuffer();
|
|
48
|
-
return {
|
|
49
|
-
headIndex: snapshot.headIndex,
|
|
50
|
-
filled: snapshot.filled,
|
|
51
|
-
values: snapshot.values,
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
function restoreBuffer(snapshot, capacity) {
|
|
55
|
-
const buffer = new Float64RingBuffer(capacity);
|
|
56
|
-
try {
|
|
57
|
-
buffer.restoreFromSnapshotBuffer(snapshot);
|
|
58
|
-
return buffer;
|
|
59
|
-
}
|
|
60
|
-
catch {
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
25
|
function baseSlot(outBuffer) {
|
|
65
26
|
return {
|
|
66
27
|
outBuffer,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persistence.js","sourceRoot":"","sources":["../../src/ta/persistence.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Invinite. Licensed under the MIT License.\n// See the LICENSE file in the repo root for full license text.\n\nimport type { JsonValue, Series } from \"@invinite-org/chartlang-core\";\n\nimport { Float64RingBuffer } from \"../ringBuffer.js\";\nimport { makeSeriesView } from \"../seriesView.js\";\nimport type { StreamState } from \"../streamState.js\";\n\nconst TA_SLOT_PREFIX = \"ta:\";\n\ntype BufferSnapshot = Readonly<{\n headIndex: number;\n filled: number;\n values: ReadonlyArray<number | null>;\n}>;\n\ntype RestoredBaseSlot = {\n readonly outBuffer: Float64RingBuffer;\n readonly series: Series<number>;\n readonly shiftedViews: Map<number, Series<number>>;\n};\n\ntype RestoredSmaSlot = RestoredBaseSlot & {\n readonly kind: \"ta.sma\";\n readonly length: number;\n readonly window: Float64RingBuffer;\n sum: number;\n};\n\ntype RestoredEmaSlot = RestoredBaseSlot & {\n readonly kind: \"ta.ema\";\n readonly alpha: number;\n readonly length: number;\n seedSum: number;\n seedCount: number;\n prevEma: number;\n prevClosedEma: number;\n};\n\ntype RestoredRsiSlot = RestoredBaseSlot & {\n readonly kind: \"ta.rsi\";\n readonly length: number;\n seedGainSum: number;\n seedLossSum: number;\n diffCount: number;\n avgGain: number;\n avgLoss: number;\n prevSrc: number;\n prevClosedSrc: number;\n};\n\ntype RestoredTaSlot = RestoredSmaSlot | RestoredEmaSlot | RestoredRsiSlot;\n\nfunction isRecord(value: unknown): value is Readonly<Record<string, unknown>> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction finiteOrNull(value: number): number | null {\n return Number.isFinite(value) ? value : null;\n}\n\nfunction restoreNumber(value: unknown): number | null {\n if (value === null) return Number.NaN;\n return typeof value === \"number\" && Number.isFinite(value) ? value : null;\n}\n\n/**\n * Restore a record of named numeric fields, returning `null` if any one\n * field fails {@link restoreNumber}. Lets restore functions read several\n * persisted numbers in one all-or-nothing step instead of an OR-chain of\n * per-field null checks.\n *\n * @internal\n */\nfunction restoreNumbers<K extends string>(\n fields: Readonly<Record<K, unknown>>,\n): Record<K, number> | null {\n const out = {} as Record<K, number>;\n for (const key of Object.keys(fields) as K[]) {\n const restored = restoreNumber(fields[key]);\n if (restored === null) return null;\n out[key] = restored;\n }\n return out;\n}\n\nfunction isInteger(value: unknown): value is number {\n return typeof value === \"number\" && Number.isInteger(value);\n}\n\nfunction isBufferSnapshot(value: unknown): value is BufferSnapshot {\n if (!isRecord(value)) return false;\n if (!isInteger(value.headIndex) || !isInteger(value.filled)) return false;\n return (\n Array.isArray(value.values) &&\n value.values.every(\n (entry) => entry === null || (typeof entry === \"number\" && Number.isFinite(entry)),\n )\n );\n}\n\nfunction serialiseBuffer(buffer: Float64RingBuffer): JsonValue {\n const snapshot = buffer.serialiseSnapshotBuffer();\n return {\n headIndex: snapshot.headIndex,\n filled: snapshot.filled,\n values: snapshot.values,\n };\n}\n\nfunction restoreBuffer(snapshot: BufferSnapshot, capacity: number): Float64RingBuffer | null {\n const buffer = new Float64RingBuffer(capacity);\n try {\n buffer.restoreFromSnapshotBuffer(snapshot);\n return buffer;\n } catch {\n return null;\n }\n}\n\nfunction baseSlot(outBuffer: Float64RingBuffer): RestoredBaseSlot {\n return {\n outBuffer,\n series: makeSeriesView<number>(outBuffer),\n shiftedViews: new Map<number, Series<number>>(),\n };\n}\n\nfunction isFloat64RingBuffer(value: unknown): value is Float64RingBuffer {\n return value instanceof Float64RingBuffer;\n}\n\nfunction serialiseSma(slot: Readonly<Record<string, unknown>>): JsonValue | null {\n if (\n slot.kind !== \"ta.sma\" ||\n typeof slot.length !== \"number\" ||\n typeof slot.sum !== \"number\" ||\n !isFloat64RingBuffer(slot.outBuffer) ||\n !isFloat64RingBuffer(slot.window)\n ) {\n return null;\n }\n return {\n kind: \"ta.sma\",\n length: slot.length,\n outBuffer: serialiseBuffer(slot.outBuffer),\n window: serialiseBuffer(slot.window),\n sum: finiteOrNull(slot.sum),\n };\n}\n\nfunction serialiseEma(slot: Readonly<Record<string, unknown>>): JsonValue | null {\n if (\n slot.kind !== \"ta.ema\" ||\n typeof slot.alpha !== \"number\" ||\n typeof slot.length !== \"number\" ||\n typeof slot.seedSum !== \"number\" ||\n typeof slot.seedCount !== \"number\" ||\n typeof slot.prevEma !== \"number\" ||\n typeof slot.prevClosedEma !== \"number\" ||\n !isFloat64RingBuffer(slot.outBuffer)\n ) {\n return null;\n }\n return {\n kind: \"ta.ema\",\n alpha: finiteOrNull(slot.alpha),\n length: slot.length,\n outBuffer: serialiseBuffer(slot.outBuffer),\n seedSum: finiteOrNull(slot.seedSum),\n seedCount: slot.seedCount,\n prevEma: finiteOrNull(slot.prevEma),\n prevClosedEma: finiteOrNull(slot.prevClosedEma),\n };\n}\n\nfunction serialiseRsi(slot: Readonly<Record<string, unknown>>): JsonValue | null {\n if (\n slot.kind !== \"ta.rsi\" ||\n typeof slot.length !== \"number\" ||\n typeof slot.seedGainSum !== \"number\" ||\n typeof slot.seedLossSum !== \"number\" ||\n typeof slot.diffCount !== \"number\" ||\n typeof slot.avgGain !== \"number\" ||\n typeof slot.avgLoss !== \"number\" ||\n typeof slot.prevSrc !== \"number\" ||\n typeof slot.prevClosedSrc !== \"number\" ||\n !isFloat64RingBuffer(slot.outBuffer)\n ) {\n return null;\n }\n return {\n kind: \"ta.rsi\",\n length: slot.length,\n outBuffer: serialiseBuffer(slot.outBuffer),\n seedGainSum: finiteOrNull(slot.seedGainSum),\n seedLossSum: finiteOrNull(slot.seedLossSum),\n diffCount: slot.diffCount,\n avgGain: finiteOrNull(slot.avgGain),\n avgLoss: finiteOrNull(slot.avgLoss),\n prevSrc: finiteOrNull(slot.prevSrc),\n prevClosedSrc: finiteOrNull(slot.prevClosedSrc),\n };\n}\n\nfunction restoreSma(snapshot: Readonly<Record<string, unknown>>): RestoredSmaSlot | null {\n const outSnapshot = snapshot.outBuffer;\n const windowSnapshot = snapshot.window;\n if (\n snapshot.kind !== \"ta.sma\" ||\n !isInteger(snapshot.length) ||\n !isBufferSnapshot(outSnapshot) ||\n !isBufferSnapshot(windowSnapshot)\n ) {\n return null;\n }\n const sum = restoreNumber(snapshot.sum);\n if (sum === null) return null;\n const outBuffer = restoreBuffer(outSnapshot, outSnapshot.values.length);\n const window = restoreBuffer(windowSnapshot, snapshot.length);\n if (outBuffer === null || window === null) return null;\n return {\n kind: \"ta.sma\",\n ...baseSlot(outBuffer),\n length: snapshot.length,\n window,\n sum,\n };\n}\n\nfunction restoreEma(snapshot: Readonly<Record<string, unknown>>): RestoredEmaSlot | null {\n const outSnapshot = snapshot.outBuffer;\n if (\n snapshot.kind !== \"ta.ema\" ||\n !isInteger(snapshot.length) ||\n !isInteger(snapshot.seedCount) ||\n !isBufferSnapshot(outSnapshot)\n ) {\n return null;\n }\n const numbers = restoreNumbers({\n alpha: snapshot.alpha,\n seedSum: snapshot.seedSum,\n prevEma: snapshot.prevEma,\n prevClosedEma: snapshot.prevClosedEma,\n });\n if (numbers === null) return null;\n const outBuffer = restoreBuffer(outSnapshot, outSnapshot.values.length);\n if (outBuffer === null) return null;\n return {\n kind: \"ta.ema\",\n ...baseSlot(outBuffer),\n alpha: numbers.alpha,\n length: snapshot.length,\n seedSum: numbers.seedSum,\n seedCount: snapshot.seedCount,\n prevEma: numbers.prevEma,\n prevClosedEma: numbers.prevClosedEma,\n };\n}\n\nfunction restoreRsi(snapshot: Readonly<Record<string, unknown>>): RestoredRsiSlot | null {\n const outSnapshot = snapshot.outBuffer;\n if (\n snapshot.kind !== \"ta.rsi\" ||\n !isInteger(snapshot.length) ||\n !isInteger(snapshot.diffCount) ||\n !isBufferSnapshot(outSnapshot)\n ) {\n return null;\n }\n const numbers = restoreNumbers({\n seedGainSum: snapshot.seedGainSum,\n seedLossSum: snapshot.seedLossSum,\n avgGain: snapshot.avgGain,\n avgLoss: snapshot.avgLoss,\n prevSrc: snapshot.prevSrc,\n prevClosedSrc: snapshot.prevClosedSrc,\n });\n if (numbers === null) return null;\n const outBuffer = restoreBuffer(outSnapshot, outSnapshot.values.length);\n if (outBuffer === null) return null;\n return {\n kind: \"ta.rsi\",\n ...baseSlot(outBuffer),\n length: snapshot.length,\n seedGainSum: numbers.seedGainSum,\n seedLossSum: numbers.seedLossSum,\n diffCount: snapshot.diffCount,\n avgGain: numbers.avgGain,\n avgLoss: numbers.avgLoss,\n prevSrc: numbers.prevSrc,\n prevClosedSrc: numbers.prevClosedSrc,\n };\n}\n\nfunction serialiseTaSlot(slot: unknown): JsonValue | null {\n if (!isRecord(slot)) return null;\n if (slot.kind === \"ta.sma\") return serialiseSma(slot);\n if (slot.kind === \"ta.ema\") return serialiseEma(slot);\n if (slot.kind === \"ta.rsi\") return serialiseRsi(slot);\n return null;\n}\n\nfunction restoreTaSlot(snapshot: unknown): RestoredTaSlot | null {\n if (!isRecord(snapshot)) return null;\n if (snapshot.kind === \"ta.sma\") return restoreSma(snapshot);\n if (snapshot.kind === \"ta.ema\") return restoreEma(snapshot);\n if (snapshot.kind === \"ta.rsi\") return restoreRsi(snapshot);\n return null;\n}\n\n/**\n * Return whether a snapshot slot key belongs to the TA persistence namespace.\n *\n * @since 0.5\n * @internal\n * @stable\n * @formula key.startsWith(\"ta:\")\n * @example\n * isTaSlotSnapshotKey(\"ta:slot#0\"); // true\n */\nexport function isTaSlotSnapshotKey(key: string): boolean {\n return key.startsWith(TA_SLOT_PREFIX);\n}\n\n/**\n * Serialise supported `ta.*` runtime slots into JSON-clean snapshot entries.\n *\n * @since 0.5\n * @internal\n * @stable\n * @formula snapshot[`ta:${slotId}`] = serialise(slot) for supported TA slots\n * @example\n * // const entries = serialiseTaSlots(stream);\n * const entries = {};\n * void entries;\n */\nexport function serialiseTaSlots(stream: StreamState): Readonly<Record<string, JsonValue>> {\n const out: Record<string, JsonValue> = {};\n for (const [slotId, slot] of stream.taSlots.entries()) {\n const snapshot = serialiseTaSlot(slot);\n if (snapshot !== null) {\n out[`${TA_SLOT_PREFIX}${slotId}`] = snapshot;\n }\n }\n return Object.freeze(out);\n}\n\n/**\n * Restore supported `ta.*` runtime slots from namespaced snapshot entries.\n *\n * @since 0.5\n * @internal\n * @stable\n * @formula stream.taSlots[slotId] = restore(snapshot[`ta:${slotId}`])\n * @example\n * // restoreTaSlots(stream, snapshot.slots);\n * const restored = true;\n * void restored;\n */\nexport function restoreTaSlots(\n stream: StreamState,\n slots: Readonly<Record<string, unknown>>,\n): void {\n stream.taSlots.clear();\n for (const [key, value] of Object.entries(slots)) {\n if (!isTaSlotSnapshotKey(key)) continue;\n const slot = restoreTaSlot(value);\n if (slot !== null) {\n stream.taSlots.set(key.slice(TA_SLOT_PREFIX.length), slot);\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"persistence.js","sourceRoot":"","sources":["../../src/ta/persistence.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAI/D,OAAO,EACH,YAAY,EACZ,gBAAgB,EAChB,SAAS,EACT,QAAQ,EACR,aAAa,EACb,aAAa,EACb,eAAe,GAClB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGlD,MAAM,cAAc,GAAG,KAAK,CAAC;AAuC7B;;;;;;;GAOG;AACH,SAAS,cAAc,CACnB,MAAoC;IAEpC,MAAM,GAAG,GAAG,EAAuB,CAAC;IACpC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAQ,EAAE,CAAC;QAC3C,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC5C,IAAI,QAAQ,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QACnC,GAAG,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC;IACxB,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAED,SAAS,QAAQ,CAAC,SAA4B;IAC1C,OAAO;QACH,SAAS;QACT,MAAM,EAAE,cAAc,CAAS,SAAS,CAAC;QACzC,YAAY,EAAE,IAAI,GAAG,EAA0B;KAClD,CAAC;AACN,CAAC;AAED,SAAS,mBAAmB,CAAC,KAAc;IACvC,OAAO,KAAK,YAAY,iBAAiB,CAAC;AAC9C,CAAC;AAED,SAAS,YAAY,CAAC,IAAuC;IACzD,IACI,IAAI,CAAC,IAAI,KAAK,QAAQ;QACtB,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ;QAC/B,OAAO,IAAI,CAAC,GAAG,KAAK,QAAQ;QAC5B,CAAC,mBAAmB,CAAC,IAAI,CAAC,SAAS,CAAC;QACpC,CAAC,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,EACnC,CAAC;QACC,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,OAAO;QACH,IAAI,EAAE,QAAQ;QACd,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,SAAS,EAAE,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC;QAC1C,MAAM,EAAE,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC;QACpC,GAAG,EAAE,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC;KAC9B,CAAC;AACN,CAAC;AAED,SAAS,YAAY,CAAC,IAAuC;IACzD,IACI,IAAI,CAAC,IAAI,KAAK,QAAQ;QACtB,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ;QAC9B,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ;QAC/B,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ;QAChC,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ;QAClC,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ;QAChC,OAAO,IAAI,CAAC,aAAa,KAAK,QAAQ;QACtC,CAAC,mBAAmB,CAAC,IAAI,CAAC,SAAS,CAAC,EACtC,CAAC;QACC,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,OAAO;QACH,IAAI,EAAE,QAAQ;QACd,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC;QAC/B,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,SAAS,EAAE,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC;QAC1C,OAAO,EAAE,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC;QACnC,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,OAAO,EAAE,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC;QACnC,aAAa,EAAE,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC;KAClD,CAAC;AACN,CAAC;AAED,SAAS,YAAY,CAAC,IAAuC;IACzD,IACI,IAAI,CAAC,IAAI,KAAK,QAAQ;QACtB,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ;QAC/B,OAAO,IAAI,CAAC,WAAW,KAAK,QAAQ;QACpC,OAAO,IAAI,CAAC,WAAW,KAAK,QAAQ;QACpC,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ;QAClC,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ;QAChC,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ;QAChC,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ;QAChC,OAAO,IAAI,CAAC,aAAa,KAAK,QAAQ;QACtC,CAAC,mBAAmB,CAAC,IAAI,CAAC,SAAS,CAAC,EACtC,CAAC;QACC,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,OAAO;QACH,IAAI,EAAE,QAAQ;QACd,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,SAAS,EAAE,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC;QAC1C,WAAW,EAAE,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC;QAC3C,WAAW,EAAE,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC;QAC3C,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,OAAO,EAAE,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC;QACnC,OAAO,EAAE,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC;QACnC,OAAO,EAAE,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC;QACnC,aAAa,EAAE,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC;KAClD,CAAC;AACN,CAAC;AAED,SAAS,UAAU,CAAC,QAA2C;IAC3D,MAAM,WAAW,GAAG,QAAQ,CAAC,SAAS,CAAC;IACvC,MAAM,cAAc,GAAG,QAAQ,CAAC,MAAM,CAAC;IACvC,IACI,QAAQ,CAAC,IAAI,KAAK,QAAQ;QAC1B,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC;QAC3B,CAAC,gBAAgB,CAAC,WAAW,CAAC;QAC9B,CAAC,gBAAgB,CAAC,cAAc,CAAC,EACnC,CAAC;QACC,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,MAAM,GAAG,GAAG,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IACxC,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC9B,MAAM,SAAS,GAAG,aAAa,CAAC,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACxE,MAAM,MAAM,GAAG,aAAa,CAAC,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC9D,IAAI,SAAS,KAAK,IAAI,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACvD,OAAO;QACH,IAAI,EAAE,QAAQ;QACd,GAAG,QAAQ,CAAC,SAAS,CAAC;QACtB,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,MAAM;QACN,GAAG;KACN,CAAC;AACN,CAAC;AAED,SAAS,UAAU,CAAC,QAA2C;IAC3D,MAAM,WAAW,GAAG,QAAQ,CAAC,SAAS,CAAC;IACvC,IACI,QAAQ,CAAC,IAAI,KAAK,QAAQ;QAC1B,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC;QAC3B,CAAC,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC;QAC9B,CAAC,gBAAgB,CAAC,WAAW,CAAC,EAChC,CAAC;QACC,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,MAAM,OAAO,GAAG,cAAc,CAAC;QAC3B,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,aAAa,EAAE,QAAQ,CAAC,aAAa;KACxC,CAAC,CAAC;IACH,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAClC,MAAM,SAAS,GAAG,aAAa,CAAC,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACxE,IAAI,SAAS,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACpC,OAAO;QACH,IAAI,EAAE,QAAQ;QACd,GAAG,QAAQ,CAAC,SAAS,CAAC;QACtB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,SAAS,EAAE,QAAQ,CAAC,SAAS;QAC7B,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,aAAa,EAAE,OAAO,CAAC,aAAa;KACvC,CAAC;AACN,CAAC;AAED,SAAS,UAAU,CAAC,QAA2C;IAC3D,MAAM,WAAW,GAAG,QAAQ,CAAC,SAAS,CAAC;IACvC,IACI,QAAQ,CAAC,IAAI,KAAK,QAAQ;QAC1B,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC;QAC3B,CAAC,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC;QAC9B,CAAC,gBAAgB,CAAC,WAAW,CAAC,EAChC,CAAC;QACC,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,MAAM,OAAO,GAAG,cAAc,CAAC;QAC3B,WAAW,EAAE,QAAQ,CAAC,WAAW;QACjC,WAAW,EAAE,QAAQ,CAAC,WAAW;QACjC,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,aAAa,EAAE,QAAQ,CAAC,aAAa;KACxC,CAAC,CAAC;IACH,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAClC,MAAM,SAAS,GAAG,aAAa,CAAC,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACxE,IAAI,SAAS,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACpC,OAAO;QACH,IAAI,EAAE,QAAQ;QACd,GAAG,QAAQ,CAAC,SAAS,CAAC;QACtB,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,SAAS,EAAE,QAAQ,CAAC,SAAS;QAC7B,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,aAAa,EAAE,OAAO,CAAC,aAAa;KACvC,CAAC;AACN,CAAC;AAED,SAAS,eAAe,CAAC,IAAa;IAClC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACjC,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;IACtD,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;IACtD,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;IACtD,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,SAAS,aAAa,CAAC,QAAiB;IACpC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IACrC,IAAI,QAAQ,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC5D,IAAI,QAAQ,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC5D,IAAI,QAAQ,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC5D,OAAO,IAAI,CAAC;AAChB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAW;IAC3C,OAAO,GAAG,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;AAC1C,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAmB;IAChD,MAAM,GAAG,GAA8B,EAAE,CAAC;IAC1C,KAAK,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;QACpD,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACpB,GAAG,CAAC,GAAG,cAAc,GAAG,MAAM,EAAE,CAAC,GAAG,QAAQ,CAAC;QACjD,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AAC9B,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,cAAc,CAC1B,MAAmB,EACnB,KAAwC;IAExC,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/C,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC;YAAE,SAAS;QACxC,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;QAClC,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAChB,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;QAC/D,CAAC;IACL,CAAC;AACL,CAAC","sourcesContent":["// Copyright (c) 2026 Invinite. Licensed under the MIT License.\n// See the LICENSE file in the repo root for full license text.\n\nimport type { JsonValue, Series } from \"@invinite-org/chartlang-core\";\n\nimport {\n finiteOrNull,\n isBufferSnapshot,\n isInteger,\n isRecord,\n restoreBuffer,\n restoreNumber,\n serialiseBuffer,\n} from \"../bufferSnapshot.js\";\nimport { Float64RingBuffer } from \"../ringBuffer.js\";\nimport { makeSeriesView } from \"../seriesView.js\";\nimport type { StreamState } from \"../streamState.js\";\n\nconst TA_SLOT_PREFIX = \"ta:\";\n\ntype RestoredBaseSlot = {\n readonly outBuffer: Float64RingBuffer;\n readonly series: Series<number>;\n readonly shiftedViews: Map<number, Series<number>>;\n};\n\ntype RestoredSmaSlot = RestoredBaseSlot & {\n readonly kind: \"ta.sma\";\n readonly length: number;\n readonly window: Float64RingBuffer;\n sum: number;\n};\n\ntype RestoredEmaSlot = RestoredBaseSlot & {\n readonly kind: \"ta.ema\";\n readonly alpha: number;\n readonly length: number;\n seedSum: number;\n seedCount: number;\n prevEma: number;\n prevClosedEma: number;\n};\n\ntype RestoredRsiSlot = RestoredBaseSlot & {\n readonly kind: \"ta.rsi\";\n readonly length: number;\n seedGainSum: number;\n seedLossSum: number;\n diffCount: number;\n avgGain: number;\n avgLoss: number;\n prevSrc: number;\n prevClosedSrc: number;\n};\n\ntype RestoredTaSlot = RestoredSmaSlot | RestoredEmaSlot | RestoredRsiSlot;\n\n/**\n * Restore a record of named numeric fields, returning `null` if any one\n * field fails {@link restoreNumber}. Lets restore functions read several\n * persisted numbers in one all-or-nothing step instead of an OR-chain of\n * per-field null checks.\n *\n * @internal\n */\nfunction restoreNumbers<K extends string>(\n fields: Readonly<Record<K, unknown>>,\n): Record<K, number> | null {\n const out = {} as Record<K, number>;\n for (const key of Object.keys(fields) as K[]) {\n const restored = restoreNumber(fields[key]);\n if (restored === null) return null;\n out[key] = restored;\n }\n return out;\n}\n\nfunction baseSlot(outBuffer: Float64RingBuffer): RestoredBaseSlot {\n return {\n outBuffer,\n series: makeSeriesView<number>(outBuffer),\n shiftedViews: new Map<number, Series<number>>(),\n };\n}\n\nfunction isFloat64RingBuffer(value: unknown): value is Float64RingBuffer {\n return value instanceof Float64RingBuffer;\n}\n\nfunction serialiseSma(slot: Readonly<Record<string, unknown>>): JsonValue | null {\n if (\n slot.kind !== \"ta.sma\" ||\n typeof slot.length !== \"number\" ||\n typeof slot.sum !== \"number\" ||\n !isFloat64RingBuffer(slot.outBuffer) ||\n !isFloat64RingBuffer(slot.window)\n ) {\n return null;\n }\n return {\n kind: \"ta.sma\",\n length: slot.length,\n outBuffer: serialiseBuffer(slot.outBuffer),\n window: serialiseBuffer(slot.window),\n sum: finiteOrNull(slot.sum),\n };\n}\n\nfunction serialiseEma(slot: Readonly<Record<string, unknown>>): JsonValue | null {\n if (\n slot.kind !== \"ta.ema\" ||\n typeof slot.alpha !== \"number\" ||\n typeof slot.length !== \"number\" ||\n typeof slot.seedSum !== \"number\" ||\n typeof slot.seedCount !== \"number\" ||\n typeof slot.prevEma !== \"number\" ||\n typeof slot.prevClosedEma !== \"number\" ||\n !isFloat64RingBuffer(slot.outBuffer)\n ) {\n return null;\n }\n return {\n kind: \"ta.ema\",\n alpha: finiteOrNull(slot.alpha),\n length: slot.length,\n outBuffer: serialiseBuffer(slot.outBuffer),\n seedSum: finiteOrNull(slot.seedSum),\n seedCount: slot.seedCount,\n prevEma: finiteOrNull(slot.prevEma),\n prevClosedEma: finiteOrNull(slot.prevClosedEma),\n };\n}\n\nfunction serialiseRsi(slot: Readonly<Record<string, unknown>>): JsonValue | null {\n if (\n slot.kind !== \"ta.rsi\" ||\n typeof slot.length !== \"number\" ||\n typeof slot.seedGainSum !== \"number\" ||\n typeof slot.seedLossSum !== \"number\" ||\n typeof slot.diffCount !== \"number\" ||\n typeof slot.avgGain !== \"number\" ||\n typeof slot.avgLoss !== \"number\" ||\n typeof slot.prevSrc !== \"number\" ||\n typeof slot.prevClosedSrc !== \"number\" ||\n !isFloat64RingBuffer(slot.outBuffer)\n ) {\n return null;\n }\n return {\n kind: \"ta.rsi\",\n length: slot.length,\n outBuffer: serialiseBuffer(slot.outBuffer),\n seedGainSum: finiteOrNull(slot.seedGainSum),\n seedLossSum: finiteOrNull(slot.seedLossSum),\n diffCount: slot.diffCount,\n avgGain: finiteOrNull(slot.avgGain),\n avgLoss: finiteOrNull(slot.avgLoss),\n prevSrc: finiteOrNull(slot.prevSrc),\n prevClosedSrc: finiteOrNull(slot.prevClosedSrc),\n };\n}\n\nfunction restoreSma(snapshot: Readonly<Record<string, unknown>>): RestoredSmaSlot | null {\n const outSnapshot = snapshot.outBuffer;\n const windowSnapshot = snapshot.window;\n if (\n snapshot.kind !== \"ta.sma\" ||\n !isInteger(snapshot.length) ||\n !isBufferSnapshot(outSnapshot) ||\n !isBufferSnapshot(windowSnapshot)\n ) {\n return null;\n }\n const sum = restoreNumber(snapshot.sum);\n if (sum === null) return null;\n const outBuffer = restoreBuffer(outSnapshot, outSnapshot.values.length);\n const window = restoreBuffer(windowSnapshot, snapshot.length);\n if (outBuffer === null || window === null) return null;\n return {\n kind: \"ta.sma\",\n ...baseSlot(outBuffer),\n length: snapshot.length,\n window,\n sum,\n };\n}\n\nfunction restoreEma(snapshot: Readonly<Record<string, unknown>>): RestoredEmaSlot | null {\n const outSnapshot = snapshot.outBuffer;\n if (\n snapshot.kind !== \"ta.ema\" ||\n !isInteger(snapshot.length) ||\n !isInteger(snapshot.seedCount) ||\n !isBufferSnapshot(outSnapshot)\n ) {\n return null;\n }\n const numbers = restoreNumbers({\n alpha: snapshot.alpha,\n seedSum: snapshot.seedSum,\n prevEma: snapshot.prevEma,\n prevClosedEma: snapshot.prevClosedEma,\n });\n if (numbers === null) return null;\n const outBuffer = restoreBuffer(outSnapshot, outSnapshot.values.length);\n if (outBuffer === null) return null;\n return {\n kind: \"ta.ema\",\n ...baseSlot(outBuffer),\n alpha: numbers.alpha,\n length: snapshot.length,\n seedSum: numbers.seedSum,\n seedCount: snapshot.seedCount,\n prevEma: numbers.prevEma,\n prevClosedEma: numbers.prevClosedEma,\n };\n}\n\nfunction restoreRsi(snapshot: Readonly<Record<string, unknown>>): RestoredRsiSlot | null {\n const outSnapshot = snapshot.outBuffer;\n if (\n snapshot.kind !== \"ta.rsi\" ||\n !isInteger(snapshot.length) ||\n !isInteger(snapshot.diffCount) ||\n !isBufferSnapshot(outSnapshot)\n ) {\n return null;\n }\n const numbers = restoreNumbers({\n seedGainSum: snapshot.seedGainSum,\n seedLossSum: snapshot.seedLossSum,\n avgGain: snapshot.avgGain,\n avgLoss: snapshot.avgLoss,\n prevSrc: snapshot.prevSrc,\n prevClosedSrc: snapshot.prevClosedSrc,\n });\n if (numbers === null) return null;\n const outBuffer = restoreBuffer(outSnapshot, outSnapshot.values.length);\n if (outBuffer === null) return null;\n return {\n kind: \"ta.rsi\",\n ...baseSlot(outBuffer),\n length: snapshot.length,\n seedGainSum: numbers.seedGainSum,\n seedLossSum: numbers.seedLossSum,\n diffCount: snapshot.diffCount,\n avgGain: numbers.avgGain,\n avgLoss: numbers.avgLoss,\n prevSrc: numbers.prevSrc,\n prevClosedSrc: numbers.prevClosedSrc,\n };\n}\n\nfunction serialiseTaSlot(slot: unknown): JsonValue | null {\n if (!isRecord(slot)) return null;\n if (slot.kind === \"ta.sma\") return serialiseSma(slot);\n if (slot.kind === \"ta.ema\") return serialiseEma(slot);\n if (slot.kind === \"ta.rsi\") return serialiseRsi(slot);\n return null;\n}\n\nfunction restoreTaSlot(snapshot: unknown): RestoredTaSlot | null {\n if (!isRecord(snapshot)) return null;\n if (snapshot.kind === \"ta.sma\") return restoreSma(snapshot);\n if (snapshot.kind === \"ta.ema\") return restoreEma(snapshot);\n if (snapshot.kind === \"ta.rsi\") return restoreRsi(snapshot);\n return null;\n}\n\n/**\n * Return whether a snapshot slot key belongs to the TA persistence namespace.\n *\n * @since 0.5\n * @internal\n * @stable\n * @formula key.startsWith(\"ta:\")\n * @example\n * isTaSlotSnapshotKey(\"ta:slot#0\"); // true\n */\nexport function isTaSlotSnapshotKey(key: string): boolean {\n return key.startsWith(TA_SLOT_PREFIX);\n}\n\n/**\n * Serialise supported `ta.*` runtime slots into JSON-clean snapshot entries.\n *\n * @since 0.5\n * @internal\n * @stable\n * @formula snapshot[`ta:${slotId}`] = serialise(slot) for supported TA slots\n * @example\n * // const entries = serialiseTaSlots(stream);\n * const entries = {};\n * void entries;\n */\nexport function serialiseTaSlots(stream: StreamState): Readonly<Record<string, JsonValue>> {\n const out: Record<string, JsonValue> = {};\n for (const [slotId, slot] of stream.taSlots.entries()) {\n const snapshot = serialiseTaSlot(slot);\n if (snapshot !== null) {\n out[`${TA_SLOT_PREFIX}${slotId}`] = snapshot;\n }\n }\n return Object.freeze(out);\n}\n\n/**\n * Restore supported `ta.*` runtime slots from namespaced snapshot entries.\n *\n * @since 0.5\n * @internal\n * @stable\n * @formula stream.taSlots[slotId] = restore(snapshot[`ta:${slotId}`])\n * @example\n * // restoreTaSlots(stream, snapshot.slots);\n * const restored = true;\n * void restored;\n */\nexport function restoreTaSlots(\n stream: StreamState,\n slots: Readonly<Record<string, unknown>>,\n): void {\n stream.taSlots.clear();\n for (const [key, value] of Object.entries(slots)) {\n if (!isTaSlotSnapshotKey(key)) continue;\n const slot = restoreTaSlot(value);\n if (slot !== null) {\n stream.taSlots.set(key.slice(TA_SLOT_PREFIX.length), slot);\n }\n }\n}\n"]}
|
package/dist/ta/ppo.d.ts
CHANGED
|
@@ -24,8 +24,9 @@ import { type ScalarOrSeries } from "./lib/sourceValue.js";
|
|
|
24
24
|
* @since 0.2
|
|
25
25
|
* @stable
|
|
26
26
|
*
|
|
27
|
-
* `opts.offset`
|
|
28
|
-
* `
|
|
27
|
+
* `opts.offset` is a presentation display shift carried to the plot
|
|
28
|
+
* emission as `xShift` for all three outputs in lockstep (`+n` right / future,
|
|
29
|
+
* `−n` left / past); the series values are unshifted.
|
|
29
30
|
*
|
|
30
31
|
* @example
|
|
31
32
|
* // import { ta } from "@invinite-org/chartlang-runtime";
|
package/dist/ta/ppo.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ppo.d.ts","sourceRoot":"","sources":["../../src/ta/ppo.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAU,MAAM,8BAA8B,CAAC;AAM/E,OAAO,EAAE,KAAK,cAAc,EAAmB,MAAM,sBAAsB,CAAC;AAqE5E
|
|
1
|
+
{"version":3,"file":"ppo.d.ts","sourceRoot":"","sources":["../../src/ta/ppo.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAU,MAAM,8BAA8B,CAAC;AAM/E,OAAO,EAAE,KAAK,cAAc,EAAmB,MAAM,sBAAsB,CAAC;AAqE5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,CAiCrF"}
|
package/dist/ta/ppo.js
CHANGED
|
@@ -84,8 +84,9 @@ function ppoValue(fast, slow) {
|
|
|
84
84
|
* @since 0.2
|
|
85
85
|
* @stable
|
|
86
86
|
*
|
|
87
|
-
* `opts.offset`
|
|
88
|
-
* `
|
|
87
|
+
* `opts.offset` is a presentation display shift carried to the plot
|
|
88
|
+
* emission as `xShift` for all three outputs in lockstep (`+n` right / future,
|
|
89
|
+
* `−n` left / past); the series values are unshifted.
|
|
89
90
|
*
|
|
90
91
|
* @example
|
|
91
92
|
* // import { ta } from "@invinite-org/chartlang-runtime";
|