@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.
Files changed (294) hide show
  1. package/CHANGELOG.md +307 -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/buildComputeContext.d.ts.map +1 -1
  11. package/dist/buildComputeContext.js +6 -1
  12. package/dist/buildComputeContext.js.map +1 -1
  13. package/dist/createScriptRunner.d.ts +6 -3
  14. package/dist/createScriptRunner.d.ts.map +1 -1
  15. package/dist/createScriptRunner.js +65 -11
  16. package/dist/createScriptRunner.js.map +1 -1
  17. package/dist/dep/DepRunner.d.ts +7 -0
  18. package/dist/dep/DepRunner.d.ts.map +1 -1
  19. package/dist/dep/DepRunner.js +4 -0
  20. package/dist/dep/DepRunner.js.map +1 -1
  21. package/dist/emit/barcolor.d.ts +44 -0
  22. package/dist/emit/barcolor.d.ts.map +1 -0
  23. package/dist/emit/barcolor.js +40 -0
  24. package/dist/emit/barcolor.js.map +1 -0
  25. package/dist/emit/bgcolor.d.ts +44 -0
  26. package/dist/emit/bgcolor.d.ts.map +1 -0
  27. package/dist/emit/bgcolor.js +45 -0
  28. package/dist/emit/bgcolor.js.map +1 -0
  29. package/dist/emit/draw/boxes/fillBetween.d.ts +45 -0
  30. package/dist/emit/draw/boxes/fillBetween.d.ts.map +1 -0
  31. package/dist/emit/draw/boxes/fillBetween.js +36 -0
  32. package/dist/emit/draw/boxes/fillBetween.js.map +1 -0
  33. package/dist/emit/draw/handle.d.ts +9 -0
  34. package/dist/emit/draw/handle.d.ts.map +1 -1
  35. package/dist/emit/draw/handle.js +65 -10
  36. package/dist/emit/draw/handle.js.map +1 -1
  37. package/dist/emit/draw/namespace.d.ts +4 -3
  38. package/dist/emit/draw/namespace.d.ts.map +1 -1
  39. package/dist/emit/draw/namespace.js +6 -3
  40. package/dist/emit/draw/namespace.js.map +1 -1
  41. package/dist/emit/index.d.ts +2 -0
  42. package/dist/emit/index.d.ts.map +1 -1
  43. package/dist/emit/index.js +2 -0
  44. package/dist/emit/index.js.map +1 -1
  45. package/dist/emit/plot.d.ts +30 -1
  46. package/dist/emit/plot.d.ts.map +1 -1
  47. package/dist/emit/plot.js +45 -1
  48. package/dist/emit/plot.js.map +1 -1
  49. package/dist/execution/dispose.d.ts.map +1 -1
  50. package/dist/execution/dispose.js +18 -0
  51. package/dist/execution/dispose.js.map +1 -1
  52. package/dist/execution/runComputeStep.d.ts.map +1 -1
  53. package/dist/execution/runComputeStep.js +12 -1
  54. package/dist/execution/runComputeStep.js.map +1 -1
  55. package/dist/index.d.ts +1 -1
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +1 -1
  58. package/dist/index.js.map +1 -1
  59. package/dist/inputs/resolveInputs.js +1 -0
  60. package/dist/inputs/resolveInputs.js.map +1 -1
  61. package/dist/persistentStateStore.runtime.d.ts.map +1 -1
  62. package/dist/persistentStateStore.runtime.js +26 -7
  63. package/dist/persistentStateStore.runtime.js.map +1 -1
  64. package/dist/primitives.d.ts +1 -1
  65. package/dist/primitives.d.ts.map +1 -1
  66. package/dist/primitives.js +7 -1
  67. package/dist/primitives.js.map +1 -1
  68. package/dist/request/index.d.ts +2 -1
  69. package/dist/request/index.d.ts.map +1 -1
  70. package/dist/request/index.js +2 -1
  71. package/dist/request/index.js.map +1 -1
  72. package/dist/request/lowerTf.d.ts.map +1 -1
  73. package/dist/request/lowerTf.js +6 -0
  74. package/dist/request/lowerTf.js.map +1 -1
  75. package/dist/request/requestNamespace.d.ts.map +1 -1
  76. package/dist/request/requestNamespace.js +30 -3
  77. package/dist/request/requestNamespace.js.map +1 -1
  78. package/dist/request/security.d.ts +40 -4
  79. package/dist/request/security.d.ts.map +1 -1
  80. package/dist/request/security.js +114 -40
  81. package/dist/request/security.js.map +1 -1
  82. package/dist/request/securityExprRunner.d.ts +137 -0
  83. package/dist/request/securityExprRunner.d.ts.map +1 -0
  84. package/dist/request/securityExprRunner.js +253 -0
  85. package/dist/request/securityExprRunner.js.map +1 -0
  86. package/dist/request/streamBars.d.ts +14 -1
  87. package/dist/request/streamBars.d.ts.map +1 -1
  88. package/dist/request/streamBars.js +39 -1
  89. package/dist/request/streamBars.js.map +1 -1
  90. package/dist/ringBuffer.d.ts +19 -0
  91. package/dist/ringBuffer.d.ts.map +1 -1
  92. package/dist/ringBuffer.js +23 -0
  93. package/dist/ringBuffer.js.map +1 -1
  94. package/dist/runtimeContext.d.ts +90 -5
  95. package/dist/runtimeContext.d.ts.map +1 -1
  96. package/dist/runtimeContext.js.map +1 -1
  97. package/dist/seriesView.d.ts +42 -17
  98. package/dist/seriesView.d.ts.map +1 -1
  99. package/dist/seriesView.js +65 -42
  100. package/dist/seriesView.js.map +1 -1
  101. package/dist/state/arrayPersistence.d.ts +48 -0
  102. package/dist/state/arrayPersistence.d.ts.map +1 -0
  103. package/dist/state/arrayPersistence.js +88 -0
  104. package/dist/state/arrayPersistence.js.map +1 -0
  105. package/dist/state/arrayStateSlot.d.ts +78 -0
  106. package/dist/state/arrayStateSlot.d.ts.map +1 -0
  107. package/dist/state/arrayStateSlot.js +116 -0
  108. package/dist/state/arrayStateSlot.js.map +1 -0
  109. package/dist/state/index.d.ts +4 -1
  110. package/dist/state/index.d.ts.map +1 -1
  111. package/dist/state/index.js +4 -1
  112. package/dist/state/index.js.map +1 -1
  113. package/dist/state/lifecycle.d.ts +68 -0
  114. package/dist/state/lifecycle.d.ts.map +1 -1
  115. package/dist/state/lifecycle.js +89 -0
  116. package/dist/state/lifecycle.js.map +1 -1
  117. package/dist/state/seriesPersistence.d.ts +48 -0
  118. package/dist/state/seriesPersistence.d.ts.map +1 -0
  119. package/dist/state/seriesPersistence.js +87 -0
  120. package/dist/state/seriesPersistence.js.map +1 -0
  121. package/dist/state/seriesSlot.d.ts +105 -0
  122. package/dist/state/seriesSlot.d.ts.map +1 -0
  123. package/dist/state/seriesSlot.js +123 -0
  124. package/dist/state/seriesSlot.js.map +1 -0
  125. package/dist/state/stateNamespace.d.ts.map +1 -1
  126. package/dist/state/stateNamespace.js +55 -0
  127. package/dist/state/stateNamespace.js.map +1 -1
  128. package/dist/streamState.d.ts +25 -19
  129. package/dist/streamState.d.ts.map +1 -1
  130. package/dist/streamState.js +40 -66
  131. package/dist/streamState.js.map +1 -1
  132. package/dist/ta/adx.d.ts +3 -2
  133. package/dist/ta/adx.d.ts.map +1 -1
  134. package/dist/ta/adx.js +3 -2
  135. package/dist/ta/adx.js.map +1 -1
  136. package/dist/ta/alma.d.ts +6 -4
  137. package/dist/ta/alma.d.ts.map +1 -1
  138. package/dist/ta/alma.js +19 -6
  139. package/dist/ta/alma.js.map +1 -1
  140. package/dist/ta/atr.d.ts +3 -2
  141. package/dist/ta/atr.d.ts.map +1 -1
  142. package/dist/ta/atr.js +3 -2
  143. package/dist/ta/atr.js.map +1 -1
  144. package/dist/ta/bb.d.ts +3 -2
  145. package/dist/ta/bb.d.ts.map +1 -1
  146. package/dist/ta/bb.js +3 -2
  147. package/dist/ta/bb.js.map +1 -1
  148. package/dist/ta/chaikinOsc.d.ts +3 -2
  149. package/dist/ta/chaikinOsc.d.ts.map +1 -1
  150. package/dist/ta/chaikinOsc.js +3 -2
  151. package/dist/ta/chaikinOsc.js.map +1 -1
  152. package/dist/ta/crossover.d.ts +3 -2
  153. package/dist/ta/crossover.d.ts.map +1 -1
  154. package/dist/ta/crossover.js +3 -2
  155. package/dist/ta/crossover.js.map +1 -1
  156. package/dist/ta/crossunder.d.ts +3 -2
  157. package/dist/ta/crossunder.d.ts.map +1 -1
  158. package/dist/ta/crossunder.js +3 -2
  159. package/dist/ta/crossunder.js.map +1 -1
  160. package/dist/ta/dmi.d.ts +4 -3
  161. package/dist/ta/dmi.d.ts.map +1 -1
  162. package/dist/ta/dmi.js +4 -3
  163. package/dist/ta/dmi.js.map +1 -1
  164. package/dist/ta/ema.d.ts +3 -2
  165. package/dist/ta/ema.d.ts.map +1 -1
  166. package/dist/ta/ema.js +3 -2
  167. package/dist/ta/ema.js.map +1 -1
  168. package/dist/ta/eom.d.ts +3 -1
  169. package/dist/ta/eom.d.ts.map +1 -1
  170. package/dist/ta/eom.js +3 -1
  171. package/dist/ta/eom.js.map +1 -1
  172. package/dist/ta/highestbars.d.ts +25 -0
  173. package/dist/ta/highestbars.d.ts.map +1 -0
  174. package/dist/ta/highestbars.js +106 -0
  175. package/dist/ta/highestbars.js.map +1 -0
  176. package/dist/ta/historicalVolatility.d.ts +3 -2
  177. package/dist/ta/historicalVolatility.d.ts.map +1 -1
  178. package/dist/ta/historicalVolatility.js +3 -2
  179. package/dist/ta/historicalVolatility.js.map +1 -1
  180. package/dist/ta/ichimoku.d.ts +3 -1
  181. package/dist/ta/ichimoku.d.ts.map +1 -1
  182. package/dist/ta/ichimoku.js +3 -1
  183. package/dist/ta/ichimoku.js.map +1 -1
  184. package/dist/ta/lowestbars.d.ts +25 -0
  185. package/dist/ta/lowestbars.d.ts.map +1 -0
  186. package/dist/ta/lowestbars.js +102 -0
  187. package/dist/ta/lowestbars.js.map +1 -0
  188. package/dist/ta/macd.d.ts +3 -2
  189. package/dist/ta/macd.d.ts.map +1 -1
  190. package/dist/ta/macd.js +3 -2
  191. package/dist/ta/macd.js.map +1 -1
  192. package/dist/ta/massIndex.d.ts +3 -2
  193. package/dist/ta/massIndex.d.ts.map +1 -1
  194. package/dist/ta/massIndex.js +3 -2
  195. package/dist/ta/massIndex.js.map +1 -1
  196. package/dist/ta/mfi.d.ts +3 -1
  197. package/dist/ta/mfi.d.ts.map +1 -1
  198. package/dist/ta/mfi.js +3 -1
  199. package/dist/ta/mfi.js.map +1 -1
  200. package/dist/ta/netVolume.d.ts +3 -1
  201. package/dist/ta/netVolume.d.ts.map +1 -1
  202. package/dist/ta/netVolume.js +3 -1
  203. package/dist/ta/netVolume.js.map +1 -1
  204. package/dist/ta/nvi.d.ts +3 -1
  205. package/dist/ta/nvi.d.ts.map +1 -1
  206. package/dist/ta/nvi.js +3 -1
  207. package/dist/ta/nvi.js.map +1 -1
  208. package/dist/ta/persistence.d.ts.map +1 -1
  209. package/dist/ta/persistence.js +1 -40
  210. package/dist/ta/persistence.js.map +1 -1
  211. package/dist/ta/ppo.d.ts +3 -2
  212. package/dist/ta/ppo.d.ts.map +1 -1
  213. package/dist/ta/ppo.js +3 -2
  214. package/dist/ta/ppo.js.map +1 -1
  215. package/dist/ta/pvi.d.ts +3 -1
  216. package/dist/ta/pvi.d.ts.map +1 -1
  217. package/dist/ta/pvi.js +3 -1
  218. package/dist/ta/pvi.js.map +1 -1
  219. package/dist/ta/pvo.d.ts +3 -1
  220. package/dist/ta/pvo.d.ts.map +1 -1
  221. package/dist/ta/pvo.js +3 -1
  222. package/dist/ta/pvo.js.map +1 -1
  223. package/dist/ta/pvt.d.ts +3 -1
  224. package/dist/ta/pvt.d.ts.map +1 -1
  225. package/dist/ta/pvt.js +3 -1
  226. package/dist/ta/pvt.js.map +1 -1
  227. package/dist/ta/registry.d.ts +7 -1
  228. package/dist/ta/registry.d.ts.map +1 -1
  229. package/dist/ta/registry.js +4 -0
  230. package/dist/ta/registry.js.map +1 -1
  231. package/dist/ta/rsi.d.ts +3 -2
  232. package/dist/ta/rsi.d.ts.map +1 -1
  233. package/dist/ta/rsi.js +3 -2
  234. package/dist/ta/rsi.js.map +1 -1
  235. package/dist/ta/rvi.d.ts +3 -2
  236. package/dist/ta/rvi.d.ts.map +1 -1
  237. package/dist/ta/rvi.js +3 -2
  238. package/dist/ta/rvi.js.map +1 -1
  239. package/dist/ta/sessionVolumeProfile.d.ts.map +1 -1
  240. package/dist/ta/sessionVolumeProfile.js +1 -17
  241. package/dist/ta/sessionVolumeProfile.js.map +1 -1
  242. package/dist/ta/sma.d.ts +6 -3
  243. package/dist/ta/sma.d.ts.map +1 -1
  244. package/dist/ta/sma.js +6 -3
  245. package/dist/ta/sma.js.map +1 -1
  246. package/dist/ta/stdev.d.ts +3 -2
  247. package/dist/ta/stdev.d.ts.map +1 -1
  248. package/dist/ta/stdev.js +3 -2
  249. package/dist/ta/stdev.js.map +1 -1
  250. package/dist/ta/trendStrengthIndex.d.ts +3 -2
  251. package/dist/ta/trendStrengthIndex.d.ts.map +1 -1
  252. package/dist/ta/trendStrengthIndex.js +3 -2
  253. package/dist/ta/trendStrengthIndex.js.map +1 -1
  254. package/dist/ta/trix.d.ts +4 -3
  255. package/dist/ta/trix.d.ts.map +1 -1
  256. package/dist/ta/trix.js +4 -3
  257. package/dist/ta/trix.js.map +1 -1
  258. package/dist/ta/vortex.d.ts +3 -2
  259. package/dist/ta/vortex.d.ts.map +1 -1
  260. package/dist/ta/vortex.js +3 -2
  261. package/dist/ta/vortex.js.map +1 -1
  262. package/dist/time-accessors/civil.d.ts +73 -0
  263. package/dist/time-accessors/civil.d.ts.map +1 -0
  264. package/dist/time-accessors/civil.js +105 -0
  265. package/dist/time-accessors/civil.js.map +1 -0
  266. package/dist/time-accessors/index.d.ts +8 -0
  267. package/dist/time-accessors/index.d.ts.map +1 -0
  268. package/dist/time-accessors/index.js +9 -0
  269. package/dist/time-accessors/index.js.map +1 -0
  270. package/dist/time-accessors/sessionAccessors.d.ts +50 -0
  271. package/dist/time-accessors/sessionAccessors.d.ts.map +1 -0
  272. package/dist/time-accessors/sessionAccessors.js +79 -0
  273. package/dist/time-accessors/sessionAccessors.js.map +1 -0
  274. package/dist/time-accessors/sessionWindow.d.ts +17 -0
  275. package/dist/time-accessors/sessionWindow.d.ts.map +1 -0
  276. package/dist/time-accessors/sessionWindow.js +41 -0
  277. package/dist/time-accessors/sessionWindow.js.map +1 -0
  278. package/dist/time-accessors/timeAccessors.d.ts +54 -0
  279. package/dist/time-accessors/timeAccessors.d.ts.map +1 -0
  280. package/dist/time-accessors/timeAccessors.js +132 -0
  281. package/dist/time-accessors/timeAccessors.js.map +1 -0
  282. package/dist/time-accessors/tzDiagnostic.d.ts +17 -0
  283. package/dist/time-accessors/tzDiagnostic.d.ts.map +1 -0
  284. package/dist/time-accessors/tzDiagnostic.js +34 -0
  285. package/dist/time-accessors/tzDiagnostic.js.map +1 -0
  286. package/dist/time-accessors/tzOffset.d.ts +31 -0
  287. package/dist/time-accessors/tzOffset.d.ts.map +1 -0
  288. package/dist/time-accessors/tzOffset.js +67 -0
  289. package/dist/time-accessors/tzOffset.js.map +1 -0
  290. package/package.json +3 -3
  291. package/dist/ta/lib/applyOffset.d.ts +0 -19
  292. package/dist/ta/lib/applyOffset.d.ts.map +0 -1
  293. package/dist/ta/lib/applyOffset.js +0 -38
  294. package/dist/ta/lib/applyOffset.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"sma.js","sourceRoot":"","sources":["../../src/ta/sma.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/sma.ts\n// plus lib/sma-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.\n\nimport type { Series, SmaOpts } 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\ntype SmaSlot = {\n readonly kind: \"ta.sma\";\n readonly outBuffer: Float64RingBuffer;\n readonly series: Series<number>;\n readonly length: number;\n /**\n * Window of the **closed** source values across the last `length` bars.\n * Holds bars `[t-length+1 .. t]` for the latest closed bar t. Ticks\n * compute a hypothetical \"head replaced\" mean from `windowSum -\n * window.at(0) + tickValue`; closes pop the oldest and push the new.\n */\n readonly window: Float64RingBuffer;\n sum: number;\n /**\n * Lazy cache of offset-shifted Series views keyed by `opts.offset`.\n * `offset === 0` callers bypass this map and return `series`\n * directly — identity-preserving. Populated on first call per\n * non-zero offset; identity is stable per offset thereafter.\n */\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.sma called outside an active script step\");\n }\n return ctx;\n}\n\nfunction initSlot(length: number, capacity: number): SmaSlot {\n const outBuffer = new Float64RingBuffer(capacity);\n return {\n kind: \"ta.sma\",\n outBuffer,\n series: makeSeriesView<number>(outBuffer),\n length,\n window: new Float64RingBuffer(length),\n sum: 0,\n shiftedViews: new Map(),\n };\n}\n\nfunction viewForOffset(slot: SmaSlot, 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 tickValue(slot: SmaSlot, src: number): number {\n // `window` currently holds the close-side values `[t-length+1 .. t]` for\n // the most-recent closed bar. The tick replaces bar `t`'s value with\n // `src`, so the hypothetical mean is `(sum - window.at(0)) + src`.\n if (!Number.isFinite(src)) return Number.NaN;\n if (slot.window.length < slot.length) return Number.NaN;\n const oldestInHead = slot.window.at(0);\n return (slot.sum - oldestInHead + src) / slot.length;\n}\n\nfunction closeValue(slot: SmaSlot, src: number): number {\n if (!Number.isFinite(src)) {\n // Skip the window update; emit the prior closed mean (or NaN when\n // unwarmed). The window's contents are unchanged so future closes\n // continue against the last valid set.\n if (slot.window.length < slot.length) return Number.NaN;\n return slot.sum / slot.length;\n }\n if (slot.window.length < slot.length) {\n slot.window.append(src);\n slot.sum += src;\n if (slot.window.length < slot.length) return Number.NaN;\n return slot.sum / slot.length;\n }\n const outgoing = slot.window.at(slot.length - 1);\n slot.window.append(src);\n slot.sum = slot.sum + src - outgoing;\n return slot.sum / slot.length;\n}\n\n/**\n * Simple moving average — rolling mean of the last `length` source\n * values. Warmup of `length − 1` bars returns `NaN`. Tick-mode replays\n * the head as `(window_sum − window_head + tick_value) / length` so a\n * partial-bar tick doesn't pollute the next close's running sum.\n * `opts.offset` shifts the returned series so `series.current` reads\n * the value `offset` bars ago.\n *\n * @formula out[t] = (source[t] + source[t − 1] + … + source[t − length + 1]) / length\n * @warmup length − 1\n * @since 0.1\n * @stable\n *\n * @example\n * // import { ta } from \"@invinite-org/chartlang-runtime\";\n * // const s = ta.sma(\"slot\", bar.close, 20);\n * // const head = s.current; // NaN until bar length-1\n * // const lagged = ta.sma(\"slot2\", bar.close, 20, { offset: 5 });\n */\nexport function sma(\n slotId: string,\n source: ScalarOrSeries,\n length: number,\n opts?: SmaOpts,\n): Series<number> {\n const ctx = getCtx();\n let slot = ctx.stream.taSlots.get(slotId) as SmaSlot | 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 viewForOffset(slot, opts?.offset ?? 0);\n}\n"]}
