@invinite-org/chartlang-runtime 1.1.1 → 1.2.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.
Files changed (226) hide show
  1. package/CHANGELOG.md +236 -0
  2. package/dist/barPoint.d.ts +20 -0
  3. package/dist/barPoint.d.ts.map +1 -0
  4. package/dist/barPoint.js +72 -0
  5. package/dist/barPoint.js.map +1 -0
  6. package/dist/bufferSnapshot.d.ts +102 -0
  7. package/dist/bufferSnapshot.d.ts.map +1 -0
  8. package/dist/bufferSnapshot.js +119 -0
  9. package/dist/bufferSnapshot.js.map +1 -0
  10. package/dist/createScriptRunner.d.ts +6 -3
  11. package/dist/createScriptRunner.d.ts.map +1 -1
  12. package/dist/createScriptRunner.js +29 -5
  13. package/dist/createScriptRunner.js.map +1 -1
  14. package/dist/dep/DepRunner.d.ts.map +1 -1
  15. package/dist/dep/DepRunner.js +1 -0
  16. package/dist/dep/DepRunner.js.map +1 -1
  17. package/dist/emit/draw/boxes/fillBetween.d.ts +45 -0
  18. package/dist/emit/draw/boxes/fillBetween.d.ts.map +1 -0
  19. package/dist/emit/draw/boxes/fillBetween.js +36 -0
  20. package/dist/emit/draw/boxes/fillBetween.js.map +1 -0
  21. package/dist/emit/draw/handle.d.ts +9 -0
  22. package/dist/emit/draw/handle.d.ts.map +1 -1
  23. package/dist/emit/draw/handle.js +65 -10
  24. package/dist/emit/draw/handle.js.map +1 -1
  25. package/dist/emit/draw/namespace.d.ts +4 -3
  26. package/dist/emit/draw/namespace.d.ts.map +1 -1
  27. package/dist/emit/draw/namespace.js +6 -3
  28. package/dist/emit/draw/namespace.js.map +1 -1
  29. package/dist/emit/plot.d.ts +7 -0
  30. package/dist/emit/plot.d.ts.map +1 -1
  31. package/dist/emit/plot.js +13 -0
  32. package/dist/emit/plot.js.map +1 -1
  33. package/dist/execution/dispose.d.ts.map +1 -1
  34. package/dist/execution/dispose.js +16 -0
  35. package/dist/execution/dispose.js.map +1 -1
  36. package/dist/execution/runComputeStep.d.ts.map +1 -1
  37. package/dist/execution/runComputeStep.js +10 -1
  38. package/dist/execution/runComputeStep.js.map +1 -1
  39. package/dist/index.d.ts +1 -1
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +1 -1
  42. package/dist/index.js.map +1 -1
  43. package/dist/persistentStateStore.runtime.d.ts.map +1 -1
  44. package/dist/persistentStateStore.runtime.js +21 -7
  45. package/dist/persistentStateStore.runtime.js.map +1 -1
  46. package/dist/request/index.d.ts +2 -1
  47. package/dist/request/index.d.ts.map +1 -1
  48. package/dist/request/index.js +2 -1
  49. package/dist/request/index.js.map +1 -1
  50. package/dist/request/requestNamespace.d.ts.map +1 -1
  51. package/dist/request/requestNamespace.js +16 -3
  52. package/dist/request/requestNamespace.js.map +1 -1
  53. package/dist/request/security.d.ts +20 -1
  54. package/dist/request/security.d.ts.map +1 -1
  55. package/dist/request/security.js +62 -23
  56. package/dist/request/security.js.map +1 -1
  57. package/dist/request/securityExprRunner.d.ts +133 -0
  58. package/dist/request/securityExprRunner.d.ts.map +1 -0
  59. package/dist/request/securityExprRunner.js +235 -0
  60. package/dist/request/securityExprRunner.js.map +1 -0
  61. package/dist/request/streamBars.d.ts +14 -1
  62. package/dist/request/streamBars.d.ts.map +1 -1
  63. package/dist/request/streamBars.js +39 -1
  64. package/dist/request/streamBars.js.map +1 -1
  65. package/dist/runtimeContext.d.ts +48 -0
  66. package/dist/runtimeContext.d.ts.map +1 -1
  67. package/dist/runtimeContext.js.map +1 -1
  68. package/dist/seriesView.d.ts +42 -17
  69. package/dist/seriesView.d.ts.map +1 -1
  70. package/dist/seriesView.js +65 -42
  71. package/dist/seriesView.js.map +1 -1
  72. package/dist/state/index.d.ts +2 -1
  73. package/dist/state/index.d.ts.map +1 -1
  74. package/dist/state/index.js +2 -1
  75. package/dist/state/index.js.map +1 -1
  76. package/dist/state/lifecycle.d.ts +40 -0
  77. package/dist/state/lifecycle.d.ts.map +1 -1
  78. package/dist/state/lifecycle.js +53 -0
  79. package/dist/state/lifecycle.js.map +1 -1
  80. package/dist/state/seriesPersistence.d.ts +48 -0
  81. package/dist/state/seriesPersistence.d.ts.map +1 -0
  82. package/dist/state/seriesPersistence.js +87 -0
  83. package/dist/state/seriesPersistence.js.map +1 -0
  84. package/dist/state/seriesSlot.d.ts +105 -0
  85. package/dist/state/seriesSlot.d.ts.map +1 -0
  86. package/dist/state/seriesSlot.js +123 -0
  87. package/dist/state/seriesSlot.js.map +1 -0
  88. package/dist/state/stateNamespace.d.ts.map +1 -1
  89. package/dist/state/stateNamespace.js +28 -0
  90. package/dist/state/stateNamespace.js.map +1 -1
  91. package/dist/streamState.d.ts +25 -19
  92. package/dist/streamState.d.ts.map +1 -1
  93. package/dist/streamState.js +40 -66
  94. package/dist/streamState.js.map +1 -1
  95. package/dist/ta/adx.d.ts +3 -2
  96. package/dist/ta/adx.d.ts.map +1 -1
  97. package/dist/ta/adx.js +3 -2
  98. package/dist/ta/adx.js.map +1 -1
  99. package/dist/ta/alma.d.ts +6 -4
  100. package/dist/ta/alma.d.ts.map +1 -1
  101. package/dist/ta/alma.js +19 -6
  102. package/dist/ta/alma.js.map +1 -1
  103. package/dist/ta/atr.d.ts +3 -2
  104. package/dist/ta/atr.d.ts.map +1 -1
  105. package/dist/ta/atr.js +3 -2
  106. package/dist/ta/atr.js.map +1 -1
  107. package/dist/ta/bb.d.ts +3 -2
  108. package/dist/ta/bb.d.ts.map +1 -1
  109. package/dist/ta/bb.js +3 -2
  110. package/dist/ta/bb.js.map +1 -1
  111. package/dist/ta/chaikinOsc.d.ts +3 -2
  112. package/dist/ta/chaikinOsc.d.ts.map +1 -1
  113. package/dist/ta/chaikinOsc.js +3 -2
  114. package/dist/ta/chaikinOsc.js.map +1 -1
  115. package/dist/ta/crossover.d.ts +3 -2
  116. package/dist/ta/crossover.d.ts.map +1 -1
  117. package/dist/ta/crossover.js +3 -2
  118. package/dist/ta/crossover.js.map +1 -1
  119. package/dist/ta/crossunder.d.ts +3 -2
  120. package/dist/ta/crossunder.d.ts.map +1 -1
  121. package/dist/ta/crossunder.js +3 -2
  122. package/dist/ta/crossunder.js.map +1 -1
  123. package/dist/ta/dmi.d.ts +4 -3
  124. package/dist/ta/dmi.d.ts.map +1 -1
  125. package/dist/ta/dmi.js +4 -3
  126. package/dist/ta/dmi.js.map +1 -1
  127. package/dist/ta/ema.d.ts +3 -2
  128. package/dist/ta/ema.d.ts.map +1 -1
  129. package/dist/ta/ema.js +3 -2
  130. package/dist/ta/ema.js.map +1 -1
  131. package/dist/ta/eom.d.ts +3 -1
  132. package/dist/ta/eom.d.ts.map +1 -1
  133. package/dist/ta/eom.js +3 -1
  134. package/dist/ta/eom.js.map +1 -1
  135. package/dist/ta/highestbars.d.ts +25 -0
  136. package/dist/ta/highestbars.d.ts.map +1 -0
  137. package/dist/ta/highestbars.js +106 -0
  138. package/dist/ta/highestbars.js.map +1 -0
  139. package/dist/ta/historicalVolatility.d.ts +3 -2
  140. package/dist/ta/historicalVolatility.d.ts.map +1 -1
  141. package/dist/ta/historicalVolatility.js +3 -2
  142. package/dist/ta/historicalVolatility.js.map +1 -1
  143. package/dist/ta/ichimoku.d.ts +3 -1
  144. package/dist/ta/ichimoku.d.ts.map +1 -1
  145. package/dist/ta/ichimoku.js +3 -1
  146. package/dist/ta/ichimoku.js.map +1 -1
  147. package/dist/ta/lowestbars.d.ts +25 -0
  148. package/dist/ta/lowestbars.d.ts.map +1 -0
  149. package/dist/ta/lowestbars.js +102 -0
  150. package/dist/ta/lowestbars.js.map +1 -0
  151. package/dist/ta/macd.d.ts +3 -2
  152. package/dist/ta/macd.d.ts.map +1 -1
  153. package/dist/ta/macd.js +3 -2
  154. package/dist/ta/macd.js.map +1 -1
  155. package/dist/ta/massIndex.d.ts +3 -2
  156. package/dist/ta/massIndex.d.ts.map +1 -1
  157. package/dist/ta/massIndex.js +3 -2
  158. package/dist/ta/massIndex.js.map +1 -1
  159. package/dist/ta/mfi.d.ts +3 -1
  160. package/dist/ta/mfi.d.ts.map +1 -1
  161. package/dist/ta/mfi.js +3 -1
  162. package/dist/ta/mfi.js.map +1 -1
  163. package/dist/ta/netVolume.d.ts +3 -1
  164. package/dist/ta/netVolume.d.ts.map +1 -1
  165. package/dist/ta/netVolume.js +3 -1
  166. package/dist/ta/netVolume.js.map +1 -1
  167. package/dist/ta/nvi.d.ts +3 -1
  168. package/dist/ta/nvi.d.ts.map +1 -1
  169. package/dist/ta/nvi.js +3 -1
  170. package/dist/ta/nvi.js.map +1 -1
  171. package/dist/ta/persistence.d.ts.map +1 -1
  172. package/dist/ta/persistence.js +1 -40
  173. package/dist/ta/persistence.js.map +1 -1
  174. package/dist/ta/ppo.d.ts +3 -2
  175. package/dist/ta/ppo.d.ts.map +1 -1
  176. package/dist/ta/ppo.js +3 -2
  177. package/dist/ta/ppo.js.map +1 -1
  178. package/dist/ta/pvi.d.ts +3 -1
  179. package/dist/ta/pvi.d.ts.map +1 -1
  180. package/dist/ta/pvi.js +3 -1
  181. package/dist/ta/pvi.js.map +1 -1
  182. package/dist/ta/pvo.d.ts +3 -1
  183. package/dist/ta/pvo.d.ts.map +1 -1
  184. package/dist/ta/pvo.js +3 -1
  185. package/dist/ta/pvo.js.map +1 -1
  186. package/dist/ta/pvt.d.ts +3 -1
  187. package/dist/ta/pvt.d.ts.map +1 -1
  188. package/dist/ta/pvt.js +3 -1
  189. package/dist/ta/pvt.js.map +1 -1
  190. package/dist/ta/registry.d.ts +7 -1
  191. package/dist/ta/registry.d.ts.map +1 -1
  192. package/dist/ta/registry.js +4 -0
  193. package/dist/ta/registry.js.map +1 -1
  194. package/dist/ta/rsi.d.ts +3 -2
  195. package/dist/ta/rsi.d.ts.map +1 -1
  196. package/dist/ta/rsi.js +3 -2
  197. package/dist/ta/rsi.js.map +1 -1
  198. package/dist/ta/rvi.d.ts +3 -2
  199. package/dist/ta/rvi.d.ts.map +1 -1
  200. package/dist/ta/rvi.js +3 -2
  201. package/dist/ta/rvi.js.map +1 -1
  202. package/dist/ta/sma.d.ts +6 -3
  203. package/dist/ta/sma.d.ts.map +1 -1
  204. package/dist/ta/sma.js +6 -3
  205. package/dist/ta/sma.js.map +1 -1
  206. package/dist/ta/stdev.d.ts +3 -2
  207. package/dist/ta/stdev.d.ts.map +1 -1
  208. package/dist/ta/stdev.js +3 -2
  209. package/dist/ta/stdev.js.map +1 -1
  210. package/dist/ta/trendStrengthIndex.d.ts +3 -2
  211. package/dist/ta/trendStrengthIndex.d.ts.map +1 -1
  212. package/dist/ta/trendStrengthIndex.js +3 -2
  213. package/dist/ta/trendStrengthIndex.js.map +1 -1
  214. package/dist/ta/trix.d.ts +4 -3
  215. package/dist/ta/trix.d.ts.map +1 -1
  216. package/dist/ta/trix.js +4 -3
  217. package/dist/ta/trix.js.map +1 -1
  218. package/dist/ta/vortex.d.ts +3 -2
  219. package/dist/ta/vortex.d.ts.map +1 -1
  220. package/dist/ta/vortex.js +3 -2
  221. package/dist/ta/vortex.js.map +1 -1
  222. package/package.json +3 -3
  223. package/dist/ta/lib/applyOffset.d.ts +0 -19
  224. package/dist/ta/lib/applyOffset.d.ts.map +0 -1
  225. package/dist/ta/lib/applyOffset.js +0 -38
  226. package/dist/ta/lib/applyOffset.js.map +0 -1