1
+ {"version":3,"file":"sma.js","sourceRoot":"","sources":["../../src/ta/sma.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/sma.ts\n// plus lib/sma-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.\n\nimport type { Series, SmaOpts } 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\ntype SmaSlot = {\n readonly kind: \"ta.sma\";\n readonly outBuffer: Float64RingBuffer;\n readonly series: Series<number>;\n readonly length: number;\n /**\n * Window of the **closed** source values across the last `length` bars.\n * Holds bars `[t-length+1 .. t]` for the latest closed bar t. Ticks\n * compute a hypothetical \"head replaced\" mean from `windowSum -\n * window.at(0) + tickValue`; closes pop the oldest and push the new.\n */\n readonly window: Float64RingBuffer;\n sum: number;\n /**\n * Lazy cache of offset-shifted Series views keyed by `opts.offset`.\n * `offset === 0` callers bypass this map and return `series`\n * directly — identity-preserving. Populated on first call per\n * non-zero offset; identity is stable per offset thereafter.\n */\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.sma called outside an active script step\");\n }\n return ctx;\n}\n\nfunction initSlot(length: number, capacity: number): SmaSlot {\n const outBuffer = new Float64RingBuffer(capacity);\n return {\n kind: \"ta.sma\",\n outBuffer,\n series: makeSeriesView<number>(outBuffer),\n length,\n window: new Float64RingBuffer(length),\n sum: 0,\n shiftedViews: new Map(),\n };\n}\n\nfunction viewForOffset(slot: SmaSlot, 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 tickValue(slot: SmaSlot, src: number): number {\n // `window` currently holds the close-side values `[t-length+1 .. t]` for\n // the most-recent closed bar. The tick replaces bar `t`'s value with\n // `src`, so the hypothetical mean is `(sum - window.at(0)) + src`.\n if (!Number.isFinite(src)) return Number.NaN;\n if (slot.window.length < slot.length) return Number.NaN;\n const oldestInHead = slot.window.at(0);\n return (slot.sum - oldestInHead + src) / slot.length;\n}\n\nfunction closeValue(slot: SmaSlot, src: number): number {\n if (!Number.isFinite(src)) {\n // Skip the window update; emit the prior closed mean (or NaN when\n // unwarmed). The window's contents are unchanged so future closes\n // continue against the last valid set.\n if (slot.window.length < slot.length) return Number.NaN;\n return slot.sum / slot.length;\n }\n if (slot.window.length < slot.length) {\n slot.window.append(src);\n slot.sum += src;\n if (slot.window.length < slot.length) return Number.NaN;\n return slot.sum / slot.length;\n }\n const outgoing = slot.window.at(slot.length - 1);\n slot.window.append(src);\n slot.sum = slot.sum + src - outgoing;\n return slot.sum / slot.length;\n}\n\n/**\n * Simple moving average — rolling mean of the last `length` source\n * values. Warmup of `length − 1` bars returns `NaN`. Tick-mode replays\n * the head as `(window_sum − window_head + tick_value) / length` so a\n * partial-bar tick doesn't pollute the next close's running sum.\n * `opts.offset` is a presentation display shift carried to the plot\n * emission as `xShift` (`+n` renders the series `n` bars right / future,\n * `−n` `n` bars left / past); it does NOT transform the value —\n * `series.current` is unshifted, so alerts and `state.*` see the value\n * computed at the current bar.\n *\n * @formula out[t] = (source[t] + source[t − 1] + … + source[t − length + 1]) / length\n * @warmup length − 1\n * @since 0.1\n * @stable\n *\n * @example\n * // import { ta } from \"@invinite-org/chartlang-runtime\";\n * // const s = ta.sma(\"slot\", bar.close, 20);\n * // const head = s.current; // NaN until bar length-1\n * // const shifted = ta.sma(\"slot2\", bar.close, 20, { offset: 5 }); // renders 5 bars right\n */\nexport function sma(\n slotId: string,\n source: ScalarOrSeries,\n length: number,\n opts?: SmaOpts,\n): Series<number> {\n const ctx = getCtx();\n let slot = ctx.stream.taSlots.get(slotId) as SmaSlot | 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 viewForOffset(slot, opts?.offset ?? 0);\n}\n"]}
@@ -12,8 +12,9 @@ import { type ScalarOrSeries } from "./lib/sourceValue.js";
12
12
  * @since 0.1