@@ -16,8 +16,9 @@ import { type ScalarOrSeries } from "./lib/sourceValue.js";
16
16
  * @since 0.2
17
17
  * @stable
18
18
  *
19
- * `opts.offset` shifts the returned series so `series.current` reads
20
- * the value `offset` bars ago.
19
+ * `opts.offset` is a presentation display shift carried to the plot
20
+ * emission as `xShift` (`+n` right / future, `−n` left / past); the
21
+ * series value is unshifted.
21
22
  *
22
23
  * @example
23
24
  * // import { ta } from "@invinite-org/chartlang-core";
@@ -1 +1 @@
1
- {"version":3,"file":"historicalVolatility.d.ts","sourceRoot":"","sources":["../../src/ta/historicalVolatility.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAKnE,OAAO,EAAE,KAAK,cAAc,EAAmB,MAAM,sBAAsB,CAAC;AA4J5E;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,oBAAoB,CAChC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,MAAM,GACd,MAAM,CAAC,MAAM,CAAC,CAehB"}
1
+ {"version":3,"file":"historicalVolatility.d.ts","sourceRoot":"","sources":["../../src/ta/historicalVolatility.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAKnE,OAAO,EAAE,KAAK,cAAc,EAAmB,MAAM,sBAAsB,CAAC;AA4J5E;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,oBAAoB,CAChC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,MAAM,GACd,MAAM,CAAC,MAAM,CAAC,CAehB"}
@@ -171,8 +171,9 @@ function tickValue(slot, src) {
171
171
  * @since 0.2
172
172
  * @stable
173
173
  *
174
- * `opts.offset` shifts the returned series so `series.current` reads
175
- * the value `offset` bars ago.
174
+ * `opts.offset` is a presentation display shift carried to the plot
175
+ * emission as `xShift` (`+n` right / future, `−n` left / past); the
176
+ * series value is unshifted.
176
177
  *
177
178
  * @example
178
179
  * // import { ta } from "@invinite-org/chartlang-core";
@@ -1 +1 @@
1
- {"version":3,"file":"historicalVolatility.js","sourceRoot":"","sources":["../../src/ta/historicalVolatility.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/historical-volatility.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 log-returns window is folded\n// incrementally per bar (one append per close, one head-replace per\n// tick) rather than invinite's full-array recompute. The Phase-1\n// `lib/rollingStddev` helper is the full-recompute reference the\n// property test asserts against.\n\nimport type { HvOpts, 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 { type ScalarOrSeries, readSourceValue } from \"./lib/sourceValue.js\";\n\nconst DEFAULT_ANNUALISATION_FACTOR = 365;\n\ntype HvSlot = {\n readonly outBuffer: Float64RingBuffer;\n readonly series: Series<number>;\n readonly length: number;\n readonly annualisationFactor: number;\n readonly logReturnsWindow: Float64RingBuffer;\n sumX: number;\n sumX2: number;\n /** Last closed source value (basis of next bar's log-return). */\n prevSrc: 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.historicalVolatility called outside an active script step\");\n }\n return ctx;\n}\n\nfunction initSlot(length: number, annualisationFactor: number, capacity: number): HvSlot {\n const outBuffer = new Float64RingBuffer(capacity);\n return {\n outBuffer,\n series: makeSeriesView<number>(outBuffer),\n length,\n annualisationFactor,\n logReturnsWindow: new Float64RingBuffer(length),\n sumX: 0,\n sumX2: 0,\n prevSrc: Number.NaN,\n shiftedViews: new Map(),\n };\n}\n\nfunction viewForOffset(slot: HvSlot, 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 logReturn(prev: number, cur: number): number {\n if (!Number.isFinite(prev) || !Number.isFinite(cur) || prev <= 0 || cur <= 0) {\n return Number.NaN;\n }\n return Math.log(cur / prev);\n}\n\nfunction windowStdDev(window: Float64RingBuffer, sumX: number, sumX2: number): number {\n const n = window.length;\n // Defensive: callers gate on `length === capacity` so n > 0 here.\n /* c8 ignore next */\n if (n <= 0) return Number.NaN;\n const mean = sumX / n;\n const variance = sumX2 / n - mean * mean;\n return Math.sqrt(Math.max(0, variance));\n}\n\n/**\n * Recompute the window-sums by walking the buffer. Called when a NaN\n * has been folded into the running sums (which would otherwise poison\n * every subsequent recompute) — restores the running invariant from\n * the live window contents. O(length) but only runs when needed.\n */\nfunction recomputeSums(slot: HvSlot): void {\n let sumX = 0;\n let sumX2 = 0;\n let anyNaN = false;\n for (let i = 0; i < slot.logReturnsWindow.length; i += 1) {\n const v = slot.logReturnsWindow.at(slot.logReturnsWindow.length - 1 - i);\n if (!Number.isFinite(v)) {\n anyNaN = true;\n break;\n }\n sumX += v;\n sumX2 += v * v;\n }\n if (anyNaN) {\n slot.sumX = Number.NaN;\n slot.sumX2 = Number.NaN;\n } else {\n slot.sumX = sumX;\n slot.sumX2 = sumX2;\n }\n}\n\nfunction closeValue(slot: HvSlot, src: number): number {\n const lr = logReturn(slot.prevSrc, src);\n if (slot.logReturnsWindow.length < slot.logReturnsWindow.capacity) {\n slot.logReturnsWindow.append(lr);\n if (Number.isFinite(lr)) {\n slot.sumX += lr;\n slot.sumX2 += lr * lr;\n } else {\n slot.sumX = Number.NaN;\n slot.sumX2 = Number.NaN;\n }\n slot.prevSrc = src;\n return Number.NaN;\n }\n const outgoing = slot.logReturnsWindow.at(slot.logReturnsWindow.length - 1);\n slot.logReturnsWindow.append(lr);\n if (!Number.isFinite(lr)) {\n slot.sumX = Number.NaN;\n slot.sumX2 = Number.NaN;\n } else if (\n !Number.isFinite(slot.sumX) ||\n /* c8 ignore next */\n !Number.isFinite(outgoing)\n ) {\n // A previous NaN poisoned the running sums; rebuild from the\n // live window. NaN inside short-circuits to NaN. The\n // `!Number.isFinite(outgoing)` half of the OR is defensive —\n // outgoing comes from the live window head and is only NaN\n // when a NaN was previously appended, which also poisoned\n // `sumX` (covered by the first half of the OR).\n recomputeSums(slot);\n } else {\n slot.sumX = slot.sumX - outgoing + lr;\n slot.sumX2 = slot.sumX2 - outgoing * outgoing + lr * lr;\n }\n slot.prevSrc = src;\n if (!Number.isFinite(slot.sumX)) return Number.NaN;\n const sd = windowStdDev(slot.logReturnsWindow, slot.sumX, slot.sumX2);\n return sd * Math.sqrt(slot.annualisationFactor) * 100;\n}\n\nfunction tickValue(slot: HvSlot, src: number): number {\n if (slot.logReturnsWindow.length < slot.logReturnsWindow.capacity) return Number.NaN;\n const lr = logReturn(slot.prevSrc, src);\n if (!Number.isFinite(lr) || !Number.isFinite(slot.sumX)) return Number.NaN;\n const oldestInHead = slot.logReturnsWindow.at(0);\n // Defensive: the close-side advance appends a finite lr (otherwise\n // sumX is poisoned and we returned at the guard above). The window\n // head can only be NaN if a prior NaN was appended, which is\n // already covered by the sumX guard.\n /* c8 ignore next */\n if (!Number.isFinite(oldestInHead)) return Number.NaN;\n const sumX = slot.sumX - oldestInHead + lr;\n const sumX2 = slot.sumX2 - oldestInHead * oldestInHead + lr * lr;\n const n = slot.logReturnsWindow.length;\n const mean = sumX / n;\n const variance = sumX2 / n - mean * mean;\n return Math.sqrt(Math.max(0, variance)) * Math.sqrt(slot.annualisationFactor) * 100;\n}\n\n/**\n * Historical Volatility — sub-pane line. Standard deviation of log\n * returns annualised + percent-scaled. Use `annualisationFactor: 365`\n * for crypto / 24-7 series (the default), `252` for trading-day\n * equity series. NaN through warmup (`[0, length − 1]`) — the first\n * log return lands at bar 1 and the window needs `length` of them.\n * NaN inputs (≤ 0 or non-finite source) yield NaN log-returns; any\n * NaN inside the window short-circuits the output to NaN.\n *\n * @formula lr[t] = ln(src[t] / src[t − 1]) ;\n * hv[t] = stddev(lr[t − length + 1..= t]) · sqrt(annualisationFactor) · 100\n * @warmup length\n * @anchors annualisationFactor\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 hv = ta.historicalVolatility(bar.close, 10);\n * // plot(hv);\n */\nexport function historicalVolatility(\n slotId: string,\n source: ScalarOrSeries,\n length: number,\n opts?: HvOpts,\n): Series<number> {\n const ctx = getCtx();\n let slot = ctx.stream.taSlots.get(slotId) as HvSlot | undefined;\n if (slot === undefined) {\n const annualisationFactor = opts?.annualisationFactor ?? DEFAULT_ANNUALISATION_FACTOR;\n slot = initSlot(length, annualisationFactor, ctx.stream.ohlcv.close.capacity);\n ctx.stream.taSlots.set(slotId, slot);\n }\n const src = readSourceValue(source);\n if (ctx.isTick) {\n slot.outBuffer.replaceHead(tickValue(slot, src));\n } else {\n slot.outBuffer.append(closeValue(slot, src));\n }\n return viewForOffset(slot, opts?.offset ?? 0);\n}\n"]}
1
+ {"version":3,"file":"historicalVolatility.js","sourceRoot":"","sources":["../../src/ta/historicalVolatility.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/historical-volatility.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 log-returns window is folded\n// incrementally per bar (one append per close, one head-replace per\n// tick) rather than invinite's full-array recompute. The Phase-1\n// `lib/rollingStddev` helper is the full-recompute reference the\n// property test asserts against.\n\nimport type { HvOpts, 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 { type ScalarOrSeries, readSourceValue } from \"./lib/sourceValue.js\";\n\nconst DEFAULT_ANNUALISATION_FACTOR = 365;\n\ntype HvSlot = {\n readonly outBuffer: Float64RingBuffer;\n readonly series: Series<number>;\n readonly length: number;\n readonly annualisationFactor: number;\n readonly logReturnsWindow: Float64RingBuffer;\n sumX: number;\n sumX2: number;\n /** Last closed source value (basis of next bar's log-return). */\n prevSrc: 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.historicalVolatility called outside an active script step\");\n }\n return ctx;\n}\n\nfunction initSlot(length: number, annualisationFactor: number, capacity: number): HvSlot {\n const outBuffer = new Float64RingBuffer(capacity);\n return {\n outBuffer,\n series: makeSeriesView<number>(outBuffer),\n length,\n annualisationFactor,\n logReturnsWindow: new Float64RingBuffer(length),\n sumX: 0,\n sumX2: 0,\n prevSrc: Number.NaN,\n shiftedViews: new Map(),\n };\n}\n\nfunction viewForOffset(slot: HvSlot, 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 logReturn(prev: number, cur: number): number {\n if (!Number.isFinite(prev) || !Number.isFinite(cur) || prev <= 0 || cur <= 0) {\n return Number.NaN;\n }\n return Math.log(cur / prev);\n}\n\nfunction windowStdDev(window: Float64RingBuffer, sumX: number, sumX2: number): number {\n const n = window.length;\n // Defensive: callers gate on `length === capacity` so n > 0 here.\n /* c8 ignore next */\n if (n <= 0) return Number.NaN;\n const mean = sumX / n;\n const variance = sumX2 / n - mean * mean;\n return Math.sqrt(Math.max(0, variance));\n}\n\n/**\n * Recompute the window-sums by walking the buffer. Called when a NaN\n * has been folded into the running sums (which would otherwise poison\n * every subsequent recompute) — restores the running invariant from\n * the live window contents. O(length) but only runs when needed.\n */\nfunction recomputeSums(slot: HvSlot): void {\n let sumX = 0;\n let sumX2 = 0;\n let anyNaN = false;\n for (let i = 0; i < slot.logReturnsWindow.length; i += 1) {\n const v = slot.logReturnsWindow.at(slot.logReturnsWindow.length - 1 - i);\n if (!Number.isFinite(v)) {\n anyNaN = true;\n break;\n }\n sumX += v;\n sumX2 += v * v;\n }\n if (anyNaN) {\n slot.sumX = Number.NaN;\n slot.sumX2 = Number.NaN;\n } else {\n slot.sumX = sumX;\n slot.sumX2 = sumX2;\n }\n}\n\nfunction closeValue(slot: HvSlot, src: number): number {\n const lr = logReturn(slot.prevSrc, src);\n if (slot.logReturnsWindow.length < slot.logReturnsWindow.capacity) {\n slot.logReturnsWindow.append(lr);\n if (Number.isFinite(lr)) {\n slot.sumX += lr;\n slot.sumX2 += lr * lr;\n } else {\n slot.sumX = Number.NaN;\n slot.sumX2 = Number.NaN;\n }\n slot.prevSrc = src;\n return Number.NaN;\n }\n const outgoing = slot.logReturnsWindow.at(slot.logReturnsWindow.length - 1);\n slot.logReturnsWindow.append(lr);\n if (!Number.isFinite(lr)) {\n slot.sumX = Number.NaN;\n slot.sumX2 = Number.NaN;\n } else if (\n !Number.isFinite(slot.sumX) ||\n /* c8 ignore next */\n !Number.isFinite(outgoing)\n ) {\n // A previous NaN poisoned the running sums; rebuild from the\n // live window. NaN inside short-circuits to NaN. The\n // `!Number.isFinite(outgoing)` half of the OR is defensive —\n // outgoing comes from the live window head and is only NaN\n // when a NaN was previously appended, which also poisoned\n // `sumX` (covered by the first half of the OR).\n recomputeSums(slot);\n } else {\n slot.sumX = slot.sumX - outgoing + lr;\n slot.sumX2 = slot.sumX2 - outgoing * outgoing + lr * lr;\n }\n slot.prevSrc = src;\n if (!Number.isFinite(slot.sumX)) return Number.NaN;\n const sd = windowStdDev(slot.logReturnsWindow, slot.sumX, slot.sumX2);\n return sd * Math.sqrt(slot.annualisationFactor) * 100;\n}\n\nfunction tickValue(slot: HvSlot, src: number): number {\n if (slot.logReturnsWindow.length < slot.logReturnsWindow.capacity) return Number.NaN;\n const lr = logReturn(slot.prevSrc, src);\n if (!Number.isFinite(lr) || !Number.isFinite(slot.sumX)) return Number.NaN;\n const oldestInHead = slot.logReturnsWindow.at(0);\n // Defensive: the close-side advance appends a finite lr (otherwise\n // sumX is poisoned and we returned at the guard above). The window\n // head can only be NaN if a prior NaN was appended, which is\n // already covered by the sumX guard.\n /* c8 ignore next */\n if (!Number.isFinite(oldestInHead)) return Number.NaN;\n const sumX = slot.sumX - oldestInHead + lr;\n const sumX2 = slot.sumX2 - oldestInHead * oldestInHead + lr * lr;\n const n = slot.logReturnsWindow.length;\n const mean = sumX / n;\n const variance = sumX2 / n - mean * mean;\n return Math.sqrt(Math.max(0, variance)) * Math.sqrt(slot.annualisationFactor) * 100;\n}\n\n/**\n * Historical Volatility — sub-pane line. Standard deviation of log\n * returns annualised + percent-scaled. Use `annualisationFactor: 365`\n * for crypto / 24-7 series (the default), `252` for trading-day\n * equity series. NaN through warmup (`[0, length − 1]`) — the first\n * log return lands at bar 1 and the window needs `length` of them.\n * NaN inputs (≤ 0 or non-finite source) yield NaN log-returns; any\n * NaN inside the window short-circuits the output to NaN.\n *\n * @formula lr[t] = ln(src[t] / src[t − 1]) ;\n * hv[t] = stddev(lr[t − length + 1..= t]) · sqrt(annualisationFactor) · 100\n * @warmup length\n * @anchors annualisationFactor\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 hv = ta.historicalVolatility(bar.close, 10);\n * // plot(hv);\n */\nexport function historicalVolatility(\n slotId: string,\n source: ScalarOrSeries,\n length: number,\n opts?: HvOpts,\n): Series<number> {\n const ctx = getCtx();\n let slot = ctx.stream.taSlots.get(slotId) as HvSlot | undefined;\n if (slot === undefined) {\n const annualisationFactor = opts?.annualisationFactor ?? DEFAULT_ANNUALISATION_FACTOR;\n slot = initSlot(length, annualisationFactor, ctx.stream.ohlcv.close.capacity);\n ctx.stream.taSlots.set(slotId, slot);\n }\n const src = readSourceValue(source);\n if (ctx.isTick) {\n slot.outBuffer.replaceHead(tickValue(slot, src));\n } else {\n slot.outBuffer.append(closeValue(slot, src));\n }\n return viewForOffset(slot, opts?.offset ?? 0);\n}\n"]}
@@ -27,7 +27,9 @@ import type { IchimokuOpts, IchimokuResult } from "@invinite-org/chartlang-core"
27
27
  * @since 0.2
28
28
  * @stable
29
29
  *
30
- * `opts.offset` shifts all five outputs in lockstep.
30
+ * `opts.offset` is a presentation display shift carried to the plot
31
+ * emission as `xShift` for all five outputs in lockstep (`+n` right / future,
32
+ * `−n` left / past); the series values are unshifted.
31
33
  *
32
34
  * @example
33
35
  * // import { ta } from "@invinite-org/chartlang-runtime";
@@ -1 +1 @@
1
- {"version":3,"file":"ichimoku.d.ts","sourceRoot":"","sources":["../../src/ta/ichimoku.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAqHjF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,cAAc,CA8D5E"}
1
+ {"version":3,"file":"ichimoku.d.ts","sourceRoot":"","sources":["../../src/ta/ichimoku.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAqHjF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,cAAc,CA8D5E"}
@@ -121,7 +121,9 @@ function resultForOffset(slot, offset) {
121
121
  * @since 0.2
122
122
  * @stable
123
123
  *
124
- * `opts.offset` shifts all five outputs in lockstep.
124
+ * `opts.offset` is a presentation display shift carried to the plot
125
+ * emission as `xShift` for all five outputs in lockstep (`+n` right / future,
126
+ * `−n` left / past); the series values are unshifted.
125
127
  *
126
128
  * @example
127
129
  * // import { ta } from "@invinite-org/chartlang-runtime";
@@ -1 +1 @@
1
- {"version":3,"file":"ichimoku.js","sourceRoot":"","sources":["../../src/ta/ichimoku.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/ichimoku.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. Ichimoku composes six `ta.highest` / `ta.lowest`\n// sub-slots (one pair each for Tenkan / Kijun / Senkou B) — the same\n// composition seam `donchian.ts` uses — so a fix to highest/lowest\n// flows in for free. The forward-displaced Senkou A / Senkou B and\n// backward-displaced Chikou are produced via per-slot delay ring\n// buffers of capacity `displacement + 1`.\n\nimport type { IchimokuOpts, IchimokuResult } 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 { highest } from \"./highest.js\";\nimport { lowest } from \"./lowest.js\";\n\nconst DEFAULT_CONVERSION_LENGTH = 9;\nconst DEFAULT_BASE_LENGTH = 26;\nconst DEFAULT_LEADING_SPAN_B_LENGTH = 52;\nconst DEFAULT_DISPLACEMENT = 26;\n\ntype IchimokuSlot = {\n readonly outputs: IchimokuResult;\n readonly tenkanBuffer: Float64RingBuffer;\n readonly kijunBuffer: Float64RingBuffer;\n readonly senkouABuffer: Float64RingBuffer;\n readonly senkouBBuffer: Float64RingBuffer;\n readonly chikouBuffer: Float64RingBuffer;\n readonly conversionLength: number;\n readonly baseLength: number;\n readonly leadingSpanBLength: number;\n readonly displacement: number;\n /** Forward-shift delay for senkouA: capacity `displacement + 1`.\n * At each close we append the new `senkouARaw`; `at(displacement)`\n * gives the value from `displacement` closes ago — exactly the\n * value we emit for `senkouA[t]`. */\n readonly senkouADelay: Float64RingBuffer;\n readonly senkouBDelay: Float64RingBuffer;\n /** Backward-shift delay for chikou: capacity `displacement + 1`.\n * `chikou[t] = close[t − displacement]` — at each close we append\n * the new close; `at(displacement)` gives the value from\n * `displacement` closes ago. */\n readonly closeDelay: Float64RingBuffer;\n readonly shiftedResults: Map<number, IchimokuResult>;\n};\n\nfunction getCtx(): RuntimeContext {\n const ctx = ACTIVE_RUNTIME_CONTEXT.current;\n if (ctx === null) {\n throw new Error(\"ta.ichimoku called outside an active script step\");\n }\n return ctx;\n}\n\nfunction midpoint(hi: number, lo: number): number {\n if (!Number.isFinite(hi) || !Number.isFinite(lo)) return Number.NaN;\n return (hi + lo) / 2;\n}\n\nfunction spanAverage(a: number, b: number): number {\n if (!Number.isFinite(a) || !Number.isFinite(b)) return Number.NaN;\n return (a + b) / 2;\n}\n\nfunction delayedValue(delay: Float64RingBuffer, displacement: number): number {\n if (delay.length <= displacement) return Number.NaN;\n return delay.at(displacement);\n}\n\nfunction initSlot(\n capacity: number,\n conversionLength: number,\n baseLength: number,\n leadingSpanBLength: number,\n displacement: number,\n): IchimokuSlot {\n const tenkanBuffer = new Float64RingBuffer(capacity);\n const kijunBuffer = new Float64RingBuffer(capacity);\n const senkouABuffer = new Float64RingBuffer(capacity);\n const senkouBBuffer = new Float64RingBuffer(capacity);\n const chikouBuffer = new Float64RingBuffer(capacity);\n // The delay ring buffers need only `displacement + 1` slots — at\n // each close we append the new raw value; `at(displacement)`\n // returns the value from `displacement` closes ago.\n const delayCap = displacement + 1;\n return {\n outputs: Object.freeze({\n tenkan: makeSeriesView<number>(tenkanBuffer),\n kijun: makeSeriesView<number>(kijunBuffer),\n senkouA: makeSeriesView<number>(senkouABuffer),\n senkouB: makeSeriesView<number>(senkouBBuffer),\n chikou: makeSeriesView<number>(chikouBuffer),\n }),\n tenkanBuffer,\n kijunBuffer,\n senkouABuffer,\n senkouBBuffer,\n chikouBuffer,\n conversionLength,\n baseLength,\n leadingSpanBLength,\n displacement,\n senkouADelay: new Float64RingBuffer(delayCap),\n senkouBDelay: new Float64RingBuffer(delayCap),\n closeDelay: new Float64RingBuffer(delayCap),\n shiftedResults: new Map(),\n };\n}\n\nfunction resultForOffset(slot: IchimokuSlot, offset: number): IchimokuResult {\n if (offset === 0) return slot.outputs;\n let cached = slot.shiftedResults.get(offset);\n if (cached === undefined) {\n cached = Object.freeze({\n tenkan: makeShiftedSeriesView<number>(slot.tenkanBuffer, offset),\n kijun: makeShiftedSeriesView<number>(slot.kijunBuffer, offset),\n senkouA: makeShiftedSeriesView<number>(slot.senkouABuffer, offset),\n senkouB: makeShiftedSeriesView<number>(slot.senkouBBuffer, offset),\n chikou: makeShiftedSeriesView<number>(slot.chikouBuffer, offset),\n });\n slot.shiftedResults.set(offset, cached);\n }\n return cached;\n}\n\n/**\n * Ichimoku Cloud — five-line trend / cloud overlay. Composes six\n * `ta.highest` / `ta.lowest` sub-slots (Tenkan high+low, Kijun\n * high+low, Senkou B high+low) — the same composition seam\n * `ta.donchian` uses — so a fix to either rolling-extreme primitive\n * flows in for free. Forward-displaced Senkou A / Senkou B and\n * backward-displaced Chikou are produced via per-slot delay ring\n * buffers of capacity `displacement + 1`.\n *\n * `chikou.current` returns `close[t − displacement]` (the close from\n * `displacement` bars ago, plotted at today's position — the\n * lagging-span semantic). Renderer-side, the script author may\n * choose to draw it at the visually-offset position; the runtime\n * exposes the backward-shifted value so script-author conditionals\n * (cross / alert) see meaningful data.\n *\n * @formula tenkan[t] = (highest(high, conversionLength) + lowest(low, conversionLength)) / 2 ;\n * kijun[t] = (highest(high, baseLength) + lowest(low, baseLength)) / 2 ;\n * senkouARaw = (tenkan + kijun) / 2 ;\n * senkouBRaw = (highest(high, leadingSpanBLength) + lowest(low, leadingSpanBLength)) / 2 ;\n * senkouA[t] = senkouARaw[t − displacement] ;\n * senkouB[t] = senkouBRaw[t − displacement] ;\n * chikou[t] = close[t − displacement]\n * @anchors displacement, conversionLength, baseLength, leadingSpanBLength\n * @warmup max(conversionLength, baseLength, leadingSpanBLength) + displacement − 1\n * @since 0.2\n * @stable\n *\n * `opts.offset` shifts all five outputs in lockstep.\n *\n * @example\n * // import { ta } from \"@invinite-org/chartlang-runtime\";\n * // const i = ta.ichimoku(\"slot\");\n * // plot(i.tenkan);\n * // plot(i.kijun);\n * // plot(i.senkouA);\n * // plot(i.senkouB);\n * // plot(i.chikou);\n */\nexport function ichimoku(slotId: string, opts?: IchimokuOpts): IchimokuResult {\n const ctx = getCtx();\n const conversionLength = opts?.conversionLength ?? DEFAULT_CONVERSION_LENGTH;\n const baseLength = opts?.baseLength ?? DEFAULT_BASE_LENGTH;\n const leadingSpanBLength = opts?.leadingSpanBLength ?? DEFAULT_LEADING_SPAN_B_LENGTH;\n const displacement = opts?.displacement ?? DEFAULT_DISPLACEMENT;\n const offset = opts?.offset ?? 0;\n const bar = ctx.stream.bar;\n\n let slot = ctx.stream.taSlots.get(slotId) as IchimokuSlot | undefined;\n if (slot === undefined) {\n slot = initSlot(\n ctx.stream.ohlcv.close.capacity,\n conversionLength,\n baseLength,\n leadingSpanBLength,\n displacement,\n );\n ctx.stream.taSlots.set(slotId, slot);\n }\n\n // Compose: rolling extremes via the registered ta.highest /\n // ta.lowest sub-slots. Each composed call advances the sub-slot on\n // the same close (or replaces its head on the same tick).\n const tenkanHi = highest(`${slotId}/tenkanHigh`, bar.high, conversionLength).current;\n const tenkanLo = lowest(`${slotId}/tenkanLow`, bar.low, conversionLength).current;\n const kijunHi = highest(`${slotId}/kijunHigh`, bar.high, baseLength).current;\n const kijunLo = lowest(`${slotId}/kijunLow`, bar.low, baseLength).current;\n const senkouBHi = highest(`${slotId}/senkouBHigh`, bar.high, leadingSpanBLength).current;\n const senkouBLo = lowest(`${slotId}/senkouBLow`, bar.low, leadingSpanBLength).current;\n\n const tenkan = midpoint(tenkanHi, tenkanLo);\n const kijun = midpoint(kijunHi, kijunLo);\n const senkouBRaw = midpoint(senkouBHi, senkouBLo);\n const senkouARaw = spanAverage(tenkan, kijun);\n\n if (ctx.isTick) {\n // Replace the head of the delay buffers with the tick's\n // updated raw values. `at(displacement)` then returns the\n // SAME value as before (the displacement-shifted slot is not\n // affected by the head's value), so the displaced output\n // heads are stable on tick.\n slot.senkouADelay.replaceHead(senkouARaw);\n slot.senkouBDelay.replaceHead(senkouBRaw);\n slot.closeDelay.replaceHead(bar.close);\n slot.tenkanBuffer.replaceHead(tenkan);\n slot.kijunBuffer.replaceHead(kijun);\n slot.senkouABuffer.replaceHead(delayedValue(slot.senkouADelay, displacement));\n slot.senkouBBuffer.replaceHead(delayedValue(slot.senkouBDelay, displacement));\n slot.chikouBuffer.replaceHead(delayedValue(slot.closeDelay, displacement));\n } else {\n slot.senkouADelay.append(senkouARaw);\n slot.senkouBDelay.append(senkouBRaw);\n slot.closeDelay.append(bar.close);\n slot.tenkanBuffer.append(tenkan);\n slot.kijunBuffer.append(kijun);\n slot.senkouABuffer.append(delayedValue(slot.senkouADelay, displacement));\n slot.senkouBBuffer.append(delayedValue(slot.senkouBDelay, displacement));\n slot.chikouBuffer.append(delayedValue(slot.closeDelay, displacement));\n }\n\n return resultForOffset(slot, offset);\n}\n"]}
1
+ {"version":3,"file":"ichimoku.js","sourceRoot":"","sources":["../../src/ta/ichimoku.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/ichimoku.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. Ichimoku composes six `ta.highest` / `ta.lowest`\n// sub-slots (one pair each for Tenkan / Kijun / Senkou B) — the same\n// composition seam `donchian.ts` uses — so a fix to highest/lowest\n// flows in for free. The forward-displaced Senkou A / Senkou B and\n// backward-displaced Chikou are produced via per-slot delay ring\n// buffers of capacity `displacement + 1`.\n\nimport type { IchimokuOpts, IchimokuResult } 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 { highest } from \"./highest.js\";\nimport { lowest } from \"./lowest.js\";\n\nconst DEFAULT_CONVERSION_LENGTH = 9;\nconst DEFAULT_BASE_LENGTH = 26;\nconst DEFAULT_LEADING_SPAN_B_LENGTH = 52;\nconst DEFAULT_DISPLACEMENT = 26;\n\ntype IchimokuSlot = {\n readonly outputs: IchimokuResult;\n readonly tenkanBuffer: Float64RingBuffer;\n readonly kijunBuffer: Float64RingBuffer;\n readonly senkouABuffer: Float64RingBuffer;\n readonly senkouBBuffer: Float64RingBuffer;\n readonly chikouBuffer: Float64RingBuffer;\n readonly conversionLength: number;\n readonly baseLength: number;\n readonly leadingSpanBLength: number;\n readonly displacement: number;\n /** Forward-shift delay for senkouA: capacity `displacement + 1`.\n * At each close we append the new `senkouARaw`; `at(displacement)`\n * gives the value from `displacement` closes ago — exactly the\n * value we emit for `senkouA[t]`. */\n readonly senkouADelay: Float64RingBuffer;\n readonly senkouBDelay: Float64RingBuffer;\n /** Backward-shift delay for chikou: capacity `displacement + 1`.\n * `chikou[t] = close[t − displacement]` — at each close we append\n * the new close; `at(displacement)` gives the value from\n * `displacement` closes ago. */\n readonly closeDelay: Float64RingBuffer;\n readonly shiftedResults: Map<number, IchimokuResult>;\n};\n\nfunction getCtx(): RuntimeContext {\n const ctx = ACTIVE_RUNTIME_CONTEXT.current;\n if (ctx === null) {\n throw new Error(\"ta.ichimoku called outside an active script step\");\n }\n return ctx;\n}\n\nfunction midpoint(hi: number, lo: number): number {\n if (!Number.isFinite(hi) || !Number.isFinite(lo)) return Number.NaN;\n return (hi + lo) / 2;\n}\n\nfunction spanAverage(a: number, b: number): number {\n if (!Number.isFinite(a) || !Number.isFinite(b)) return Number.NaN;\n return (a + b) / 2;\n}\n\nfunction delayedValue(delay: Float64RingBuffer, displacement: number): number {\n if (delay.length <= displacement) return Number.NaN;\n return delay.at(displacement);\n}\n\nfunction initSlot(\n capacity: number,\n conversionLength: number,\n baseLength: number,\n leadingSpanBLength: number,\n displacement: number,\n): IchimokuSlot {\n const tenkanBuffer = new Float64RingBuffer(capacity);\n const kijunBuffer = new Float64RingBuffer(capacity);\n const senkouABuffer = new Float64RingBuffer(capacity);\n const senkouBBuffer = new Float64RingBuffer(capacity);\n const chikouBuffer = new Float64RingBuffer(capacity);\n // The delay ring buffers need only `displacement + 1` slots — at\n // each close we append the new raw value; `at(displacement)`\n // returns the value from `displacement` closes ago.\n const delayCap = displacement + 1;\n return {\n outputs: Object.freeze({\n tenkan: makeSeriesView<number>(tenkanBuffer),\n kijun: makeSeriesView<number>(kijunBuffer),\n senkouA: makeSeriesView<number>(senkouABuffer),\n senkouB: makeSeriesView<number>(senkouBBuffer),\n chikou: makeSeriesView<number>(chikouBuffer),\n }),\n tenkanBuffer,\n kijunBuffer,\n senkouABuffer,\n senkouBBuffer,\n chikouBuffer,\n conversionLength,\n baseLength,\n leadingSpanBLength,\n displacement,\n senkouADelay: new Float64RingBuffer(delayCap),\n senkouBDelay: new Float64RingBuffer(delayCap),\n closeDelay: new Float64RingBuffer(delayCap),\n shiftedResults: new Map(),\n };\n}\n\nfunction resultForOffset(slot: IchimokuSlot, offset: number): IchimokuResult {\n if (offset === 0) return slot.outputs;\n let cached = slot.shiftedResults.get(offset);\n if (cached === undefined) {\n cached = Object.freeze({\n tenkan: makeShiftedSeriesView<number>(slot.tenkanBuffer, offset),\n kijun: makeShiftedSeriesView<number>(slot.kijunBuffer, offset),\n senkouA: makeShiftedSeriesView<number>(slot.senkouABuffer, offset),\n senkouB: makeShiftedSeriesView<number>(slot.senkouBBuffer, offset),\n chikou: makeShiftedSeriesView<number>(slot.chikouBuffer, offset),\n });\n slot.shiftedResults.set(offset, cached);\n }\n return cached;\n}\n\n/**\n * Ichimoku Cloud — five-line trend / cloud overlay. Composes six\n * `ta.highest` / `ta.lowest` sub-slots (Tenkan high+low, Kijun\n * high+low, Senkou B high+low) — the same composition seam\n * `ta.donchian` uses — so a fix to either rolling-extreme primitive\n * flows in for free. Forward-displaced Senkou A / Senkou B and\n * backward-displaced Chikou are produced via per-slot delay ring\n * buffers of capacity `displacement + 1`.\n *\n * `chikou.current` returns `close[t − displacement]` (the close from\n * `displacement` bars ago, plotted at today's position — the\n * lagging-span semantic). Renderer-side, the script author may\n * choose to draw it at the visually-offset position; the runtime\n * exposes the backward-shifted value so script-author conditionals\n * (cross / alert) see meaningful data.\n *\n * @formula tenkan[t] = (highest(high, conversionLength) + lowest(low, conversionLength)) / 2 ;\n * kijun[t] = (highest(high, baseLength) + lowest(low, baseLength)) / 2 ;\n * senkouARaw = (tenkan + kijun) / 2 ;\n * senkouBRaw = (highest(high, leadingSpanBLength) + lowest(low, leadingSpanBLength)) / 2 ;\n * senkouA[t] = senkouARaw[t − displacement] ;\n * senkouB[t] = senkouBRaw[t − displacement] ;\n * chikou[t] = close[t − displacement]\n * @anchors displacement, conversionLength, baseLength, leadingSpanBLength\n * @warmup max(conversionLength, baseLength, leadingSpanBLength) + displacement − 1\n * @since 0.2\n * @stable\n *\n * `opts.offset` is a presentation display shift carried to the plot\n * emission as `xShift` for all five 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 i = ta.ichimoku(\"slot\");\n * // plot(i.tenkan);\n * // plot(i.kijun);\n * // plot(i.senkouA);\n * // plot(i.senkouB);\n * // plot(i.chikou);\n */\nexport function ichimoku(slotId: string, opts?: IchimokuOpts): IchimokuResult {\n const ctx = getCtx();\n const conversionLength = opts?.conversionLength ?? DEFAULT_CONVERSION_LENGTH;\n const baseLength = opts?.baseLength ?? DEFAULT_BASE_LENGTH;\n const leadingSpanBLength = opts?.leadingSpanBLength ?? DEFAULT_LEADING_SPAN_B_LENGTH;\n const displacement = opts?.displacement ?? DEFAULT_DISPLACEMENT;\n const offset = opts?.offset ?? 0;\n const bar = ctx.stream.bar;\n\n let slot = ctx.stream.taSlots.get(slotId) as IchimokuSlot | undefined;\n if (slot === undefined) {\n slot = initSlot(\n ctx.stream.ohlcv.close.capacity,\n conversionLength,\n baseLength,\n leadingSpanBLength,\n displacement,\n );\n ctx.stream.taSlots.set(slotId, slot);\n }\n\n // Compose: rolling extremes via the registered ta.highest /\n // ta.lowest sub-slots. Each composed call advances the sub-slot on\n // the same close (or replaces its head on the same tick).\n const tenkanHi = highest(`${slotId}/tenkanHigh`, bar.high, conversionLength).current;\n const tenkanLo = lowest(`${slotId}/tenkanLow`, bar.low, conversionLength).current;\n const kijunHi = highest(`${slotId}/kijunHigh`, bar.high, baseLength).current;\n const kijunLo = lowest(`${slotId}/kijunLow`, bar.low, baseLength).current;\n const senkouBHi = highest(`${slotId}/senkouBHigh`, bar.high, leadingSpanBLength).current;\n const senkouBLo = lowest(`${slotId}/senkouBLow`, bar.low, leadingSpanBLength).current;\n\n const tenkan = midpoint(tenkanHi, tenkanLo);\n const kijun = midpoint(kijunHi, kijunLo);\n const senkouBRaw = midpoint(senkouBHi, senkouBLo);\n const senkouARaw = spanAverage(tenkan, kijun);\n\n if (ctx.isTick) {\n // Replace the head of the delay buffers with the tick's\n // updated raw values. `at(displacement)` then returns the\n // SAME value as before (the displacement-shifted slot is not\n // affected by the head's value), so the displaced output\n // heads are stable on tick.\n slot.senkouADelay.replaceHead(senkouARaw);\n slot.senkouBDelay.replaceHead(senkouBRaw);\n slot.closeDelay.replaceHead(bar.close);\n slot.tenkanBuffer.replaceHead(tenkan);\n slot.kijunBuffer.replaceHead(kijun);\n slot.senkouABuffer.replaceHead(delayedValue(slot.senkouADelay, displacement));\n slot.senkouBBuffer.replaceHead(delayedValue(slot.senkouBDelay, displacement));\n slot.chikouBuffer.replaceHead(delayedValue(slot.closeDelay, displacement));\n } else {\n slot.senkouADelay.append(senkouARaw);\n slot.senkouBDelay.append(senkouBRaw);\n slot.closeDelay.append(bar.close);\n slot.tenkanBuffer.append(tenkan);\n slot.kijunBuffer.append(kijun);\n slot.senkouABuffer.append(delayedValue(slot.senkouADelay, displacement));\n slot.senkouBBuffer.append(delayedValue(slot.senkouBDelay, displacement));\n slot.chikouBuffer.append(delayedValue(slot.closeDelay, displacement));\n }\n\n return resultForOffset(slot, offset);\n}\n"]}
@@ -0,0 +1,25 @@
1
+ import type { LowestbarsOpts, Series } from "@invinite-org/chartlang-core";
2
+ import { type ScalarOrSeries } from "./lib/sourceValue.js";
3
+ /**
4
+ * Bar offset (≤ 0) to the lowest `source` value over the trailing
5
+ * `length` bars (the window INCLUDES the current bar). `0` means the
6
+ * current bar is the lowest; `-k` means the lowest occurred `k` bars
7
+ * ago. Ties resolve to the MOST RECENT bar (smallest |offset|). NaN
8
+ * inputs are skipped as candidates; an all-NaN window emits NaN. The
9
+ * output is NaN until `length` closed bars have folded in. Tick-mode
10
+ * replays the in-progress head as the offset-0 candidate without
11
+ * advancing the buffer.
12
+ *
13
+ * @formula out[t] = argmin_{k ∈ [0, length)} source[t − k] expressed as −k
14
+ * (NaN slots skipped; ties → smallest k)
15
+ * @warmup length − 1
16
+ * @since 0.2
17
+ * @stable
18
+ *
19
+ * @example
20
+ * // import { ta } from "@invinite-org/chartlang-core";
21
+ * // const lbar = ta.lowestbars(bar.low, 20);
22
+ * // const left = bar.point(lbar.current, bar.low.current);
23
+ */
24
+ export declare function lowestbars(slotId: string, source: ScalarOrSeries, length: number, _opts?: LowestbarsOpts): Series<number>;
25
+ //# sourceMappingURL=lowestbars.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lowestbars.d.ts","sourceRoot":"","sources":["../../src/ta/lowestbars.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAK3E,OAAO,EAAE,KAAK,cAAc,EAAmB,MAAM,sBAAsB,CAAC;AAsE5E;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,UAAU,CACtB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,cAAc,GACvB,MAAM,CAAC,MAAM,CAAC,CAchB"}
@@ -0,0 +1,102 @@
1
+ // Copyright (c) 2026 Invinite. Licensed under the MIT License.
2
+ // See the LICENSE file in the repo root for full license text.
3
+ //
4
+ // No invinite source — semantics per Pine `ta.lowestbars`.
5
+ // Structural choices (callsite-id slot, Series<T> proxy, replaceHead
6
+ // mode) follow chartlang's primitive shape.
7
+ import { Float64RingBuffer } from "../ringBuffer.js";
8
+ import { ACTIVE_RUNTIME_CONTEXT } from "../runtimeContext.js";
9
+ import { makeSeriesView } from "../seriesView.js";
10
+ import { readSourceValue } from "./lib/sourceValue.js";
11
+ function getCtx() {
12
+ const ctx = ACTIVE_RUNTIME_CONTEXT.current;
13
+ if (ctx === null) {
14
+ throw new Error("ta.lowestbars called outside an active script step");
15
+ }
16
+ return ctx;
17
+ }
18
+ function initSlot(length, capacity) {
19
+ const outBuffer = new Float64RingBuffer(capacity);
20
+ return {
21
+ outBuffer,
22
+ series: makeSeriesView(outBuffer),
23
+ length,
24
+ sourceWindow: new Float64RingBuffer(length),
25
+ barCount: 0,
26
+ };
27
+ }
28
+ /**
29
+ * Bar offset (≤ 0) to the lowest value across the retained window.
30
+ * Walks the window most-recent-first (`at(0)` is the head bar) and
31
+ * updates the running extreme only on a STRICT improvement, so ties
32
+ * keep the most-recent bar (smallest |offset|). `headValue` overrides
33
+ * the head candidate (`at(0)`) for the tick-replay path; pass
34
+ * `undefined` to read the closed head from the window.
35
+ *
36
+ * Returns `NaN` when every candidate in the window is NaN.
37
+ */
38
+ function offsetToMin(slot, headValue) {
39
+ const filled = slot.sourceWindow.length;
40
+ let bestValue = Number.POSITIVE_INFINITY;
41
+ let bestOffset = Number.NaN;
42
+ for (let i = 0; i < filled; i += 1) {
43
+ const v = i === 0 && headValue !== undefined ? headValue : slot.sourceWindow.at(i);
44
+ if (Number.isFinite(v) && v < bestValue) {
45
+ bestValue = v;
46
+ // `i === 0` ⇒ offset 0 (NOT −0) so `Object.is`-based equality
47
+ // and JSON serialisation never see negative zero.
48
+ bestOffset = i === 0 ? 0 : -i;
49
+ }
50
+ }
51
+ return bestOffset;
52
+ }
53
+ function closeValue(slot, src) {
54
+ slot.barCount += 1;
55
+ slot.sourceWindow.append(src);
56
+ if (slot.barCount < slot.length)
57
+ return Number.NaN;
58
+ return offsetToMin(slot, undefined);
59
+ }
60
+ function tickValue(slot, src) {
61
+ if (slot.barCount < slot.length)
62
+ return Number.NaN;
63
+ return offsetToMin(slot, src);
64
+ }
65
+ /**
66
+ * Bar offset (≤ 0) to the lowest `source` value over the trailing
67
+ * `length` bars (the window INCLUDES the current bar). `0` means the
68
+ * current bar is the lowest; `-k` means the lowest occurred `k` bars
69
+ * ago. Ties resolve to the MOST RECENT bar (smallest |offset|). NaN
70
+ * inputs are skipped as candidates; an all-NaN window emits NaN. The
71
+ * output is NaN until `length` closed bars have folded in. Tick-mode
72
+ * replays the in-progress head as the offset-0 candidate without
73
+ * advancing the buffer.
74
+ *
75
+ * @formula out[t] = argmin_{k ∈ [0, length)} source[t − k] expressed as −k
76
+ * (NaN slots skipped; ties → smallest k)
77
+ * @warmup length − 1
78
+ * @since 0.2
79
+ * @stable
80
+ *
81
+ * @example
82
+ * // import { ta } from "@invinite-org/chartlang-core";
83
+ * // const lbar = ta.lowestbars(bar.low, 20);
84
+ * // const left = bar.point(lbar.current, bar.low.current);
85
+ */
86
+ export function lowestbars(slotId, source, length, _opts) {
87
+ const ctx = getCtx();
88
+ let slot = ctx.stream.taSlots.get(slotId);
89
+ if (slot === undefined) {
90
+ slot = initSlot(length, ctx.stream.ohlcv.close.capacity);
91
+ ctx.stream.taSlots.set(slotId, slot);
92
+ }
93
+ const src = readSourceValue(source);
94
+ if (ctx.isTick) {
95
+ slot.outBuffer.replaceHead(tickValue(slot, src));
96
+ }
97
+ else {
98
+ slot.outBuffer.append(closeValue(slot, src));
99
+ }
100
+ return slot.series;
101
+ }
102
+ //# sourceMappingURL=lowestbars.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lowestbars.js","sourceRoot":"","sources":["../../src/ta/lowestbars.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// No invinite source — semantics per Pine `ta.lowestbars`.\n// Structural choices (callsite-id slot, Series<T> proxy, replaceHead\n// mode) follow chartlang's primitive shape.\n\nimport type { LowestbarsOpts, Series } from \"@invinite-org/chartlang-core\";\n\nimport { Float64RingBuffer } from \"../ringBuffer.js\";\nimport { ACTIVE_RUNTIME_CONTEXT, type RuntimeContext } from \"../runtimeContext.js\";\nimport { makeSeriesView } from \"../seriesView.js\";\nimport { type ScalarOrSeries, readSourceValue } from \"./lib/sourceValue.js\";\n\ntype LowestbarsSlot = {\n readonly outBuffer: Float64RingBuffer;\n readonly series: Series<number>;\n readonly length: number;\n /** Closed source values across the trailing `length` bars. */\n readonly sourceWindow: Float64RingBuffer;\n barCount: number;\n};\n\nfunction getCtx(): RuntimeContext {\n const ctx = ACTIVE_RUNTIME_CONTEXT.current;\n if (ctx === null) {\n throw new Error(\"ta.lowestbars called outside an active script step\");\n }\n return ctx;\n}\n\nfunction initSlot(length: number, capacity: number): LowestbarsSlot {\n const outBuffer = new Float64RingBuffer(capacity);\n return {\n outBuffer,\n series: makeSeriesView<number>(outBuffer),\n length,\n sourceWindow: new Float64RingBuffer(length),\n barCount: 0,\n };\n}\n\n/**\n * Bar offset (≤ 0) to the lowest value across the retained window.\n * Walks the window most-recent-first (`at(0)` is the head bar) and\n * updates the running extreme only on a STRICT improvement, so ties\n * keep the most-recent bar (smallest |offset|). `headValue` overrides\n * the head candidate (`at(0)`) for the tick-replay path; pass\n * `undefined` to read the closed head from the window.\n *\n * Returns `NaN` when every candidate in the window is NaN.\n */\nfunction offsetToMin(slot: LowestbarsSlot, headValue: number | undefined): number {\n const filled = slot.sourceWindow.length;\n let bestValue = Number.POSITIVE_INFINITY;\n let bestOffset = Number.NaN;\n for (let i = 0; i < filled; i += 1) {\n const v = i === 0 && headValue !== undefined ? headValue : slot.sourceWindow.at(i);\n if (Number.isFinite(v) && v < bestValue) {\n bestValue = v;\n // `i === 0` ⇒ offset 0 (NOT −0) so `Object.is`-based equality\n // and JSON serialisation never see negative zero.\n bestOffset = i === 0 ? 0 : -i;\n }\n }\n return bestOffset;\n}\n\nfunction closeValue(slot: LowestbarsSlot, src: number): number {\n slot.barCount += 1;\n slot.sourceWindow.append(src);\n\n if (slot.barCount < slot.length) return Number.NaN;\n\n return offsetToMin(slot, undefined);\n}\n\nfunction tickValue(slot: LowestbarsSlot, src: number): number {\n if (slot.barCount < slot.length) return Number.NaN;\n return offsetToMin(slot, src);\n}\n\n/**\n * Bar offset (≤ 0) to the lowest `source` value over the trailing\n * `length` bars (the window INCLUDES the current bar). `0` means the\n * current bar is the lowest; `-k` means the lowest occurred `k` bars\n * ago. Ties resolve to the MOST RECENT bar (smallest |offset|). NaN\n * inputs are skipped as candidates; an all-NaN window emits NaN. The\n * output is NaN until `length` closed bars have folded in. Tick-mode\n * replays the in-progress head as the offset-0 candidate without\n * advancing the buffer.\n *\n * @formula out[t] = argmin_{k ∈ [0, length)} source[t − k] expressed as −k\n * (NaN slots skipped; ties → smallest k)\n * @warmup length − 1\n * @since 0.2\n * @stable\n *\n * @example\n * // import { ta } from \"@invinite-org/chartlang-core\";\n * // const lbar = ta.lowestbars(bar.low, 20);\n * // const left = bar.point(lbar.current, bar.low.current);\n */\nexport function lowestbars(\n slotId: string,\n source: ScalarOrSeries,\n length: number,\n _opts?: LowestbarsOpts,\n): Series<number> {\n const ctx = getCtx();\n let slot = ctx.stream.taSlots.get(slotId) as LowestbarsSlot | undefined;\n if (slot === undefined) {\n slot = initSlot(length, ctx.stream.ohlcv.close.capacity);\n ctx.stream.taSlots.set(slotId, slot);\n }\n const src = readSourceValue(source);\n if (ctx.isTick) {\n slot.outBuffer.replaceHead(tickValue(slot, src));\n } else {\n slot.outBuffer.append(closeValue(slot, src));\n }\n return slot.series;\n}\n"]}
package/dist/ta/macd.d.ts CHANGED
@@ -17,8 +17,9 @@ import { type ScalarOrSeries } from "./lib/sourceValue.js";
17
17
  * @since 0.1
18
18
  * @stable
19
19
  *
20
- * `opts.offset` shifts all three outputs in lockstep
21
- * `series.current` on each output returns the value `offset` bars ago.
20
+ * `opts.offset` is a presentation display shift carried to the plot
21
+ * emission as `xShift` for all three outputs in lockstep (`+n` right / future,
22
+ * `−n` left / past); the series values are unshifted.
22
23
  *
23
24
  * @example
24
25
  * // import { ta } from "@invinite-org/chartlang-runtime";
@@ -1 +1 @@
1
- {"version":3,"file":"macd.d.ts","sourceRoot":"","sources":["../../src/ta/macd.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAU,MAAM,8BAA8B,CAAC;AAMjF,OAAO,EAAE,KAAK,cAAc,EAAmB,MAAM,sBAAsB,CAAC;AAmE5E;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,IAAI,CAAC,EAAE,QAAQ,GAAG,UAAU,CAsCxF"}
1
+ {"version":3,"file":"macd.d.ts","sourceRoot":"","sources":["../../src/ta/macd.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAU,MAAM,8BAA8B,CAAC;AAMjF,OAAO,EAAE,KAAK,cAAc,EAAmB,MAAM,sBAAsB,CAAC;AAmE5E;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,IAAI,CAAC,EAAE,QAAQ,GAAG,UAAU,CAsCxF"}
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` shifts all three outputs in lockstep
76
- * `series.current` on each output returns the value `offset` bars ago.
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";
@@ -1 +1 @@
1
- {"version":3,"file":"macd.js","sourceRoot":"","sources":["../../src/ta/macd.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/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":"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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/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"]}
@@ -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` shifts the returned series so `series.current` reads
21
- * the value `offset` bars ago.
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;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,CAqB9E"}
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"}
@@ -115,8 +115,9 @@ function tickValue(slot, ratio) {
115
115
  * @since 0.2
116
116
  * @stable
117
117
  *
118
- * `opts.offset` shifts the returned series so `series.current` reads
119
- * the value `offset` bars ago.
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";
@@ -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` shifts the returned series.
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";
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,CA+ClF"}
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` shifts the returned series.
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";