13
13
  * @stable
14
14
  *
15
- * `opts.offset` shifts the returned series so `series.current` reads
16
- * the value `offset` bars ago.
15
+ * `opts.offset` is a presentation display shift carried to the plot
16
+ * emission as `xShift` (`+n` right / future, `−n` left / past); the
17
+ * series value is unshifted.
17
18
  *
18
19
  * @example
19
20
  * // import { ta } from "@invinite-org/chartlang-runtime";
@@ -1 +1 @@
1
- {"version":3,"file":"stdev.d.ts","sourceRoot":"","sources":["../../src/ta/stdev.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAKtE,OAAO,EAAE,KAAK,cAAc,EAAmB,MAAM,sBAAsB,CAAC;AAuF5E;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,KAAK,CACjB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,SAAS,GACjB,MAAM,CAAC,MAAM,CAAC,CAehB"}
1
+ {"version":3,"file":"stdev.d.ts","sourceRoot":"","sources":["../../src/ta/stdev.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAKtE,OAAO,EAAE,KAAK,cAAc,EAAmB,MAAM,sBAAsB,CAAC;AAuF5E;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,KAAK,CACjB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,SAAS,GACjB,MAAM,CAAC,MAAM,CAAC,CAehB"}
package/dist/ta/stdev.js CHANGED
@@ -96,8 +96,9 @@ function tickValue(slot, src) {
96
96
  * @since 0.1
97
97
  * @stable
98
98
  *
99
- * `opts.offset` shifts the returned series so `series.current` reads
100
- * the value `offset` bars ago.
99
+ * `opts.offset` is a presentation display shift carried to the plot
100
+ * emission as `xShift` (`+n` right / future, `−n` left / past); the
101
+ * series value is unshifted.
101
102
  *
102
103
  * @example
103
104
  * // import { ta } from "@invinite-org/chartlang-runtime";
@@ -1 +1 @@
1
- {"version":3,"file":"stdev.js","sourceRoot":"","sources":["../../src/ta/stdev.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/lib/rolling-stddev.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.\n\nimport type { Series, StdevOpts } 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\ntype StdevSlot = {\n readonly outBuffer: Float64RingBuffer;\n readonly series: Series<number>;\n readonly length: number;\n readonly biased: boolean;\n readonly window: Float64RingBuffer;\n sumX: number;\n sumX2: 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.stdev called outside an active script step\");\n }\n return ctx;\n}\n\nfunction initSlot(length: number, capacity: number, biased: boolean): StdevSlot {\n const outBuffer = new Float64RingBuffer(capacity);\n return {\n outBuffer,\n series: makeSeriesView<number>(outBuffer),\n length,\n biased,\n window: new Float64RingBuffer(length),\n sumX: 0,\n sumX2: 0,\n shiftedViews: new Map(),\n };\n}\n\nfunction viewForOffset(slot: StdevSlot, 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 denominator(slot: StdevSlot): number {\n return slot.biased ? slot.length : slot.length - 1;\n}\n\nfunction stddevFromSums(sumX: number, sumX2: number, slot: StdevSlot): number {\n const denom = denominator(slot);\n if (denom <= 0) return Number.NaN;\n const mean = sumX / slot.length;\n const variance = (sumX2 - slot.length * mean * mean) / denom;\n // Numerical: clamp tiny negatives from accumulated rounding error.\n return Math.sqrt(Math.max(0, variance));\n}\n\nfunction closeValue(slot: StdevSlot, src: number): number {\n if (!Number.isFinite(src)) {\n if (slot.window.length < slot.length) return Number.NaN;\n return stddevFromSums(slot.sumX, slot.sumX2, slot);\n }\n if (slot.window.length < slot.length) {\n slot.window.append(src);\n slot.sumX += src;\n slot.sumX2 += src * src;\n if (slot.window.length < slot.length) return Number.NaN;\n return stddevFromSums(slot.sumX, slot.sumX2, slot);\n }\n const outgoing = slot.window.at(slot.length - 1);\n slot.window.append(src);\n slot.sumX = slot.sumX + src - outgoing;\n slot.sumX2 = slot.sumX2 + src * src - outgoing * outgoing;\n return stddevFromSums(slot.sumX, slot.sumX2, slot);\n}\n\nfunction tickValue(slot: StdevSlot, src: number): number {\n if (!Number.isFinite(src)) return Number.NaN;\n if (slot.window.length < slot.length) return Number.NaN;\n const oldestInHead = slot.window.at(0);\n const sumX = slot.sumX - oldestInHead + src;\n const sumX2 = slot.sumX2 - oldestInHead * oldestInHead + src * src;\n return stddevFromSums(sumX, sumX2, slot);\n}\n\n/**\n * Rolling sample / population standard deviation over the last\n * `length` source values. Defaults to **biased = false** (sample\n * stddev, denominator `length − 1`) to match core's `StdevOpts`.\n * `biased = true` switches to population (denominator `length`).\n *\n * @formula μ = mean(window) ;\n * σ = sqrt(Σ(x − μ)² / N), N = length (biased) or length − 1 (sample)\n * @warmup length − 1\n * @since 0.1\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-runtime\";\n * // const s = ta.stdev(\"slot\", bar.close, 20, { biased: false });\n * // const head = s.current;\n * // const lagged = ta.stdev(\"slot2\", bar.close, 20, { offset: 5 });\n */\nexport function stdev(\n slotId: string,\n source: ScalarOrSeries,\n length: number,\n opts?: StdevOpts,\n): Series<number> {\n const ctx = getCtx();\n let slot = ctx.stream.taSlots.get(slotId) as StdevSlot | undefined;\n if (slot === undefined) {\n const biased = opts?.biased === true;\n slot = initSlot(length, ctx.stream.ohlcv.close.capacity, biased);\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":"stdev.js","sourceRoot":"","sources":["../../src/ta/stdev.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/lib/rolling-stddev.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.\n\nimport type { Series, StdevOpts } 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\ntype StdevSlot = {\n readonly outBuffer: Float64RingBuffer;\n readonly series: Series<number>;\n readonly length: number;\n readonly biased: boolean;\n readonly window: Float64RingBuffer;\n sumX: number;\n sumX2: 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.stdev called outside an active script step\");\n }\n return ctx;\n}\n\nfunction initSlot(length: number, capacity: number, biased: boolean): StdevSlot {\n const outBuffer = new Float64RingBuffer(capacity);\n return {\n outBuffer,\n series: makeSeriesView<number>(outBuffer),\n length,\n biased,\n window: new Float64RingBuffer(length),\n sumX: 0,\n sumX2: 0,\n shiftedViews: new Map(),\n };\n}\n\nfunction viewForOffset(slot: StdevSlot, 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 denominator(slot: StdevSlot): number {\n return slot.biased ? slot.length : slot.length - 1;\n}\n\nfunction stddevFromSums(sumX: number, sumX2: number, slot: StdevSlot): number {\n const denom = denominator(slot);\n if (denom <= 0) return Number.NaN;\n const mean = sumX / slot.length;\n const variance = (sumX2 - slot.length * mean * mean) / denom;\n // Numerical: clamp tiny negatives from accumulated rounding error.\n return Math.sqrt(Math.max(0, variance));\n}\n\nfunction closeValue(slot: StdevSlot, src: number): number {\n if (!Number.isFinite(src)) {\n if (slot.window.length < slot.length) return Number.NaN;\n return stddevFromSums(slot.sumX, slot.sumX2, slot);\n }\n if (slot.window.length < slot.length) {\n slot.window.append(src);\n slot.sumX += src;\n slot.sumX2 += src * src;\n if (slot.window.length < slot.length) return Number.NaN;\n return stddevFromSums(slot.sumX, slot.sumX2, slot);\n }\n const outgoing = slot.window.at(slot.length - 1);\n slot.window.append(src);\n slot.sumX = slot.sumX + src - outgoing;\n slot.sumX2 = slot.sumX2 + src * src - outgoing * outgoing;\n return stddevFromSums(slot.sumX, slot.sumX2, slot);\n}\n\nfunction tickValue(slot: StdevSlot, src: number): number {\n if (!Number.isFinite(src)) return Number.NaN;\n if (slot.window.length < slot.length) return Number.NaN;\n const oldestInHead = slot.window.at(0);\n const sumX = slot.sumX - oldestInHead + src;\n const sumX2 = slot.sumX2 - oldestInHead * oldestInHead + src * src;\n return stddevFromSums(sumX, sumX2, slot);\n}\n\n/**\n * Rolling sample / population standard deviation over the last\n * `length` source values. Defaults to **biased = false** (sample\n * stddev, denominator `length − 1`) to match core's `StdevOpts`.\n * `biased = true` switches to population (denominator `length`).\n *\n * @formula μ = mean(window) ;\n * σ = sqrt(Σ(x − μ)² / N), N = length (biased) or length − 1 (sample)\n * @warmup length − 1\n * @since 0.1\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-runtime\";\n * // const s = ta.stdev(\"slot\", bar.close, 20, { biased: false });\n * // const head = s.current;\n * // const lagged = ta.stdev(\"slot2\", bar.close, 20, { offset: 5 });\n */\nexport function stdev(\n slotId: string,\n source: ScalarOrSeries,\n length: number,\n opts?: StdevOpts,\n): Series<number> {\n const ctx = getCtx();\n let slot = ctx.stream.taSlots.get(slotId) as StdevSlot | undefined;\n if (slot === undefined) {\n const biased = opts?.biased === true;\n slot = initSlot(length, ctx.stream.ohlcv.close.capacity, biased);\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"]}
@@ -23,8 +23,9 @@ import { type ScalarOrSeries } from "./lib/sourceValue.js";
23
23
  * @since 0.2
24
24
  * @stable
25
25
  *
26
- * `opts.offset` shifts the returned series so `series.current` reads
27
- * the value `offset` bars ago.
26
+ * `opts.offset` is a presentation display shift carried to the plot
27
+ * emission as `xShift` (`+n` right / future, `−n` left / past); the
28
+ * series value is unshifted.
28
29
  *
29
30
  * @example
30
31
  * // import { ta } from "@invinite-org/chartlang-runtime";
@@ -1 +1 @@
1
- {"version":3,"file":"trendStrengthIndex.d.ts","sourceRoot":"","sources":["../../src/ta/trendStrengthIndex.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,MAAM,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAKnF,OAAO,EAAE,KAAK,cAAc,EAAmB,MAAM,sBAAsB,CAAC;AAyI5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,kBAAkB,CAC9B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,sBAAsB,GAC9B,MAAM,CAAC,MAAM,CAAC,CAchB"}
1
+ {"version":3,"file":"trendStrengthIndex.d.ts","sourceRoot":"","sources":["../../src/ta/trendStrengthIndex.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,MAAM,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAKnF,OAAO,EAAE,KAAK,cAAc,EAAmB,MAAM,sBAAsB,CAAC;AAyI5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,kBAAkB,CAC9B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,sBAAsB,GAC9B,MAAM,CAAC,MAAM,CAAC,CAchB"}
@@ -156,8 +156,9 @@ function tickStep(slot, src) {
156
156
  * @since 0.2
157
157
  * @stable
158
158
  *
159
- * `opts.offset` shifts the returned series so `series.current` reads
160
- * the value `offset` bars ago.
159
+ * `opts.offset` is a presentation display shift carried to the plot
160
+ * emission as `xShift` (`+n` right / future, `−n` left / past); the
161
+ * series value is unshifted.
161
162
  *
162
163
  * @example
163
164
  * // import { ta } from "@invinite-org/chartlang-runtime";
@@ -1 +1 @@
1
- {"version":3,"file":"trendStrengthIndex.js","sourceRoot":"","sources":["../../src/ta/trendStrengthIndex.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAC/D,EAAE;AACF,uFAAuF;AACvF,mEAAmE;AACnE,qEAAqE;AACrE,gBAAgB;AAChB,qEAAqE;AACrE,4DAA4D;AAC5D,oEAAoE;AACpE,qEAAqE;AACrE,qEAAqE;AACrE,mEAAmE;AAInE,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,EAAuB,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAwB5E,SAAS,MAAM;IACX,MAAM,GAAG,GAAG,sBAAsB,CAAC,OAAO,CAAC;IAC3C,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAC;IAClF,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAED,SAAS,QAAQ,CAAC,MAAc,EAAE,QAAgB;IAC9C,MAAM,SAAS,GAAG,IAAI,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAClD,OAAO;QACH,SAAS;QACT,MAAM,EAAE,cAAc,CAAS,SAAS,CAAC;QACzC,MAAM;QACN,YAAY,EAAE,IAAI,iBAAiB,CAAC,MAAM,CAAC;QAC3C,QAAQ,EAAE,CAAC;QACX,QAAQ,EAAE,CAAC;QACX,aAAa,EAAE,MAAM,CAAC,GAAG;QACzB,kBAAkB,EAAE,CAAC;QACrB,YAAY,EAAE,IAAI,GAAG,EAAE;KAC1B,CAAC;AACN,CAAC;AAED,SAAS,aAAa,CAAC,IAA4B,EAAE,MAAc;IAC/D,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;;;;;;;;GAQG;AACH,SAAS,WAAW,CAAC,MAAyB,EAAE,MAAc,EAAE,UAAkB;IAC9E,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACjC,MAAM,CAAC,GAAG,CAAC,KAAK,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QACpE,8DAA8D;QAC9D,4DAA4D;QAC5D,oBAAoB;QACpB,oBAAoB;QACpB,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;YAAE,OAAO,MAAM,CAAC,GAAG,CAAC;QAC3C,IAAI,IAAI,CAAC,CAAC;QACV,IAAI,IAAI,CAAC,CAAC;IACd,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,GAAG,MAAM,CAAC;IAC5B,MAAM,KAAK,GAAG,IAAI,GAAG,MAAM,CAAC;IAC5B,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACjC,MAAM,CAAC,GAAG,CAAC,KAAK,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QACpE,MAAM,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC;QACrB,MAAM,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC;QACrB,KAAK,IAAI,EAAE,GAAG,EAAE,CAAC;QACjB,KAAK,IAAI,EAAE,GAAG,EAAE,CAAC;QACjB,KAAK,IAAI,EAAE,GAAG,EAAE,CAAC;IACrB,CAAC;IACD,IAAI,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC,GAAG,CAAC;IAClD,MAAM,CAAC,GAAG,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC;IAC3C,IAAI,CAAC,GAAG,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC,CAAC;IACtB,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,CAAC,CAAC;IACpB,OAAO,CAAC,CAAC;AACb,CAAC;AAED,SAAS,SAAS,CAAC,IAA4B,EAAE,GAAW;IACxD,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,QAAQ,CAAC;IAExC,6DAA6D;IAC7D,iEAAiE;IACjE,4BAA4B;IAC5B,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAC1C,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC3D,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC;YAAE,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;IAC7D,CAAC;SAAM,CAAC;QACJ,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC,GAAG,CAAC;IACpC,CAAC;IACD,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;IAE9C,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;IAEnB,IAAI,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,MAAM;QAAE,OAAO,MAAM,CAAC,GAAG,CAAC;IACnD,IAAI,IAAI,CAAC,QAAQ,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC,GAAG,CAAC;IACzC,OAAO,WAAW,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAC5D,CAAC;AAED,SAAS,QAAQ,CAAC,IAA4B,EAAE,GAAW;IACvD,IAAI,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,MAAM;QAAE,OAAO,MAAM,CAAC,GAAG,CAAC;IACnD,0DAA0D;IAC1D,6DAA6D;IAC7D,qDAAqD;IACrD,IAAI,QAAQ,GAAG,IAAI,CAAC,kBAAkB,CAAC;IACvC,qEAAqE;IACrE,iEAAiE;IACjE,uCAAuC;IACvC,oBAAoB;IACpB,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC;QAAE,QAAQ,IAAI,CAAC,CAAC;IACpD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,QAAQ,IAAI,CAAC,CAAC;IACzC,IAAI,QAAQ,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC,GAAG,CAAC;IACpC,OAAO,WAAW,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAC5D,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,UAAU,kBAAkB,CAC9B,MAAc,EACd,MAAsB,EACtB,MAAc,EACd,IAA6B;IAE7B,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;IACrB,IAAI,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAuC,CAAC;IAChF,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACrB,IAAI,GAAG,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACzD,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACzC,CAAC;IACD,MAAM,GAAG,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IACpC,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;QACb,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;IACpD,CAAC;SAAM,CAAC;QACJ,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC,CAAC,CAAC;AAClD,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/trend-strength-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. Named `trendStrengthIndex` to disambiguate\n// from the existing `ta.tsi` (Task 14 momentum True Strength Index).\n// The math is TradingView's documented Pearson-of-price-vs-bar-index\n// formulation — `lib/pearson.ts` (Task 4) is the reference helper.\n\nimport type { Series, TrendStrengthIndexOpts } 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\ntype TrendStrengthIndexSlot = {\n readonly outBuffer: Float64RingBuffer;\n readonly series: Series<number>;\n readonly length: number;\n /** Rolling window of recent source values, most-recent first\n * (`at(0)` = head, `at(length - 1)` = oldest). */\n readonly sourceWindow: Float64RingBuffer;\n /** Number of CLOSED bars folded into the slot so far. */\n barCount: number;\n /** Number of NaN values currently in the window — when > 0 the\n * Pearson denominator is undefined and the primitive emits NaN. */\n nanCount: number;\n /** The source value evicted on the current close (the tail value\n * popped when the window was full at append time). Used by tick\n * replay to restore the window to its pre-close state. */\n evictedSource: number;\n /** `nanCount` AS OF THE PRIOR CLOSE — restored by tick replay\n * before substituting the tick's contribution. */\n prevClosedNanCount: number;\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.trendStrengthIndex called outside an active script step\");\n }\n return ctx;\n}\n\nfunction initSlot(length: number, capacity: number): TrendStrengthIndexSlot {\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 nanCount: 0,\n evictedSource: Number.NaN,\n prevClosedNanCount: 0,\n shiftedViews: new Map(),\n };\n}\n\nfunction viewForOffset(slot: TrendStrengthIndexSlot, 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 * Compute Pearson correlation between the trailing source window and a\n * matching length-`n` linear bar-index series `[0, 1, ..., n-1]`.\n * Mirrors `lib/pearson.ts`'s two-pass mean/covariance formulation\n * exactly (sum then mean, then per-slot deviations) so the property\n * test's full-recompute reference and the incremental head agree\n * within Float64 noise. Returns NaN if any slot is non-finite or the\n * source-side window variance is exactly zero.\n */\nfunction pearsonHead(window: Float64RingBuffer, length: number, headSource: number): number {\n let sumX = 0;\n let sumY = 0;\n for (let k = 0; k < length; k += 1) {\n const x = k === length - 1 ? headSource : window.at(length - 1 - k);\n // Defensive: callers gate on `nanCount === 0` so every window\n // slot is finite when we get here; the `headSource` is also\n // guarded upstream.\n /* c8 ignore next */\n if (!Number.isFinite(x)) return Number.NaN;\n sumX += x;\n sumY += k;\n }\n const meanX = sumX / length;\n const meanY = sumY / length;\n let sumXY = 0;\n let sumXX = 0;\n let sumYY = 0;\n for (let k = 0; k < length; k += 1) {\n const x = k === length - 1 ? headSource : window.at(length - 1 - k);\n const dX = x - meanX;\n const dY = k - meanY;\n sumXY += dX * dY;\n sumXX += dX * dX;\n sumYY += dY * dY;\n }\n if (sumXX === 0 || sumYY === 0) return Number.NaN;\n const r = sumXY / Math.sqrt(sumXX * sumYY);\n if (r < -1) return -1;\n if (r > 1) return 1;\n return r;\n}\n\nfunction closeStep(slot: TrendStrengthIndexSlot, src: number): number {\n slot.prevClosedNanCount = slot.nanCount;\n\n // Update the rolling window. If the window is full, the tail\n // (oldest) value is about to be evicted by `append` — capture it\n // and adjust the NaN count.\n if (slot.sourceWindow.length >= slot.length) {\n slot.evictedSource = slot.sourceWindow.at(slot.length - 1);\n if (Number.isNaN(slot.evictedSource)) slot.nanCount -= 1;\n } else {\n slot.evictedSource = Number.NaN;\n }\n slot.sourceWindow.append(src);\n if (!Number.isFinite(src)) slot.nanCount += 1;\n\n slot.barCount += 1;\n\n if (slot.barCount < slot.length) return Number.NaN;\n if (slot.nanCount > 0) return Number.NaN;\n return pearsonHead(slot.sourceWindow, slot.length, src);\n}\n\nfunction tickStep(slot: TrendStrengthIndexSlot, src: number): number {\n if (slot.barCount < slot.length) return Number.NaN;\n // Re-derive NaN count: start from the prior closed count,\n // un-account for the value evicted by the current close, and\n // account for the tick's substituted value at age 0.\n let nanCount = slot.prevClosedNanCount;\n // Defensive: `evictedSource` is only NaN when the close-side advance\n // evicted a NaN, requiring an earlier NaN bar in the stream that\n // we then tick on. Reachable but rare.\n /* c8 ignore next */\n if (Number.isNaN(slot.evictedSource)) nanCount -= 1;\n if (!Number.isFinite(src)) nanCount += 1;\n if (nanCount > 0) return Number.NaN;\n return pearsonHead(slot.sourceWindow, slot.length, src);\n}\n\n/**\n * Trend Strength Index — Pearson correlation between `source` and the\n * bar index over each trailing `length`-bar window. Bounded `[-1, +1]`:\n * `+1` = clean uptrend (price rises monotonically with bar index), `−1`\n * = clean downtrend, `0` = no linear trend. Distinct from\n * `ta.tsi` (the True Strength Index — a momentum oscillator).\n * The math is TradingView's documented Trend Strength Index\n * (https://www.tradingview.com/support/solutions/43000730926-trend-strength-index/).\n *\n * Default `length = 20` per chartlang task spec (invinite plugin\n * default is `14`).\n *\n * @formula meanX = Σx / n ; meanY = Σy / n ; n = length ;\n * num = Σ((x − meanX)(y − meanY)) ;\n * den = sqrt(Σ(x − meanX)² · Σ(y − meanY)²) ;\n * tsi = clamp(num / den, −1, +1) ;\n * NaN if the source window has zero variance, the index\n * variance is zero (`length < 2`), or any window slot is\n * non-finite.\n * @warmup length − 1\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-runtime\";\n * // const t = ta.trendStrengthIndex(\"slot\", bar.close, 20);\n * // plot(t);\n */\nexport function trendStrengthIndex(\n slotId: string,\n source: ScalarOrSeries,\n length: number,\n opts?: TrendStrengthIndexOpts,\n): Series<number> {\n const ctx = getCtx();\n let slot = ctx.stream.taSlots.get(slotId) as TrendStrengthIndexSlot | 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(tickStep(slot, src));\n } else {\n slot.outBuffer.append(closeStep(slot, src));\n }\n return viewForOffset(slot, opts?.offset ?? 0);\n}\n"]}
1
+ {"version":3,"file":"trendStrengthIndex.js","sourceRoot":"","sources":["../../src/ta/trendStrengthIndex.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAC/D,EAAE;AACF,uFAAuF;AACvF,mEAAmE;AACnE,qEAAqE;AACrE,gBAAgB;AAChB,qEAAqE;AACrE,4DAA4D;AAC5D,oEAAoE;AACpE,qEAAqE;AACrE,qEAAqE;AACrE,mEAAmE;AAInE,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,EAAuB,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAwB5E,SAAS,MAAM;IACX,MAAM,GAAG,GAAG,sBAAsB,CAAC,OAAO,CAAC;IAC3C,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAC;IAClF,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAED,SAAS,QAAQ,CAAC,MAAc,EAAE,QAAgB;IAC9C,MAAM,SAAS,GAAG,IAAI,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAClD,OAAO;QACH,SAAS;QACT,MAAM,EAAE,cAAc,CAAS,SAAS,CAAC;QACzC,MAAM;QACN,YAAY,EAAE,IAAI,iBAAiB,CAAC,MAAM,CAAC;QAC3C,QAAQ,EAAE,CAAC;QACX,QAAQ,EAAE,CAAC;QACX,aAAa,EAAE,MAAM,CAAC,GAAG;QACzB,kBAAkB,EAAE,CAAC;QACrB,YAAY,EAAE,IAAI,GAAG,EAAE;KAC1B,CAAC;AACN,CAAC;AAED,SAAS,aAAa,CAAC,IAA4B,EAAE,MAAc;IAC/D,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;;;;;;;;GAQG;AACH,SAAS,WAAW,CAAC,MAAyB,EAAE,MAAc,EAAE,UAAkB;IAC9E,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACjC,MAAM,CAAC,GAAG,CAAC,KAAK,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QACpE,8DAA8D;QAC9D,4DAA4D;QAC5D,oBAAoB;QACpB,oBAAoB;QACpB,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;YAAE,OAAO,MAAM,CAAC,GAAG,CAAC;QAC3C,IAAI,IAAI,CAAC,CAAC;QACV,IAAI,IAAI,CAAC,CAAC;IACd,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,GAAG,MAAM,CAAC;IAC5B,MAAM,KAAK,GAAG,IAAI,GAAG,MAAM,CAAC;IAC5B,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACjC,MAAM,CAAC,GAAG,CAAC,KAAK,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QACpE,MAAM,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC;QACrB,MAAM,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC;QACrB,KAAK,IAAI,EAAE,GAAG,EAAE,CAAC;QACjB,KAAK,IAAI,EAAE,GAAG,EAAE,CAAC;QACjB,KAAK,IAAI,EAAE,GAAG,EAAE,CAAC;IACrB,CAAC;IACD,IAAI,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC,GAAG,CAAC;IAClD,MAAM,CAAC,GAAG,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC;IAC3C,IAAI,CAAC,GAAG,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC,CAAC;IACtB,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,CAAC,CAAC;IACpB,OAAO,CAAC,CAAC;AACb,CAAC;AAED,SAAS,SAAS,CAAC,IAA4B,EAAE,GAAW;IACxD,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,QAAQ,CAAC;IAExC,6DAA6D;IAC7D,iEAAiE;IACjE,4BAA4B;IAC5B,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAC1C,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC3D,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC;YAAE,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;IAC7D,CAAC;SAAM,CAAC;QACJ,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC,GAAG,CAAC;IACpC,CAAC;IACD,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;IAE9C,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;IAEnB,IAAI,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,MAAM;QAAE,OAAO,MAAM,CAAC,GAAG,CAAC;IACnD,IAAI,IAAI,CAAC,QAAQ,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC,GAAG,CAAC;IACzC,OAAO,WAAW,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAC5D,CAAC;AAED,SAAS,QAAQ,CAAC,IAA4B,EAAE,GAAW;IACvD,IAAI,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,MAAM;QAAE,OAAO,MAAM,CAAC,GAAG,CAAC;IACnD,0DAA0D;IAC1D,6DAA6D;IAC7D,qDAAqD;IACrD,IAAI,QAAQ,GAAG,IAAI,CAAC,kBAAkB,CAAC;IACvC,qEAAqE;IACrE,iEAAiE;IACjE,uCAAuC;IACvC,oBAAoB;IACpB,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC;QAAE,QAAQ,IAAI,CAAC,CAAC;IACpD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,QAAQ,IAAI,CAAC,CAAC;IACzC,IAAI,QAAQ,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC,GAAG,CAAC;IACpC,OAAO,WAAW,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAC5D,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,UAAU,kBAAkB,CAC9B,MAAc,EACd,MAAsB,EACtB,MAAc,EACd,IAA6B;IAE7B,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;IACrB,IAAI,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAuC,CAAC;IAChF,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACrB,IAAI,GAAG,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACzD,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACzC,CAAC;IACD,MAAM,GAAG,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IACpC,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;QACb,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;IACpD,CAAC;SAAM,CAAC;QACJ,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC,CAAC,CAAC;AAClD,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/trend-strength-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. Named `trendStrengthIndex` to disambiguate\n// from the existing `ta.tsi` (Task 14 momentum True Strength Index).\n// The math is TradingView's documented Pearson-of-price-vs-bar-index\n// formulation — `lib/pearson.ts` (Task 4) is the reference helper.\n\nimport type { Series, TrendStrengthIndexOpts } 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\ntype TrendStrengthIndexSlot = {\n readonly outBuffer: Float64RingBuffer;\n readonly series: Series<number>;\n readonly length: number;\n /** Rolling window of recent source values, most-recent first\n * (`at(0)` = head, `at(length - 1)` = oldest). */\n readonly sourceWindow: Float64RingBuffer;\n /** Number of CLOSED bars folded into the slot so far. */\n barCount: number;\n /** Number of NaN values currently in the window — when > 0 the\n * Pearson denominator is undefined and the primitive emits NaN. */\n nanCount: number;\n /** The source value evicted on the current close (the tail value\n * popped when the window was full at append time). Used by tick\n * replay to restore the window to its pre-close state. */\n evictedSource: number;\n /** `nanCount` AS OF THE PRIOR CLOSE — restored by tick replay\n * before substituting the tick's contribution. */\n prevClosedNanCount: number;\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.trendStrengthIndex called outside an active script step\");\n }\n return ctx;\n}\n\nfunction initSlot(length: number, capacity: number): TrendStrengthIndexSlot {\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 nanCount: 0,\n evictedSource: Number.NaN,\n prevClosedNanCount: 0,\n shiftedViews: new Map(),\n };\n}\n\nfunction viewForOffset(slot: TrendStrengthIndexSlot, 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 * Compute Pearson correlation between the trailing source window and a\n * matching length-`n` linear bar-index series `[0, 1, ..., n-1]`.\n * Mirrors `lib/pearson.ts`'s two-pass mean/covariance formulation\n * exactly (sum then mean, then per-slot deviations) so the property\n * test's full-recompute reference and the incremental head agree\n * within Float64 noise. Returns NaN if any slot is non-finite or the\n * source-side window variance is exactly zero.\n */\nfunction pearsonHead(window: Float64RingBuffer, length: number, headSource: number): number {\n let sumX = 0;\n let sumY = 0;\n for (let k = 0; k < length; k += 1) {\n const x = k === length - 1 ? headSource : window.at(length - 1 - k);\n // Defensive: callers gate on `nanCount === 0` so every window\n // slot is finite when we get here; the `headSource` is also\n // guarded upstream.\n /* c8 ignore next */\n if (!Number.isFinite(x)) return Number.NaN;\n sumX += x;\n sumY += k;\n }\n const meanX = sumX / length;\n const meanY = sumY / length;\n let sumXY = 0;\n let sumXX = 0;\n let sumYY = 0;\n for (let k = 0; k < length; k += 1) {\n const x = k === length - 1 ? headSource : window.at(length - 1 - k);\n const dX = x - meanX;\n const dY = k - meanY;\n sumXY += dX * dY;\n sumXX += dX * dX;\n sumYY += dY * dY;\n }\n if (sumXX === 0 || sumYY === 0) return Number.NaN;\n const r = sumXY / Math.sqrt(sumXX * sumYY);\n if (r < -1) return -1;\n if (r > 1) return 1;\n return r;\n}\n\nfunction closeStep(slot: TrendStrengthIndexSlot, src: number): number {\n slot.prevClosedNanCount = slot.nanCount;\n\n // Update the rolling window. If the window is full, the tail\n // (oldest) value is about to be evicted by `append` — capture it\n // and adjust the NaN count.\n if (slot.sourceWindow.length >= slot.length) {\n slot.evictedSource = slot.sourceWindow.at(slot.length - 1);\n if (Number.isNaN(slot.evictedSource)) slot.nanCount -= 1;\n } else {\n slot.evictedSource = Number.NaN;\n }\n slot.sourceWindow.append(src);\n if (!Number.isFinite(src)) slot.nanCount += 1;\n\n slot.barCount += 1;\n\n if (slot.barCount < slot.length) return Number.NaN;\n if (slot.nanCount > 0) return Number.NaN;\n return pearsonHead(slot.sourceWindow, slot.length, src);\n}\n\nfunction tickStep(slot: TrendStrengthIndexSlot, src: number): number {\n if (slot.barCount < slot.length) return Number.NaN;\n // Re-derive NaN count: start from the prior closed count,\n // un-account for the value evicted by the current close, and\n // account for the tick's substituted value at age 0.\n let nanCount = slot.prevClosedNanCount;\n // Defensive: `evictedSource` is only NaN when the close-side advance\n // evicted a NaN, requiring an earlier NaN bar in the stream that\n // we then tick on. Reachable but rare.\n /* c8 ignore next */\n if (Number.isNaN(slot.evictedSource)) nanCount -= 1;\n if (!Number.isFinite(src)) nanCount += 1;\n if (nanCount > 0) return Number.NaN;\n return pearsonHead(slot.sourceWindow, slot.length, src);\n}\n\n/**\n * Trend Strength Index — Pearson correlation between `source` and the\n * bar index over each trailing `length`-bar window. Bounded `[-1, +1]`:\n * `+1` = clean uptrend (price rises monotonically with bar index), `−1`\n * = clean downtrend, `0` = no linear trend. Distinct from\n * `ta.tsi` (the True Strength Index — a momentum oscillator).\n * The math is TradingView's documented Trend Strength Index\n * (https://www.tradingview.com/support/solutions/43000730926-trend-strength-index/).\n *\n * Default `length = 20` per chartlang task spec (invinite plugin\n * default is `14`).\n *\n * @formula meanX = Σx / n ; meanY = Σy / n ; n = length ;\n * num = Σ((x − meanX)(y − meanY)) ;\n * den = sqrt(Σ(x − meanX)² · Σ(y − meanY)²) ;\n * tsi = clamp(num / den, −1, +1) ;\n * NaN if the source window has zero variance, the index\n * variance is zero (`length < 2`), or any window slot is\n * non-finite.\n * @warmup length − 1\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-runtime\";\n * // const t = ta.trendStrengthIndex(\"slot\", bar.close, 20);\n * // plot(t);\n */\nexport function trendStrengthIndex(\n slotId: string,\n source: ScalarOrSeries,\n length: number,\n opts?: TrendStrengthIndexOpts,\n): Series<number> {\n const ctx = getCtx();\n let slot = ctx.stream.taSlots.get(slotId) as TrendStrengthIndexSlot | 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(tickStep(slot, src));\n } else {\n slot.outBuffer.append(closeStep(slot, src));\n }\n return viewForOffset(slot, opts?.offset ?? 0);\n}\n"]}
package/dist/ta/trix.d.ts CHANGED
@@ -20,9 +20,10 @@ import { type ScalarOrSeries } from "./lib/sourceValue.js";
20
20
  * @since 0.2
21
21
  * @stable
22
22
  *
23
- * `opts.offset` shifts both outputs in lockstep
24
- * `series.current` on each output returns the value `offset` bars
25
- * ago.
23
+ * `opts.offset` is a presentation display shift carried to the plot
24
+ * emission as `xShift` for both outputs in lockstep (`+n` right / future,
25
+ * `−n` left / past); the series values are unshifted, so
26
+ * `series.current` on each output returns the value at the current bar.
26
27
  *
27
28
  * @example
28
29
  * // import { ta } from "@invinite-org/chartlang-runtime";
@@ -1 +1 @@
1
- {"version":3,"file":"trix.d.ts","sourceRoot":"","sources":["../../src/ta/trix.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAU,QAAQ,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAMjF,OAAO,EAAE,KAAK,cAAc,EAAmB,MAAM,sBAAsB,CAAC;AA0D5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,IAAI,CAChB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,QAAQ,GAChB,UAAU,CAqDZ"}
1
+ {"version":3,"file":"trix.d.ts","sourceRoot":"","sources":["../../src/ta/trix.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAU,QAAQ,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAMjF,OAAO,EAAE,KAAK,cAAc,EAAmB,MAAM,sBAAsB,CAAC;AA0D5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,IAAI,CAChB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,QAAQ,GAChB,UAAU,CAqDZ"}
package/dist/ta/trix.js CHANGED
@@ -70,9 +70,10 @@ function resultForOffset(slot, offset, signalBuf) {
70
70
  * @since 0.2
71
71
  * @stable
72
72
  *
73
- * `opts.offset` shifts both outputs in lockstep
74
- * `series.current` on each output returns the value `offset` bars
75
- * ago.
73
+ * `opts.offset` is a presentation display shift carried to the plot
74
+ * emission as `xShift` for both outputs in lockstep (`+n` right / future,
75
+ * `−n` left / past); the series values are unshifted, so
76
+ * `series.current` on each output returns the value at the current bar.
76
77
  *
77
78
  * @example
78
79
  * // import { ta } from "@invinite-org/chartlang-runtime";
@@ -1 +1 @@
1
- {"version":3,"file":"trix.js","sourceRoot":"","sources":["../../src/ta/trix.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/trix.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. TRIX composes three EMA sub-slots derived\n// from the parent slot id (`${slotId}/ema1` / `/ema2` / `/ema3`) for\n// the triple-smoothing chain plus a fourth `${slotId}/signal` EMA\n// over the TRIX line. Mirrors the MACD sub-slot composition pattern.\n\nimport type { Series, TrixOpts, TrixResult } from \"@invinite-org/chartlang-core\";\n\nimport { ema } from \"./ema.js\";\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_SIGNAL = 9;\n\ntype TrixSlot = {\n readonly result: TrixResult;\n readonly trixBuffer: Float64RingBuffer;\n /** Closed `ema3` as of the prior closed bar — divisor for the\n * next close's TRIX. */\n prevClosedEma3: number;\n /** Closed `ema3` as of the bar BEFORE the prior closed bar —\n * used by tick replay (the current bar's \"prior\" is two closes\n * back from a tick on the in-progress bar). */\n prevPrevClosedEma3: number;\n /**\n * Per-offset frozen `TrixResult` cache. `offset === 0` returns\n * `result` by identity. Non-zero offsets get a frozen result\n * whose two Series are `makeShiftedSeriesView` proxies over the\n * same two underlying ring buffers.\n */\n readonly shiftedResults: Map<number, TrixResult>;\n};\n\nfunction getCtx(): RuntimeContext {\n const ctx = ACTIVE_RUNTIME_CONTEXT.current;\n if (ctx === null) {\n throw new Error(\"ta.trix called outside an active script step\");\n }\n return ctx;\n}\n\nfunction initSlot(capacity: number, signalSeries: Series<number>): TrixSlot {\n const trixBuffer = new Float64RingBuffer(capacity);\n return {\n result: Object.freeze({\n trix: makeSeriesView<number>(trixBuffer),\n signal: signalSeries,\n }),\n trixBuffer,\n prevClosedEma3: Number.NaN,\n prevPrevClosedEma3: Number.NaN,\n shiftedResults: new Map(),\n };\n}\n\nfunction resultForOffset(slot: TrixSlot, offset: number, signalBuf: Float64RingBuffer): TrixResult {\n if (offset === 0) return slot.result;\n let cached = slot.shiftedResults.get(offset);\n if (cached === undefined) {\n cached = Object.freeze({\n trix: makeShiftedSeriesView<number>(slot.trixBuffer, offset),\n signal: makeShiftedSeriesView<number>(signalBuf, offset),\n });\n slot.shiftedResults.set(offset, cached);\n }\n return cached;\n}\n\n/**\n * TRIX — triple-smoothed EMA rate-of-change momentum oscillator\n * with an EMA-signal line. Composes three EMA sub-slots derived\n * from the parent slot id (`${slotId}/ema1` / `/ema2` / `/ema3`)\n * for the triple-smoothing chain, then computes\n * `100 · (ema3[t] − ema3[t−1]) / ema3[t−1]` per bar, and folds the\n * result into a fourth EMA sub-slot (`${slotId}/signal`) for the\n * signal line.\n *\n * @formula ema1 = EMA(source, length) ;\n * ema2 = EMA(ema1, length) ;\n * ema3 = EMA(ema2, length) ;\n * trix[t] = ema3[t-1] === 0 ? NaN : 100 · (ema3[t] − ema3[t-1]) / ema3[t-1] ;\n * signal[t] = EMA(trix, signalLength)\n * @warmup 3 · length + signalLength − 3 (first defined `signal` index ;\n * trix line first defined at `3 · length − 2`)\n * @anchors length, signalLength\n * @since 0.2\n * @stable\n *\n * `opts.offset` shifts both outputs in lockstep —\n * `series.current` on each output returns the value `offset` bars\n * ago.\n *\n * @example\n * // import { ta } from \"@invinite-org/chartlang-runtime\";\n * // const t = ta.trix(\"slot\", bar.close, 18);\n * // plot(t.trix);\n * // plot(t.signal);\n */\nexport function trix(\n slotId: string,\n source: ScalarOrSeries,\n length: number,\n opts?: TrixOpts,\n): TrixResult {\n const ctx = getCtx();\n const signalLength = opts?.signalLength ?? DEFAULT_SIGNAL;\n const offset = opts?.offset ?? 0;\n const src = readSourceValue(source);\n\n // Triple-smoothing chain. Each outer EMA reads the prior EMA's\n // `.current` scalar.\n const e1 = ema(`${slotId}/ema1`, src, length).current;\n const e2 = ema(`${slotId}/ema2`, e1, length).current;\n const e3 = ema(`${slotId}/ema3`, e2, length).current;\n\n let slot = ctx.stream.taSlots.get(slotId) as TrixSlot | undefined;\n // The TRIX divisor on the current bar is the prior closed `ema3`.\n // On close-side we use `prevClosedEma3` (set at the previous close\n // — this is `ema3[t-1]` from the script-author's view). On ticks\n // we use `prevPrevClosedEma3` (set two closes back) because the\n // CURRENT bar's close has not yet happened — tick replay's \"prev\n // closed ema3\" is still two closes back.\n let prevE3: number;\n if (slot === undefined) prevE3 = Number.NaN;\n else if (ctx.isTick) prevE3 = slot.prevPrevClosedEma3;\n else prevE3 = slot.prevClosedEma3;\n const trixValue =\n Number.isFinite(e3) && Number.isFinite(prevE3) && prevE3 !== 0\n ? (100 * (e3 - prevE3)) / prevE3\n : Number.NaN;\n // Feed the TRIX value into the signal EMA. Always read the\n // un-shifted view; offset shifting for the composite result is\n // handled locally via `resultForOffset`.\n const signalSeries = ema(`${slotId}/signal`, trixValue, signalLength);\n if (slot === undefined) {\n slot = initSlot(ctx.stream.ohlcv.close.capacity, signalSeries);\n ctx.stream.taSlots.set(slotId, slot);\n }\n const signalSubSlot = ctx.stream.taSlots.get(`${slotId}/signal`) as {\n outBuffer: Float64RingBuffer;\n };\n\n if (ctx.isTick) {\n slot.trixBuffer.replaceHead(trixValue);\n // Signal EMA's tick was handled by its own `ema()` call above.\n } else {\n slot.trixBuffer.append(trixValue);\n // Roll the prev-prev snapshot forward BEFORE overwriting\n // `prevClosedEma3` so tick replay on the next bar reads the\n // correct two-closes-back value.\n slot.prevPrevClosedEma3 = slot.prevClosedEma3;\n if (Number.isFinite(e3)) {\n slot.prevClosedEma3 = e3;\n }\n }\n return resultForOffset(slot, offset, signalSubSlot.outBuffer);\n}\n"]}
1
+ {"version":3,"file":"trix.js","sourceRoot":"","sources":["../../src/ta/trix.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/trix.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. TRIX composes three EMA sub-slots derived\n// from the parent slot id (`${slotId}/ema1` / `/ema2` / `/ema3`) for\n// the triple-smoothing chain plus a fourth `${slotId}/signal` EMA\n// over the TRIX line. Mirrors the MACD sub-slot composition pattern.\n\nimport type { Series, TrixOpts, TrixResult } from \"@invinite-org/chartlang-core\";\n\nimport { ema } from \"./ema.js\";\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_SIGNAL = 9;\n\ntype TrixSlot = {\n readonly result: TrixResult;\n readonly trixBuffer: Float64RingBuffer;\n /** Closed `ema3` as of the prior closed bar — divisor for the\n * next close's TRIX. */\n prevClosedEma3: number;\n /** Closed `ema3` as of the bar BEFORE the prior closed bar —\n * used by tick replay (the current bar's \"prior\" is two closes\n * back from a tick on the in-progress bar). */\n prevPrevClosedEma3: number;\n /**\n * Per-offset frozen `TrixResult` cache. `offset === 0` returns\n * `result` by identity. Non-zero offsets get a frozen result\n * whose two Series are `makeShiftedSeriesView` proxies over the\n * same two underlying ring buffers.\n */\n readonly shiftedResults: Map<number, TrixResult>;\n};\n\nfunction getCtx(): RuntimeContext {\n const ctx = ACTIVE_RUNTIME_CONTEXT.current;\n if (ctx === null) {\n throw new Error(\"ta.trix called outside an active script step\");\n }\n return ctx;\n}\n\nfunction initSlot(capacity: number, signalSeries: Series<number>): TrixSlot {\n const trixBuffer = new Float64RingBuffer(capacity);\n return {\n result: Object.freeze({\n trix: makeSeriesView<number>(trixBuffer),\n signal: signalSeries,\n }),\n trixBuffer,\n prevClosedEma3: Number.NaN,\n prevPrevClosedEma3: Number.NaN,\n shiftedResults: new Map(),\n };\n}\n\nfunction resultForOffset(slot: TrixSlot, offset: number, signalBuf: Float64RingBuffer): TrixResult {\n if (offset === 0) return slot.result;\n let cached = slot.shiftedResults.get(offset);\n if (cached === undefined) {\n cached = Object.freeze({\n trix: makeShiftedSeriesView<number>(slot.trixBuffer, offset),\n signal: makeShiftedSeriesView<number>(signalBuf, offset),\n });\n slot.shiftedResults.set(offset, cached);\n }\n return cached;\n}\n\n/**\n * TRIX — triple-smoothed EMA rate-of-change momentum oscillator\n * with an EMA-signal line. Composes three EMA sub-slots derived\n * from the parent slot id (`${slotId}/ema1` / `/ema2` / `/ema3`)\n * for the triple-smoothing chain, then computes\n * `100 · (ema3[t] − ema3[t−1]) / ema3[t−1]` per bar, and folds the\n * result into a fourth EMA sub-slot (`${slotId}/signal`) for the\n * signal line.\n *\n * @formula ema1 = EMA(source, length) ;\n * ema2 = EMA(ema1, length) ;\n * ema3 = EMA(ema2, length) ;\n * trix[t] = ema3[t-1] === 0 ? NaN : 100 · (ema3[t] − ema3[t-1]) / ema3[t-1] ;\n * signal[t] = EMA(trix, signalLength)\n * @warmup 3 · length + signalLength − 3 (first defined `signal` index ;\n * trix line first defined at `3 · length − 2`)\n * @anchors length, signalLength\n * @since 0.2\n * @stable\n *\n * `opts.offset` is a presentation display shift carried to the plot\n * emission as `xShift` for both outputs in lockstep (`+n` right / future,\n * `−n` left / past); the series values are unshifted, so\n * `series.current` on each output returns the value at the current bar.\n *\n * @example\n * // import { ta } from \"@invinite-org/chartlang-runtime\";\n * // const t = ta.trix(\"slot\", bar.close, 18);\n * // plot(t.trix);\n * // plot(t.signal);\n */\nexport function trix(\n slotId: string,\n source: ScalarOrSeries,\n length: number,\n opts?: TrixOpts,\n): TrixResult {\n const ctx = getCtx();\n const signalLength = opts?.signalLength ?? DEFAULT_SIGNAL;\n const offset = opts?.offset ?? 0;\n const src = readSourceValue(source);\n\n // Triple-smoothing chain. Each outer EMA reads the prior EMA's\n // `.current` scalar.\n const e1 = ema(`${slotId}/ema1`, src, length).current;\n const e2 = ema(`${slotId}/ema2`, e1, length).current;\n const e3 = ema(`${slotId}/ema3`, e2, length).current;\n\n let slot = ctx.stream.taSlots.get(slotId) as TrixSlot | undefined;\n // The TRIX divisor on the current bar is the prior closed `ema3`.\n // On close-side we use `prevClosedEma3` (set at the previous close\n // — this is `ema3[t-1]` from the script-author's view). On ticks\n // we use `prevPrevClosedEma3` (set two closes back) because the\n // CURRENT bar's close has not yet happened — tick replay's \"prev\n // closed ema3\" is still two closes back.\n let prevE3: number;\n if (slot === undefined) prevE3 = Number.NaN;\n else if (ctx.isTick) prevE3 = slot.prevPrevClosedEma3;\n else prevE3 = slot.prevClosedEma3;\n const trixValue =\n Number.isFinite(e3) && Number.isFinite(prevE3) && prevE3 !== 0\n ? (100 * (e3 - prevE3)) / prevE3\n : Number.NaN;\n // Feed the TRIX value into the signal EMA. Always read the\n // un-shifted view; offset shifting for the composite result is\n // handled locally via `resultForOffset`.\n const signalSeries = ema(`${slotId}/signal`, trixValue, signalLength);\n if (slot === undefined) {\n slot = initSlot(ctx.stream.ohlcv.close.capacity, signalSeries);\n ctx.stream.taSlots.set(slotId, slot);\n }\n const signalSubSlot = ctx.stream.taSlots.get(`${slotId}/signal`) as {\n outBuffer: Float64RingBuffer;\n };\n\n if (ctx.isTick) {\n slot.trixBuffer.replaceHead(trixValue);\n // Signal EMA's tick was handled by its own `ema()` call above.\n } else {\n slot.trixBuffer.append(trixValue);\n // Roll the prev-prev snapshot forward BEFORE overwriting\n // `prevClosedEma3` so tick replay on the next bar reads the\n // correct two-closes-back value.\n slot.prevPrevClosedEma3 = slot.prevClosedEma3;\n if (Number.isFinite(e3)) {\n slot.prevClosedEma3 = e3;\n }\n }\n return resultForOffset(slot, offset, signalSubSlot.outBuffer);\n}\n"]}
@@ -17,8 +17,9 @@ import type { VortexOpts, VortexResult } from "@invinite-org/chartlang-core";
17
17
  * @since 0.2
18
18
  * @stable
19
19
  *
20
- * `opts.offset` shifts both series 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 both series 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":"vortex.d.ts","sourceRoot":"","sources":["../../src/ta/vortex.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AA4M7E;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,UAAU,GAAG,YAAY,CAkBtF"}
1
+ {"version":3,"file":"vortex.d.ts","sourceRoot":"","sources":["../../src/ta/vortex.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AA4M7E;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,UAAU,GAAG,YAAY,CAkBtF"}
package/dist/ta/vortex.js CHANGED
@@ -178,8 +178,9 @@ function tickStep(slot, high, low, close) {
178
178
  * @since 0.2
179
179
  * @stable
180
180
  *
181
- * `opts.offset` shifts both series in lockstep
182
- * `series.current` on each output returns the value `offset` bars ago.
181
+ * `opts.offset` is a presentation display shift carried to the plot
182
+ * emission as `xShift` for both series in lockstep (`+n` right / future,
183
+ * `−n` left / past); the series values are unshifted.
183
184
  *
184
185
  * @example
185
186
  * // import { ta } from "@invinite-org/chartlang-runtime";
@@ -1 +1 @@
1
- {"version":3,"file":"vortex.js","sourceRoot":"","sources":["../../src/ta/vortex.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/vortex.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. Vortex reads `bar.high` / `bar.low` /\n// `bar.close` directly (mirrors Pine's `ta.vortex(length)` — no source\n// param) and maintains rolling running-sum windows over `vmPlus`,\n// `vmMinus`, and TR for O(1) per-bar updates. NaN-on-zero-TR semantics\n// per chartlang task spec §6 (invinite emits 0 on zero TR; chartlang\n// emits NaN to surface the degenerate window).\n\nimport type { VortexOpts, VortexResult } 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 VortexSlot = {\n readonly result: VortexResult;\n readonly plusBuffer: Float64RingBuffer;\n readonly minusBuffer: Float64RingBuffer;\n readonly length: number;\n readonly vmPlusWindow: Float64RingBuffer;\n readonly vmMinusWindow: Float64RingBuffer;\n readonly trWindow: Float64RingBuffer;\n runningPlus: number;\n runningMinus: number;\n runningTr: number;\n prevHigh: number;\n prevLow: number;\n prevClose: number;\n /** Snapshots captured at the prior close so tick replay can read\n * against the bar-before-current. */\n prevPrevHigh: number;\n prevPrevLow: number;\n prevPrevClose: number;\n /** Running sums AS OF THE PRIOR CLOSE — used by tick replay to\n * substitute the tick's contribution. */\n prevClosedRunningPlus: number;\n prevClosedRunningMinus: number;\n prevClosedRunningTr: number;\n /** Tail values evicted on the current close (the\n * `length`-bars-ago slot at the moment the current close advanced\n * the window). Used by tick replay to recompute the running sum\n * with the tick's substituted contribution. */\n evictedPlus: number;\n evictedMinus: number;\n evictedTr: number;\n /** Number of CLOSED bars folded into the slot so far. */\n barCount: number;\n readonly shiftedResults: Map<number, VortexResult>;\n};\n\nfunction getCtx(): RuntimeContext {\n const ctx = ACTIVE_RUNTIME_CONTEXT.current;\n if (ctx === null) {\n throw new Error(\"ta.vortex called outside an active script step\");\n }\n return ctx;\n}\n\nfunction trueRange(high: number, low: number, prevClose: number): number {\n if (!Number.isFinite(prevClose)) return high - low;\n return Math.max(high - low, Math.abs(high - prevClose), Math.abs(low - prevClose));\n}\n\nfunction initSlot(length: number, capacity: number): VortexSlot {\n const plusBuffer = new Float64RingBuffer(capacity);\n const minusBuffer = new Float64RingBuffer(capacity);\n return {\n result: Object.freeze({\n plus: makeSeriesView<number>(plusBuffer),\n minus: makeSeriesView<number>(minusBuffer),\n }),\n plusBuffer,\n minusBuffer,\n length,\n vmPlusWindow: new Float64RingBuffer(length),\n vmMinusWindow: new Float64RingBuffer(length),\n trWindow: new Float64RingBuffer(length),\n runningPlus: 0,\n runningMinus: 0,\n runningTr: 0,\n prevHigh: Number.NaN,\n prevLow: Number.NaN,\n prevClose: Number.NaN,\n prevPrevHigh: Number.NaN,\n prevPrevLow: Number.NaN,\n prevPrevClose: Number.NaN,\n prevClosedRunningPlus: 0,\n prevClosedRunningMinus: 0,\n prevClosedRunningTr: 0,\n evictedPlus: 0,\n evictedMinus: 0,\n evictedTr: 0,\n barCount: 0,\n shiftedResults: new Map(),\n };\n}\n\nfunction resultForOffset(slot: VortexSlot, offset: number): VortexResult {\n if (offset === 0) return slot.result;\n let cached = slot.shiftedResults.get(offset);\n if (cached === undefined) {\n cached = Object.freeze({\n plus: makeShiftedSeriesView<number>(slot.plusBuffer, offset),\n minus: makeShiftedSeriesView<number>(slot.minusBuffer, offset),\n });\n slot.shiftedResults.set(offset, cached);\n }\n return cached;\n}\n\nfunction divide(num: number, den: number): number {\n // chartlang task spec §6: zero-TR window → NaN (degenerate; invinite\n // emits 0). Surface as NaN so script-author conditionals can branch.\n if (den === 0 || !Number.isFinite(den) || !Number.isFinite(num)) return Number.NaN;\n return num / den;\n}\n\nfunction closeStep(\n slot: VortexSlot,\n high: number,\n low: number,\n close: number,\n): { plus: number; minus: number } {\n if (!Number.isFinite(high) || !Number.isFinite(low) || !Number.isFinite(close)) {\n // Hold state forward; emit NaN.\n return { plus: Number.NaN, minus: Number.NaN };\n }\n\n slot.barCount += 1;\n\n // Snapshot prior running sums (post-prior-close state — these are\n // the values tick replay needs to re-derive the head against the\n // PRIOR closed bar).\n slot.prevClosedRunningPlus = slot.runningPlus;\n slot.prevClosedRunningMinus = slot.runningMinus;\n slot.prevClosedRunningTr = slot.runningTr;\n\n const tr = trueRange(high, low, slot.prevClose);\n const vmPlus = Number.isFinite(slot.prevLow) ? Math.abs(high - slot.prevLow) : 0;\n const vmMinus = Number.isFinite(slot.prevHigh) ? Math.abs(low - slot.prevHigh) : 0;\n\n // Capture prev-prev BEFORE overwriting prev (tick replay reads\n // against the bar-before-current).\n slot.prevPrevHigh = slot.prevHigh;\n slot.prevPrevLow = slot.prevLow;\n slot.prevPrevClose = slot.prevClose;\n slot.prevHigh = high;\n slot.prevLow = low;\n slot.prevClose = close;\n\n // Roll the windows; capture evicted tail values for tick replay.\n if (slot.vmPlusWindow.length >= slot.length) {\n slot.evictedPlus = slot.vmPlusWindow.at(slot.length - 1);\n slot.evictedMinus = slot.vmMinusWindow.at(slot.length - 1);\n slot.evictedTr = slot.trWindow.at(slot.length - 1);\n slot.runningPlus -= slot.evictedPlus;\n slot.runningMinus -= slot.evictedMinus;\n slot.runningTr -= slot.evictedTr;\n } else {\n slot.evictedPlus = 0;\n slot.evictedMinus = 0;\n slot.evictedTr = 0;\n }\n slot.vmPlusWindow.append(vmPlus);\n slot.vmMinusWindow.append(vmMinus);\n slot.trWindow.append(tr);\n slot.runningPlus += vmPlus;\n slot.runningMinus += vmMinus;\n slot.runningTr += tr;\n\n // Warmup: first defined output at `barCount === length + 1` (i.e.\n // bar index `length` zero-based). The seed window completes when\n // the `length`-th VM/TR triple has been folded in.\n if (slot.barCount <= slot.length) {\n return { plus: Number.NaN, minus: Number.NaN };\n }\n return {\n plus: divide(slot.runningPlus, slot.runningTr),\n minus: divide(slot.runningMinus, slot.runningTr),\n };\n}\n\nfunction tickStep(\n slot: VortexSlot,\n high: number,\n low: number,\n close: number,\n): { plus: number; minus: number } {\n if (slot.barCount <= slot.length) return { plus: Number.NaN, minus: Number.NaN };\n if (!Number.isFinite(high) || !Number.isFinite(low) || !Number.isFinite(close)) {\n return { plus: Number.NaN, minus: Number.NaN };\n }\n // Tick replay against the BAR BEFORE the current bar.\n const tr = trueRange(high, low, slot.prevPrevClose);\n // Defensive: `prevPrev*` is seeded by the prior close-side advance,\n // so by the time `barCount > length` (the guard above) these are\n // finite. The `: 0` fallback is defence-in-depth.\n /* c8 ignore next 2 */\n const vmPlus = Number.isFinite(slot.prevPrevLow) ? Math.abs(high - slot.prevPrevLow) : 0;\n const vmMinus = Number.isFinite(slot.prevPrevHigh) ? Math.abs(low - slot.prevPrevHigh) : 0;\n // Re-derive the running sums by starting from the prior closed\n // sums, subtracting the head bar's evicted tail (captured at the\n // close-side advance), and adding the tick's contribution.\n const runningPlus = slot.prevClosedRunningPlus - slot.evictedPlus + vmPlus;\n const runningMinus = slot.prevClosedRunningMinus - slot.evictedMinus + vmMinus;\n const runningTr = slot.prevClosedRunningTr - slot.evictedTr + tr;\n return {\n plus: divide(runningPlus, runningTr),\n minus: divide(runningMinus, runningTr),\n };\n}\n\n/**\n * Vortex Indicator (Botes & Siepman, 2010) — paired `+VI` / `−VI`\n * trend-direction lines. Reads `bar.high` / `bar.low` / `bar.close`\n * directly (mirrors Pine's `ta.vortex(length)` — no source param).\n * Maintains rolling running-sum windows over the per-bar `vmPlus`,\n * `vmMinus`, and TR series for O(1) per-bar updates. Returns a cached\n * `{ plus, minus }` record (same identity every bar).\n *\n * @formula TR[t] = max(high − low, |high − prevClose|, |low − prevClose|) ;\n * vmPlus[t] = |high[t] − low[t-1]| ;\n * vmMinus[t] = |low[t] − high[t-1]| ;\n * plus[t] = Σ(vmPlus, length) / Σ(TR, length) ;\n * minus[t] = Σ(vmMinus, length) / Σ(TR, length) ;\n * NaN when Σ(TR, length) === 0 (degenerate flat window).\n * @warmup length\n * @since 0.2\n * @stable\n *\n * `opts.offset` shifts both series 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 v = ta.vortex(\"slot\", 14);\n * // plot(v.plus);\n * // plot(v.minus);\n */\nexport function vortex(slotId: string, length: number, opts?: VortexOpts): VortexResult {\n const ctx = getCtx();\n let slot = ctx.stream.taSlots.get(slotId) as VortexSlot | undefined;\n if (slot === undefined) {\n slot = initSlot(length, ctx.stream.ohlcv.close.capacity);\n ctx.stream.taSlots.set(slotId, slot);\n }\n const bar = ctx.stream.bar;\n if (ctx.isTick) {\n const { plus, minus } = tickStep(slot, bar.high, bar.low, bar.close);\n slot.plusBuffer.replaceHead(plus);\n slot.minusBuffer.replaceHead(minus);\n } else {\n const { plus, minus } = closeStep(slot, bar.high, bar.low, bar.close);\n slot.plusBuffer.append(plus);\n slot.minusBuffer.append(minus);\n }\n return resultForOffset(slot, opts?.offset ?? 0);\n}\n"]}
1
+ {"version":3,"file":"vortex.js","sourceRoot":"","sources":["../../src/ta/vortex.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/vortex.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. Vortex reads `bar.high` / `bar.low` /\n// `bar.close` directly (mirrors Pine's `ta.vortex(length)` — no source\n// param) and maintains rolling running-sum windows over `vmPlus`,\n// `vmMinus`, and TR for O(1) per-bar updates. NaN-on-zero-TR semantics\n// per chartlang task spec §6 (invinite emits 0 on zero TR; chartlang\n// emits NaN to surface the degenerate window).\n\nimport type { VortexOpts, VortexResult } 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 VortexSlot = {\n readonly result: VortexResult;\n readonly plusBuffer: Float64RingBuffer;\n readonly minusBuffer: Float64RingBuffer;\n readonly length: number;\n readonly vmPlusWindow: Float64RingBuffer;\n readonly vmMinusWindow: Float64RingBuffer;\n readonly trWindow: Float64RingBuffer;\n runningPlus: number;\n runningMinus: number;\n runningTr: number;\n prevHigh: number;\n prevLow: number;\n prevClose: number;\n /** Snapshots captured at the prior close so tick replay can read\n * against the bar-before-current. */\n prevPrevHigh: number;\n prevPrevLow: number;\n prevPrevClose: number;\n /** Running sums AS OF THE PRIOR CLOSE — used by tick replay to\n * substitute the tick's contribution. */\n prevClosedRunningPlus: number;\n prevClosedRunningMinus: number;\n prevClosedRunningTr: number;\n /** Tail values evicted on the current close (the\n * `length`-bars-ago slot at the moment the current close advanced\n * the window). Used by tick replay to recompute the running sum\n * with the tick's substituted contribution. */\n evictedPlus: number;\n evictedMinus: number;\n evictedTr: number;\n /** Number of CLOSED bars folded into the slot so far. */\n barCount: number;\n readonly shiftedResults: Map<number, VortexResult>;\n};\n\nfunction getCtx(): RuntimeContext {\n const ctx = ACTIVE_RUNTIME_CONTEXT.current;\n if (ctx === null) {\n throw new Error(\"ta.vortex called outside an active script step\");\n }\n return ctx;\n}\n\nfunction trueRange(high: number, low: number, prevClose: number): number {\n if (!Number.isFinite(prevClose)) return high - low;\n return Math.max(high - low, Math.abs(high - prevClose), Math.abs(low - prevClose));\n}\n\nfunction initSlot(length: number, capacity: number): VortexSlot {\n const plusBuffer = new Float64RingBuffer(capacity);\n const minusBuffer = new Float64RingBuffer(capacity);\n return {\n result: Object.freeze({\n plus: makeSeriesView<number>(plusBuffer),\n minus: makeSeriesView<number>(minusBuffer),\n }),\n plusBuffer,\n minusBuffer,\n length,\n vmPlusWindow: new Float64RingBuffer(length),\n vmMinusWindow: new Float64RingBuffer(length),\n trWindow: new Float64RingBuffer(length),\n runningPlus: 0,\n runningMinus: 0,\n runningTr: 0,\n prevHigh: Number.NaN,\n prevLow: Number.NaN,\n prevClose: Number.NaN,\n prevPrevHigh: Number.NaN,\n prevPrevLow: Number.NaN,\n prevPrevClose: Number.NaN,\n prevClosedRunningPlus: 0,\n prevClosedRunningMinus: 0,\n prevClosedRunningTr: 0,\n evictedPlus: 0,\n evictedMinus: 0,\n evictedTr: 0,\n barCount: 0,\n shiftedResults: new Map(),\n };\n}\n\nfunction resultForOffset(slot: VortexSlot, offset: number): VortexResult {\n if (offset === 0) return slot.result;\n let cached = slot.shiftedResults.get(offset);\n if (cached === undefined) {\n cached = Object.freeze({\n plus: makeShiftedSeriesView<number>(slot.plusBuffer, offset),\n minus: makeShiftedSeriesView<number>(slot.minusBuffer, offset),\n });\n slot.shiftedResults.set(offset, cached);\n }\n return cached;\n}\n\nfunction divide(num: number, den: number): number {\n // chartlang task spec §6: zero-TR window → NaN (degenerate; invinite\n // emits 0). Surface as NaN so script-author conditionals can branch.\n if (den === 0 || !Number.isFinite(den) || !Number.isFinite(num)) return Number.NaN;\n return num / den;\n}\n\nfunction closeStep(\n slot: VortexSlot,\n high: number,\n low: number,\n close: number,\n): { plus: number; minus: number } {\n if (!Number.isFinite(high) || !Number.isFinite(low) || !Number.isFinite(close)) {\n // Hold state forward; emit NaN.\n return { plus: Number.NaN, minus: Number.NaN };\n }\n\n slot.barCount += 1;\n\n // Snapshot prior running sums (post-prior-close state — these are\n // the values tick replay needs to re-derive the head against the\n // PRIOR closed bar).\n slot.prevClosedRunningPlus = slot.runningPlus;\n slot.prevClosedRunningMinus = slot.runningMinus;\n slot.prevClosedRunningTr = slot.runningTr;\n\n const tr = trueRange(high, low, slot.prevClose);\n const vmPlus = Number.isFinite(slot.prevLow) ? Math.abs(high - slot.prevLow) : 0;\n const vmMinus = Number.isFinite(slot.prevHigh) ? Math.abs(low - slot.prevHigh) : 0;\n\n // Capture prev-prev BEFORE overwriting prev (tick replay reads\n // against the bar-before-current).\n slot.prevPrevHigh = slot.prevHigh;\n slot.prevPrevLow = slot.prevLow;\n slot.prevPrevClose = slot.prevClose;\n slot.prevHigh = high;\n slot.prevLow = low;\n slot.prevClose = close;\n\n // Roll the windows; capture evicted tail values for tick replay.\n if (slot.vmPlusWindow.length >= slot.length) {\n slot.evictedPlus = slot.vmPlusWindow.at(slot.length - 1);\n slot.evictedMinus = slot.vmMinusWindow.at(slot.length - 1);\n slot.evictedTr = slot.trWindow.at(slot.length - 1);\n slot.runningPlus -= slot.evictedPlus;\n slot.runningMinus -= slot.evictedMinus;\n slot.runningTr -= slot.evictedTr;\n } else {\n slot.evictedPlus = 0;\n slot.evictedMinus = 0;\n slot.evictedTr = 0;\n }\n slot.vmPlusWindow.append(vmPlus);\n slot.vmMinusWindow.append(vmMinus);\n slot.trWindow.append(tr);\n slot.runningPlus += vmPlus;\n slot.runningMinus += vmMinus;\n slot.runningTr += tr;\n\n // Warmup: first defined output at `barCount === length + 1` (i.e.\n // bar index `length` zero-based). The seed window completes when\n // the `length`-th VM/TR triple has been folded in.\n if (slot.barCount <= slot.length) {\n return { plus: Number.NaN, minus: Number.NaN };\n }\n return {\n plus: divide(slot.runningPlus, slot.runningTr),\n minus: divide(slot.runningMinus, slot.runningTr),\n };\n}\n\nfunction tickStep(\n slot: VortexSlot,\n high: number,\n low: number,\n close: number,\n): { plus: number; minus: number } {\n if (slot.barCount <= slot.length) return { plus: Number.NaN, minus: Number.NaN };\n if (!Number.isFinite(high) || !Number.isFinite(low) || !Number.isFinite(close)) {\n return { plus: Number.NaN, minus: Number.NaN };\n }\n // Tick replay against the BAR BEFORE the current bar.\n const tr = trueRange(high, low, slot.prevPrevClose);\n // Defensive: `prevPrev*` is seeded by the prior close-side advance,\n // so by the time `barCount > length` (the guard above) these are\n // finite. The `: 0` fallback is defence-in-depth.\n /* c8 ignore next 2 */\n const vmPlus = Number.isFinite(slot.prevPrevLow) ? Math.abs(high - slot.prevPrevLow) : 0;\n const vmMinus = Number.isFinite(slot.prevPrevHigh) ? Math.abs(low - slot.prevPrevHigh) : 0;\n // Re-derive the running sums by starting from the prior closed\n // sums, subtracting the head bar's evicted tail (captured at the\n // close-side advance), and adding the tick's contribution.\n const runningPlus = slot.prevClosedRunningPlus - slot.evictedPlus + vmPlus;\n const runningMinus = slot.prevClosedRunningMinus - slot.evictedMinus + vmMinus;\n const runningTr = slot.prevClosedRunningTr - slot.evictedTr + tr;\n return {\n plus: divide(runningPlus, runningTr),\n minus: divide(runningMinus, runningTr),\n };\n}\n\n/**\n * Vortex Indicator (Botes & Siepman, 2010) — paired `+VI` / `−VI`\n * trend-direction lines. Reads `bar.high` / `bar.low` / `bar.close`\n * directly (mirrors Pine's `ta.vortex(length)` — no source param).\n * Maintains rolling running-sum windows over the per-bar `vmPlus`,\n * `vmMinus`, and TR series for O(1) per-bar updates. Returns a cached\n * `{ plus, minus }` record (same identity every bar).\n *\n * @formula TR[t] = max(high − low, |high − prevClose|, |low − prevClose|) ;\n * vmPlus[t] = |high[t] − low[t-1]| ;\n * vmMinus[t] = |low[t] − high[t-1]| ;\n * plus[t] = Σ(vmPlus, length) / Σ(TR, length) ;\n * minus[t] = Σ(vmMinus, length) / Σ(TR, length) ;\n * NaN when Σ(TR, length) === 0 (degenerate flat window).\n * @warmup length\n * @since 0.2\n * @stable\n *\n * `opts.offset` is a presentation display shift carried to the plot\n * emission as `xShift` for both series 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 v = ta.vortex(\"slot\", 14);\n * // plot(v.plus);\n * // plot(v.minus);\n */\nexport function vortex(slotId: string, length: number, opts?: VortexOpts): VortexResult {\n const ctx = getCtx();\n let slot = ctx.stream.taSlots.get(slotId) as VortexSlot | undefined;\n if (slot === undefined) {\n slot = initSlot(length, ctx.stream.ohlcv.close.capacity);\n ctx.stream.taSlots.set(slotId, slot);\n }\n const bar = ctx.stream.bar;\n if (ctx.isTick) {\n const { plus, minus } = tickStep(slot, bar.high, bar.low, bar.close);\n slot.plusBuffer.replaceHead(plus);\n slot.minusBuffer.replaceHead(minus);\n } else {\n const { plus, minus } = closeStep(slot, bar.high, bar.low, bar.close);\n slot.plusBuffer.append(plus);\n slot.minusBuffer.append(minus);\n }\n return resultForOffset(slot, opts?.offset ?? 0);\n}\n"]}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Integer floor-division rounding toward −∞ (unlike JS `/ | 0`, which
3
+ * truncates toward `0`). Required so pre-1970 epochs split correctly.
4
+ *
5
+ * @since 1.5
6
+ * @stable
7
+ * @example
8
+ * // import { floorDiv } from "@invinite-org/chartlang-runtime";
9
+ * // floorDiv(-1, 86_400_000); // -1, not 0
10
+ */
11
+ export declare function floorDiv(a: number, b: number): number;
12
+ /**
13
+ * Non-negative modulo (Euclidean) — `mod(-1, 7) === 6`, unlike JS `%`
14
+ * which would yield `-1`. Pairs with {@link floorDiv} for correct
15
+ * pre-epoch date math.
16
+ *
17
+ * @since 1.5
18
+ * @stable
19
+ * @example
20
+ * // import { mod } from "@invinite-org/chartlang-runtime";
21
+ * // mod(-1, 7); // 6
22
+ */
23
+ export declare function mod(a: number, b: number): number;
24
+ /**
25
+ * The civil date for a count of days since the Unix epoch
26
+ * (1970-01-01). `m` is `1..12`, `d` is `1..31`. Pure integer
27
+ * arithmetic — correct for negative (pre-epoch) `z`.
28
+ *
29
+ * @since 1.5
30
+ * @stable
31
+ * @example
32
+ * // import { civilFromDays } from "@invinite-org/chartlang-runtime";
33
+ * // civilFromDays(0); // { y: 1970, m: 1, d: 1 }
34
+ */
35
+ export declare function civilFromDays(z: number): {
36
+ y: number;
37
+ m: number;
38
+ d: number;
39
+ };
40
+ /**
41
+ * Days since the Unix epoch (1970-01-01) for a civil `y` / `m` (`1..12`)
42
+ * / `d` (`1..31`). Inverse of {@link civilFromDays}. Pure integer
43
+ * arithmetic — correct for pre-epoch dates.
44
+ *
45
+ * @since 1.5
46
+ * @stable
47
+ * @example
48
+ * // import { daysFromCivil } from "@invinite-org/chartlang-runtime";
49
+ * // daysFromCivil(1970, 1, 1); // 0
50
+ */
51
+ export declare function daysFromCivil(y: number, m: number, d: number): number;
52
+ /**
53
+ * Split a UTC ms epoch (after pre-shifting by `offsetMin` minutes) into
54
+ * civil fields. `dow` is `0=Sun .. 6=Sat`, derived from the day count
55
+ * (`mod(z + 4, 7)` — 1970-01-01 was a Thursday, index 4). All integer
56
+ * arithmetic on the floored ms — no `Date`.
57
+ *
58
+ * @since 1.5
59
+ * @stable
60
+ * @example
61
+ * // import { splitEpoch } from "@invinite-org/chartlang-runtime";
62
+ * // splitEpoch(0, 0); // { y: 1970, m: 1, d: 1, hh: 0, mm: 0, ss: 0, dow: 4 }
63
+ */
64
+ export declare function splitEpoch(ms: number, offsetMin: number): {
65
+ y: number;
66
+ m: number;
67
+ d: number;
68
+ hh: number;
69
+ mm: number;
70
+ ss: number;
71
+ dow: number;
72
+ };
73
+ //# sourceMappingURL=civil.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"civil.d.ts","sourceRoot":"","sources":["../../src/time-accessors/civil.ts"],"names":[],"mappings":"AAUA;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAc5E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAOrE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CACtB,EAAE,EAAE,MAAM,EACV,SAAS,EAAE,MAAM,GAClB;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CActF"}