@svgrid/grid 3.0.2 → 3.0.4

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 (281) hide show
  1. package/README.md +2 -1
  2. package/dist/GridMenus.svelte +44 -2
  3. package/dist/SvChartMenu.svelte +69 -0
  4. package/dist/SvChartMenu.svelte.d.ts +11 -0
  5. package/dist/SvChartPanes.svelte +113 -0
  6. package/dist/SvChartPanes.svelte.d.ts +20 -0
  7. package/dist/SvGrid.controller.svelte.d.ts +110 -39
  8. package/dist/SvGrid.controller.svelte.js +655 -200
  9. package/dist/SvGrid.css +105 -2
  10. package/dist/SvGrid.svelte +386 -514
  11. package/dist/SvGrid.types.d.ts +210 -9
  12. package/dist/SvGridCellEditor.svelte +17 -0
  13. package/dist/SvGridChart.svelte +2257 -329
  14. package/dist/SvGridChart.svelte.d.ts +6 -106
  15. package/dist/SvGridChart.types.d.ts +338 -0
  16. package/dist/SvGridChart.types.js +1 -0
  17. package/dist/SvGridChartBuilder.svelte +323 -0
  18. package/dist/SvGridChartBuilder.svelte.d.ts +34 -0
  19. package/dist/SvGridChartPanel.svelte +221 -275
  20. package/dist/SvGridChartPickers.svelte +417 -0
  21. package/dist/SvGridChartPickers.svelte.d.ts +70 -0
  22. package/dist/SvGridChartView.svelte +13 -14
  23. package/dist/SvModal.svelte +7 -1
  24. package/dist/SvModal.svelte.d.ts +5 -0
  25. package/dist/ai.d.ts +40 -8
  26. package/dist/ai.js +135 -24
  27. package/dist/aria.d.ts +11 -0
  28. package/dist/build-api.js +108 -22
  29. package/dist/cdn/GridMenus-7kbpnnBW.js +635 -0
  30. package/dist/cdn/GridMenus-BL7ZgQvU.js +636 -0
  31. package/dist/cdn/SvChartMenu-Bl6PBbkT.js +58 -0
  32. package/dist/cdn/SvChartMenu-FBSMINA6.js +59 -0
  33. package/dist/cdn/{SvDateRangeInput-DMLKmGEc.js → SvDateRangeInput-BeU_TkbQ.js} +15 -14
  34. package/dist/cdn/{SvDateRangeInput-CaOuMs8O.js → SvDateRangeInput-yX8vzleW.js} +38 -37
  35. package/dist/cdn/{SvDateTimePicker-sonaH0oh.js → SvDateTimePicker-DQwt4UAs.js} +139 -180
  36. package/dist/cdn/SvDateTimePicker-vNU6bZ-q.js +775 -0
  37. package/dist/cdn/{SvGridCellEditor-CjEJMJKc.js → SvGridCellEditor-B1p-vCK5.js} +188 -178
  38. package/dist/cdn/{SvGridCellEditor-BWnTi2N7.js → SvGridCellEditor-D_0q4xAu.js} +98 -88
  39. package/dist/cdn/SvGridChart-C3EWAZaM.js +3574 -0
  40. package/dist/cdn/SvGridChart-DzLkSwCH.js +3572 -0
  41. package/dist/cdn/SvGridChartBuilder-BE2T1ykB.js +783 -0
  42. package/dist/cdn/SvGridChartBuilder-CfII62sZ.js +784 -0
  43. package/dist/cdn/SvGridChartPanel-CCX5_Wgd.js +675 -0
  44. package/dist/cdn/SvGridChartPanel-xf4sWVTo.js +699 -0
  45. package/dist/cdn/SvGridChartView-CfuXmY5I.js +48 -0
  46. package/dist/cdn/SvGridChartView-DYwabQWj.js +47 -0
  47. package/dist/cdn/SvMenuList-CLN8OuIK.js +452 -0
  48. package/dist/cdn/SvMenuList-DkGhNKjf.js +451 -0
  49. package/dist/cdn/SvModal-BAE-pjZX.js +396 -0
  50. package/dist/cdn/SvModal-CIZWCcad.js +397 -0
  51. package/dist/cdn/chart-Dz7SqMXH.js +4341 -0
  52. package/dist/cdn/chart-export-BXqueeWJ.js +135 -0
  53. package/dist/cdn/chart-export-pdf-DONFUnNY.js +110 -0
  54. package/dist/cdn/chart-panel-messages-CUbf2R4i.js +1052 -0
  55. package/dist/cdn/chart-panel-messages-CmNrMdsr.js +1053 -0
  56. package/dist/cdn/chart-summary-BJW_tg_X.js +138 -0
  57. package/dist/cdn/chart-trend-CaN9mDEV.js +50 -0
  58. package/dist/cdn/chart-validate-HcOVFAfJ.js +188 -0
  59. package/dist/cdn/{column-resize-DsfNXMom.js → column-resize-DpmOLfRp.js} +3 -3
  60. package/dist/cdn/{date-format-BnnHlqGw.js → date-format-CtquV-p3.js} +583 -603
  61. package/dist/cdn/{date-format-BNii4zeD.js → date-format-D6KFzU_W.js} +347 -367
  62. package/dist/cdn/dismissable-DAHetSNk.js +44 -0
  63. package/dist/cdn/editor-contract-LXgQJAgd.js +22 -0
  64. package/dist/cdn/focus-trap-BBOQYBma.js +65 -0
  65. package/dist/cdn/{row-resize-BRcimkUT.js → row-resize-niQCp040.js} +44 -36
  66. package/dist/cdn/{src-DNbz94Ct.js → src-BKhZ6eXd.js} +10227 -9787
  67. package/dist/cdn/{src-BJ2opd0j.js → src-V1uu8iE9.js} +6568 -6128
  68. package/dist/cdn/svgrid.js +26 -15
  69. package/dist/cdn/svgrid.svelte-external.js +26 -15
  70. package/dist/chart-axes.d.ts +53 -0
  71. package/dist/chart-axes.js +351 -0
  72. package/dist/chart-cartesian.d.ts +88 -0
  73. package/dist/chart-cartesian.js +1862 -0
  74. package/dist/chart-decimate.d.ts +51 -0
  75. package/dist/chart-decimate.js +199 -0
  76. package/dist/chart-export-pdf.d.ts +47 -0
  77. package/dist/chart-export-pdf.js +187 -0
  78. package/dist/chart-export.d.ts +2 -1
  79. package/dist/chart-export.js +18 -3
  80. package/dist/chart-financial.d.ts +91 -0
  81. package/dist/chart-financial.js +175 -0
  82. package/dist/chart-flow.d.ts +10 -0
  83. package/dist/chart-flow.js +298 -0
  84. package/dist/chart-format.d.ts +18 -0
  85. package/dist/chart-format.js +65 -0
  86. package/dist/chart-grid.d.ts +8 -0
  87. package/dist/chart-grid.js +190 -0
  88. package/dist/chart-hierarchy.d.ts +17 -0
  89. package/dist/chart-hierarchy.js +146 -0
  90. package/dist/chart-indicators.d.ts +115 -0
  91. package/dist/chart-indicators.js +389 -0
  92. package/dist/chart-messages.d.ts +95 -0
  93. package/dist/chart-messages.js +92 -0
  94. package/dist/chart-motion.d.ts +16 -0
  95. package/dist/chart-motion.js +141 -0
  96. package/dist/chart-panel-messages.d.ts +265 -0
  97. package/dist/chart-panel-messages.js +285 -0
  98. package/dist/chart-pivot.d.ts +87 -0
  99. package/dist/chart-pivot.js +156 -0
  100. package/dist/chart-polar.d.ts +25 -0
  101. package/dist/chart-polar.js +700 -0
  102. package/dist/chart-samples.d.ts +16 -0
  103. package/dist/chart-samples.js +153 -0
  104. package/dist/chart-scale.d.ts +113 -0
  105. package/dist/chart-scale.js +405 -0
  106. package/dist/chart-stats.d.ts +111 -0
  107. package/dist/chart-stats.js +419 -0
  108. package/dist/chart-stream.d.ts +45 -0
  109. package/dist/chart-stream.js +91 -0
  110. package/dist/chart-summary.d.ts +27 -0
  111. package/dist/chart-summary.js +205 -0
  112. package/dist/chart-sync.svelte.d.ts +32 -0
  113. package/dist/chart-sync.svelte.js +23 -0
  114. package/dist/chart-table.d.ts +35 -0
  115. package/dist/chart-table.js +165 -0
  116. package/dist/chart-trend.d.ts +19 -0
  117. package/dist/chart-trend.js +61 -0
  118. package/dist/chart-types.d.ts +1456 -0
  119. package/dist/chart-types.js +6 -0
  120. package/dist/chart-validate.d.ts +43 -0
  121. package/dist/chart-validate.js +272 -0
  122. package/dist/chart-zoom.d.ts +40 -0
  123. package/dist/chart-zoom.js +145 -0
  124. package/dist/chart.d.ts +186 -865
  125. package/dist/chart.js +460 -2204
  126. package/dist/clipboard.d.ts +1 -0
  127. package/dist/clipboard.js +137 -18
  128. package/dist/column-resize.d.ts +3 -0
  129. package/dist/column-resize.js +4 -1
  130. package/dist/columns.js +3 -0
  131. package/dist/command-context.d.ts +19 -0
  132. package/dist/command-context.js +116 -0
  133. package/dist/core.d.ts +7 -0
  134. package/dist/createPopoverSelect.svelte.d.ts +2 -2
  135. package/dist/editing.d.ts +2 -8
  136. package/dist/editing.js +262 -33
  137. package/dist/fill-patterns.d.ts +0 -5
  138. package/dist/fill-patterns.js +56 -2
  139. package/dist/grid-messages.d.ts +10 -3
  140. package/dist/grid-messages.js +5 -0
  141. package/dist/history.d.ts +83 -0
  142. package/dist/history.js +133 -0
  143. package/dist/index.d.ts +24 -6
  144. package/dist/index.js +20 -2
  145. package/dist/keyboard-handlers.js +88 -31
  146. package/dist/keyboard.d.ts +55 -0
  147. package/dist/keyboard.js +132 -0
  148. package/dist/menus.d.ts +3 -1
  149. package/dist/menus.js +7 -2
  150. package/dist/merges.d.ts +97 -0
  151. package/dist/merges.js +147 -0
  152. package/dist/row-resize.d.ts +3 -0
  153. package/dist/row-resize.js +14 -0
  154. package/dist/scroll-sync.d.ts +1 -0
  155. package/dist/scroll-sync.js +24 -0
  156. package/dist/selection.d.ts +16 -0
  157. package/dist/selection.js +108 -16
  158. package/dist/shortcut-registry.d.ts +145 -0
  159. package/dist/shortcut-registry.js +44 -0
  160. package/dist/svgrid-wrapper.types.d.ts +213 -22
  161. package/dist/virtualization/virtualizer.js +38 -17
  162. package/package.json +6 -1
  163. package/src/GridMenus.svelte +44 -2
  164. package/src/SvChartMenu.svelte +69 -0
  165. package/src/SvChartPanes.svelte +113 -0
  166. package/src/SvChartPanes.test.ts +66 -0
  167. package/src/SvGrid.controller.svelte.ts +629 -172
  168. package/src/SvGrid.css +105 -2
  169. package/src/SvGrid.svelte +386 -514
  170. package/src/SvGrid.types.ts +188 -8
  171. package/src/SvGridCellEditor.svelte +17 -0
  172. package/src/SvGridChart.svelte +2257 -329
  173. package/src/SvGridChart.test.ts +1419 -1
  174. package/src/SvGridChart.types.ts +336 -0
  175. package/src/SvGridChartBuilder.svelte +323 -0
  176. package/src/SvGridChartPanel.svelte +221 -275
  177. package/src/SvGridChartPickers.svelte +417 -0
  178. package/src/SvGridChartView.svelte +13 -14
  179. package/src/SvModal.svelte +7 -1
  180. package/src/SvModal.test.ts +9 -0
  181. package/src/ai.test.ts +57 -2
  182. package/src/ai.ts +166 -22
  183. package/src/aria.d.ts +11 -0
  184. package/src/build-api.coverage.test.ts +184 -0
  185. package/src/build-api.ts +107 -18
  186. package/src/chart-axes.test.ts +573 -0
  187. package/src/chart-axes.ts +342 -0
  188. package/src/chart-cartesian.ts +1863 -0
  189. package/src/chart-decimate.test.ts +249 -0
  190. package/src/chart-decimate.ts +186 -0
  191. package/src/chart-export-pdf.test.ts +91 -0
  192. package/src/chart-export-pdf.ts +207 -0
  193. package/src/chart-export.test.ts +14 -0
  194. package/src/chart-export.ts +13 -3
  195. package/src/chart-financial.ts +208 -0
  196. package/src/chart-flow.ts +286 -0
  197. package/src/chart-format.ts +67 -0
  198. package/src/chart-grid.ts +179 -0
  199. package/src/chart-hierarchy.ts +135 -0
  200. package/src/chart-indicators.test.ts +443 -0
  201. package/src/chart-indicators.ts +384 -0
  202. package/src/chart-messages.test.ts +21 -0
  203. package/src/chart-messages.ts +184 -0
  204. package/src/chart-motion.test.ts +71 -0
  205. package/src/chart-motion.ts +148 -0
  206. package/src/chart-panel-messages.test.ts +29 -0
  207. package/src/chart-panel-messages.ts +550 -0
  208. package/src/chart-pivot.test.ts +54 -0
  209. package/src/chart-pivot.ts +225 -0
  210. package/src/chart-polar.ts +674 -0
  211. package/src/chart-samples.ts +106 -0
  212. package/src/chart-scale.ts +394 -0
  213. package/src/chart-series-types.test.ts +714 -0
  214. package/src/chart-stats.ts +409 -0
  215. package/src/chart-stream.test.ts +75 -0
  216. package/src/chart-stream.ts +114 -0
  217. package/src/chart-summary.test.ts +90 -0
  218. package/src/chart-summary.ts +197 -0
  219. package/src/chart-sync.svelte.ts +41 -0
  220. package/src/chart-table.test.ts +76 -0
  221. package/src/chart-table.ts +178 -0
  222. package/src/chart-trend.ts +47 -0
  223. package/src/chart-types.ts +1309 -0
  224. package/src/chart-validate.test.ts +100 -0
  225. package/src/chart-validate.ts +247 -0
  226. package/src/chart-zoom.test.ts +137 -0
  227. package/src/chart-zoom.ts +124 -0
  228. package/src/chart.coverage.test.ts +81 -0
  229. package/src/chart.test.ts +56 -3
  230. package/src/chart.ts +557 -2784
  231. package/src/clipboard.test.ts +151 -0
  232. package/src/clipboard.ts +145 -16
  233. package/src/column-resize.ts +7 -1
  234. package/src/columns.ts +2 -0
  235. package/src/command-context.test.ts +200 -0
  236. package/src/command-context.ts +137 -0
  237. package/src/core.ts +7 -0
  238. package/src/editing.test.ts +281 -3
  239. package/src/editing.ts +256 -38
  240. package/src/fill-patterns.test.ts +35 -0
  241. package/src/fill-patterns.ts +56 -2
  242. package/src/grid-messages.ts +16 -3
  243. package/src/history.test.ts +196 -0
  244. package/src/history.ts +162 -0
  245. package/src/icon-seam.test.ts +3 -2
  246. package/src/index.ts +150 -2
  247. package/src/keyboard-handlers.coverage.test.ts +126 -6
  248. package/src/keyboard-handlers.ts +91 -30
  249. package/src/keyboard-shortcuts.seam.test.ts +200 -0
  250. package/src/keyboard.test.ts +124 -1
  251. package/src/keyboard.ts +142 -0
  252. package/src/menus.test.ts +26 -0
  253. package/src/menus.ts +10 -3
  254. package/src/merges.test.ts +112 -0
  255. package/src/merges.ts +168 -0
  256. package/src/pivot.grid.test.ts +40 -5
  257. package/src/row-resize.test.ts +44 -0
  258. package/src/row-resize.ts +17 -0
  259. package/src/scroll-sync.test.ts +42 -1
  260. package/src/scroll-sync.ts +25 -0
  261. package/src/selection.multi-range.test.ts +34 -0
  262. package/src/selection.test.ts +30 -2
  263. package/src/selection.ts +111 -19
  264. package/src/shortcut-registry.test.ts +97 -0
  265. package/src/shortcut-registry.ts +181 -0
  266. package/src/svgrid-wrapper.types.ts +184 -13
  267. package/src/svgrid.behavior.test.ts +135 -0
  268. package/src/svgrid.charting.test.ts +513 -3
  269. package/src/svgrid.context-menu.test.ts +31 -0
  270. package/src/svgrid.new-features.wrapper.test.ts +10 -3
  271. package/src/virtualization/virtualizer.ts +39 -17
  272. package/dist/cdn/GridMenus-DyFA-QxA.js +0 -602
  273. package/dist/cdn/GridMenus-M2XP9t0_.js +0 -603
  274. package/dist/cdn/SvDateTimePicker-CCbDZNZB.js +0 -816
  275. package/dist/cdn/SvGridChart-DAb9RbRs.js +0 -1480
  276. package/dist/cdn/SvGridChart-Yl-IXZFO.js +0 -1481
  277. package/dist/cdn/SvGridChartPanel-DayQpDsR.js +0 -834
  278. package/dist/cdn/SvGridChartPanel-aqf8n-vJ.js +0 -810
  279. package/dist/cdn/SvGridChartView-CxfakxR0.js +0 -56
  280. package/dist/cdn/SvGridChartView-Dm_YEx9L.js +0 -55
  281. package/dist/cdn/chart-CKJRCPOk.js +0 -1652
@@ -0,0 +1,389 @@
1
+ import { exponentialFit, exponentialMovingAverage, linearFit, logarithmicFit, polynomialFit, powerFit, simpleMovingAverage } from './chart-stats.js';
2
+ const nanArray = (n) => new Array(n).fill(Number.NaN);
3
+ /** Weighted moving average: the latest value weighs `period`, the oldest 1. */
4
+ export function wma(values, period) {
5
+ const n = values.length;
6
+ const p = Math.max(1, Math.floor(period));
7
+ const out = nanArray(n);
8
+ const denom = (p * (p + 1)) / 2;
9
+ for (let i = p - 1; i < n; i += 1) {
10
+ let sum = 0;
11
+ let ok = true;
12
+ for (let k = 0; k < p; k += 1) {
13
+ const v = values[i - k];
14
+ if (!Number.isFinite(v)) {
15
+ ok = false;
16
+ break;
17
+ }
18
+ sum += v * (p - k);
19
+ }
20
+ if (ok)
21
+ out[i] = sum / denom;
22
+ }
23
+ return out;
24
+ }
25
+ /**
26
+ * Bollinger bands: a simple moving average with a band `k` standard
27
+ * deviations either side (population deviation, as the terminals compute it).
28
+ */
29
+ export function bollingerBands(values, period = 20, k = 2) {
30
+ const n = values.length;
31
+ const p = Math.max(1, Math.floor(period));
32
+ const middle = simpleMovingAverage(values, p);
33
+ const upper = nanArray(n);
34
+ const lower = nanArray(n);
35
+ for (let i = p - 1; i < n; i += 1) {
36
+ const m = middle[i];
37
+ if (!Number.isFinite(m))
38
+ continue;
39
+ let ss = 0;
40
+ let count = 0;
41
+ for (let j = i - p + 1; j <= i; j += 1) {
42
+ const v = values[j];
43
+ if (!Number.isFinite(v))
44
+ continue;
45
+ ss += (v - m) * (v - m);
46
+ count += 1;
47
+ }
48
+ const sd = count ? Math.sqrt(ss / count) : 0;
49
+ upper[i] = m + k * sd;
50
+ lower[i] = m - k * sd;
51
+ }
52
+ return { middle, upper, lower };
53
+ }
54
+ /**
55
+ * Relative strength index over `period` bars, 0 to 100. The first value lands
56
+ * at index `period`; before it there is nothing to average.
57
+ */
58
+ export function rsi(values, period = 14) {
59
+ const n = values.length;
60
+ const p = Math.max(1, Math.floor(period));
61
+ const out = nanArray(n);
62
+ if (n <= p)
63
+ return out;
64
+ let gain = 0;
65
+ let loss = 0;
66
+ for (let i = 1; i <= p; i += 1) {
67
+ const d = values[i] - values[i - 1];
68
+ if (d > 0)
69
+ gain += d;
70
+ else
71
+ loss -= d;
72
+ }
73
+ let avgGain = gain / p;
74
+ let avgLoss = loss / p;
75
+ const rs = (g, l) => (l === 0 ? 100 : 100 - 100 / (1 + g / l));
76
+ out[p] = rs(avgGain, avgLoss);
77
+ for (let i = p + 1; i < n; i += 1) {
78
+ const d = values[i] - values[i - 1];
79
+ avgGain = (avgGain * (p - 1) + (d > 0 ? d : 0)) / p;
80
+ avgLoss = (avgLoss * (p - 1) + (d < 0 ? -d : 0)) / p;
81
+ out[i] = rs(avgGain, avgLoss);
82
+ }
83
+ return out;
84
+ }
85
+ /**
86
+ * Moving average convergence / divergence: the fast EMA minus the slow one,
87
+ * its signal EMA, and the histogram between them.
88
+ */
89
+ export function macd(values, fast = 12, slow = 26, signal = 9) {
90
+ const n = values.length;
91
+ const f = exponentialMovingAverage(values, fast);
92
+ const s = exponentialMovingAverage(values, slow);
93
+ const line = nanArray(n);
94
+ // The slow EMA is only meaningful once it has seen `slow` values; before
95
+ // that both averages are still warming up and the difference is noise.
96
+ for (let i = slow - 1; i < n; i += 1)
97
+ line[i] = f[i] - s[i];
98
+ const sig = nanArray(n);
99
+ const hist = nanArray(n);
100
+ const alpha = 2 / (Math.max(1, signal) + 1);
101
+ let prev = null;
102
+ let seen = 0;
103
+ for (let i = 0; i < n; i += 1) {
104
+ const v = line[i];
105
+ if (!Number.isFinite(v))
106
+ continue;
107
+ seen += 1;
108
+ prev = prev == null ? v : alpha * v + (1 - alpha) * prev;
109
+ if (seen >= signal) {
110
+ sig[i] = prev;
111
+ hist[i] = v - prev;
112
+ }
113
+ }
114
+ return { macd: line, signal: sig, histogram: hist };
115
+ }
116
+ /** Typical price of a bar: the mean of its high, low and close. */
117
+ const typical = (b) => (b.h + b.l + b.c) / 3;
118
+ /**
119
+ * Volume-weighted average price, cumulative from the first bar. Reset it per
120
+ * session by resampling or slicing before calling.
121
+ */
122
+ export function vwap(ohlc, volumes) {
123
+ const n = ohlc.length;
124
+ const out = nanArray(n);
125
+ let pv = 0;
126
+ let vol = 0;
127
+ for (let i = 0; i < n; i += 1) {
128
+ const b = ohlc[i];
129
+ const v = volumes[i];
130
+ if (!b || !Number.isFinite(v)) {
131
+ out[i] = vol > 0 ? pv / vol : Number.NaN;
132
+ continue;
133
+ }
134
+ pv += typical(b) * v;
135
+ vol += v;
136
+ out[i] = vol > 0 ? pv / vol : Number.NaN;
137
+ }
138
+ return out;
139
+ }
140
+ /** True range of bar i: the widest of high-low, high-prevClose, low-prevClose. */
141
+ function trueRange(ohlc, i) {
142
+ const b = ohlc[i];
143
+ if (!b)
144
+ return Number.NaN;
145
+ const prev = i > 0 ? ohlc[i - 1] : null;
146
+ if (!prev)
147
+ return b.h - b.l;
148
+ return Math.max(b.h - b.l, Math.abs(b.h - prev.c), Math.abs(b.l - prev.c));
149
+ }
150
+ /** Average true range with Wilder's smoothing. */
151
+ export function atr(ohlc, period = 14) {
152
+ const n = ohlc.length;
153
+ const p = Math.max(1, Math.floor(period));
154
+ const out = nanArray(n);
155
+ if (n < p)
156
+ return out;
157
+ let sum = 0;
158
+ for (let i = 0; i < p; i += 1) {
159
+ const tr = trueRange(ohlc, i);
160
+ if (!Number.isFinite(tr))
161
+ return out;
162
+ sum += tr;
163
+ }
164
+ let prev = sum / p;
165
+ out[p - 1] = prev;
166
+ for (let i = p; i < n; i += 1) {
167
+ const tr = trueRange(ohlc, i);
168
+ if (!Number.isFinite(tr)) {
169
+ out[i] = Number.NaN;
170
+ continue;
171
+ }
172
+ prev = (prev * (p - 1) + tr) / p;
173
+ out[i] = prev;
174
+ }
175
+ return out;
176
+ }
177
+ /**
178
+ * Stochastic oscillator: %K is where the close sits in the last `period`
179
+ * bars' range (0 to 100), %D its `smooth`-bar simple average.
180
+ */
181
+ export function stochastic(ohlc, period = 14, smooth = 3) {
182
+ const n = ohlc.length;
183
+ const p = Math.max(1, Math.floor(period));
184
+ const k = nanArray(n);
185
+ for (let i = p - 1; i < n; i += 1) {
186
+ let hi = -Infinity;
187
+ let lo = Infinity;
188
+ let ok = true;
189
+ for (let j = i - p + 1; j <= i; j += 1) {
190
+ const b = ohlc[j];
191
+ if (!b) {
192
+ ok = false;
193
+ break;
194
+ }
195
+ if (b.h > hi)
196
+ hi = b.h;
197
+ if (b.l < lo)
198
+ lo = b.l;
199
+ }
200
+ const c = ohlc[i]?.c;
201
+ if (!ok || c == null)
202
+ continue;
203
+ k[i] = hi === lo ? 50 : ((c - lo) / (hi - lo)) * 100;
204
+ }
205
+ // %D is a strict average: it starts once `smooth` %K values exist, rather
206
+ // than averaging whatever is there (which is what the tolerant SMA does).
207
+ const sm = Math.max(1, Math.floor(smooth));
208
+ const d = nanArray(n);
209
+ for (let i = sm - 1; i < n; i += 1) {
210
+ let sum = 0;
211
+ let ok = true;
212
+ for (let j = i - sm + 1; j <= i; j += 1) {
213
+ if (!Number.isFinite(k[j])) {
214
+ ok = false;
215
+ break;
216
+ }
217
+ sum += k[j];
218
+ }
219
+ if (ok)
220
+ d[i] = sum / sm;
221
+ }
222
+ return { k, d };
223
+ }
224
+ /** On-balance volume: a running total that adds volume on up closes and
225
+ * subtracts it on down closes. */
226
+ export function obv(closes, volumes) {
227
+ const n = closes.length;
228
+ const out = nanArray(n);
229
+ let total = 0;
230
+ for (let i = 0; i < n; i += 1) {
231
+ const v = volumes[i];
232
+ if (!Number.isFinite(closes[i]) || !Number.isFinite(v)) {
233
+ out[i] = total;
234
+ continue;
235
+ }
236
+ if (i > 0 && Number.isFinite(closes[i - 1])) {
237
+ if (closes[i] > closes[i - 1])
238
+ total += v;
239
+ else if (closes[i] < closes[i - 1])
240
+ total -= v;
241
+ }
242
+ out[i] = total;
243
+ }
244
+ return out;
245
+ }
246
+ /**
247
+ * Compute overlay values for a series spec like 'sma:7' / 'ema:14' / 'wma:9' /
248
+ * 'linear'. `'bb:N:K'` returns the band's middle line (the engine draws the
249
+ * band from `bollingerBands` itself) and `'vwap'` needs bars and volumes, so
250
+ * it comes back as gaps here; pass `extra` for those two.
251
+ */
252
+ export function computeOverlay(values, spec, extra) {
253
+ return computeOverlayFit(values, spec, extra).values;
254
+ }
255
+ /**
256
+ * {@link computeOverlay} plus what a regression knows about itself: its
257
+ * R-squared and equation. The moving averages, the bands and VWAP carry
258
+ * neither.
259
+ */
260
+ export function computeOverlayFit(values, spec, extra) {
261
+ const fit = (f) => (f.equation ? { values: f.fitted, r2: f.r2, equation: f.equation } : { values: f.fitted });
262
+ const reg = regressionFit(values, spec);
263
+ if (reg)
264
+ return fit(reg);
265
+ return { values: computeOverlayPlain(values, spec, extra) };
266
+ }
267
+ /**
268
+ * The regression an overlay string names (`linear`, `poly:N`, `exp`, `log`,
269
+ * `power`), fitted to `values` on their index or on `xs`; `null` for the
270
+ * moving averages and bands, which are not regressions. A scatter passes its
271
+ * points' x and y so the curve is y on x, and reads `predict` to draw it.
272
+ */
273
+ export function regressionFit(values, spec, xs) {
274
+ if (spec === 'linear')
275
+ return linearFit(values, xs);
276
+ if (spec === 'exp')
277
+ return exponentialFit(values, xs);
278
+ if (spec === 'log')
279
+ return logarithmicFit(values, xs);
280
+ if (spec === 'power')
281
+ return powerFit(values, xs);
282
+ const poly = /^poly:(\d+)$/.exec(spec);
283
+ if (poly)
284
+ return polynomialFit(values, Number(poly[1]), xs);
285
+ return null;
286
+ }
287
+ /** The name a tooltip or legend gives an overlay: `sma:20` reads "SMA 20",
288
+ * `poly:3` "poly 3", `bb:20:2` "BB 20", the rest as written. */
289
+ export function overlayName(spec) {
290
+ const m = /^(sma|ema|wma|bb|poly):(\d+)/.exec(spec);
291
+ if (!m)
292
+ return spec === 'linear' ? 'trend' : spec;
293
+ return `${m[1] === 'poly' ? 'poly' : m[1].toUpperCase()} ${m[2]}`;
294
+ }
295
+ function computeOverlayPlain(values, spec, extra) {
296
+ if (spec === 'vwap')
297
+ return extra?.ohlc && extra.volumes ? vwap(extra.ohlc, extra.volumes) : values.map(() => NaN);
298
+ const bb = /^bb:(\d+):(\d+(?:\.\d+)?)$/.exec(spec);
299
+ if (bb)
300
+ return bollingerBands(values, Number(bb[1]), Number(bb[2])).middle;
301
+ const m = /^(sma|ema|wma):(\d+)$/.exec(spec);
302
+ if (!m)
303
+ return values.map(() => NaN);
304
+ const period = Number(m[2]);
305
+ if (m[1] === 'wma')
306
+ return wma(values, period);
307
+ return m[1] === 'ema' ? exponentialMovingAverage(values, period) : simpleMovingAverage(values, period);
308
+ }
309
+ /** The series an indicator reads: by label, else the first with `ohlc`, else the first. */
310
+ function sourceSeries(spec, ind) {
311
+ if (ind.source)
312
+ return spec.series.find((s) => s.label === ind.source) ?? null;
313
+ return spec.series.find((s) => s.ohlc) ?? spec.series[0] ?? null;
314
+ }
315
+ /** Closes of a series: its `ohlc` closes when it has them, else its values. */
316
+ function closesOf(s) {
317
+ if (s.ohlc)
318
+ return s.ohlc.map((b) => (b ? b.c : Number.NaN));
319
+ return s.values.slice();
320
+ }
321
+ /**
322
+ * Build the spec of an indicator pane from a price spec: the same categories
323
+ * and x axis, the indicator's lines or bars, and the reference lines the
324
+ * indicator is read against (30 / 70 for RSI, 20 / 80 for the stochastic, a
325
+ * zero line for MACD). `SvChartPanes` stacks these under the price chart and
326
+ * syncs them; a pane is also a complete spec for a standalone `SvChart`.
327
+ */
328
+ export function indicatorPane(spec, ind) {
329
+ const src = sourceSeries(spec, ind);
330
+ const n = spec.categories.length;
331
+ const closes = src ? closesOf(src) : nanArray(n);
332
+ const ohlc = src?.ohlc ?? closes.map((c) => (Number.isFinite(c) ? { o: c, h: c, l: c, c } : null));
333
+ const volumes = src?.volumes ?? nanArray(n);
334
+ const params = ind.params ?? {};
335
+ const base = {
336
+ categories: spec.categories,
337
+ xType: spec.xType,
338
+ xAxis: spec.xAxis,
339
+ height: ind.height ?? Math.max(90, Math.round((spec.height ?? 300) / 3)),
340
+ palette: spec.palette,
341
+ valueFormat: 'number',
342
+ locale: spec.locale,
343
+ };
344
+ const refs = (lines) => lines.map(([value, label]) => ({ value, label, dashed: true }));
345
+ switch (ind.kind) {
346
+ case 'rsi': {
347
+ const p = params.period ?? 14;
348
+ return { ...base, type: 'line', title: `RSI ${p}`, series: [{ label: `RSI ${p}`, values: rsi(closes, p), marker: 'none' }],
349
+ yAxis: { min: 0, max: 100, tickInterval: 50, nice: false }, referenceLines: refs([[30, '30'], [70, '70']]) };
350
+ }
351
+ case 'stochastic': {
352
+ const p = params.period ?? 14;
353
+ const sm = params.smooth ?? 3;
354
+ const { k, d } = stochastic(ohlc, p, sm);
355
+ return { ...base, type: 'line', title: `Stochastic ${p}, ${sm}`, series: [
356
+ { label: '%K', values: k, marker: 'none' }, { label: '%D', values: d, marker: 'none', dash: [4, 3] },
357
+ ], yAxis: { min: 0, max: 100, tickInterval: 50, nice: false }, referenceLines: refs([[20, '20'], [80, '80']]) };
358
+ }
359
+ case 'macd': {
360
+ const fast = params.fast ?? 12;
361
+ const slow = params.slow ?? 26;
362
+ const signal = params.signal ?? 9;
363
+ const m = macd(closes, fast, slow, signal);
364
+ return { ...base, type: 'bar', title: `MACD ${fast}, ${slow}, ${signal}`, series: [
365
+ { label: 'Histogram', values: m.histogram, colors: m.histogram.map((v) => (v < 0 ? '#ef4444' : '#16a34a')) },
366
+ { label: 'MACD', values: m.macd, type: 'line', marker: 'none' },
367
+ { label: 'Signal', values: m.signal, type: 'line', marker: 'none', dash: [4, 3] },
368
+ ], referenceLines: refs([[0, '']]) };
369
+ }
370
+ case 'atr': {
371
+ const p = params.period ?? 14;
372
+ return { ...base, type: 'line', title: `ATR ${p}`, series: [{ label: `ATR ${p}`, values: atr(ohlc, p), marker: 'none' }], yAxis: { min: 0 } };
373
+ }
374
+ case 'obv':
375
+ return { ...base, type: 'line', title: 'OBV', series: [{ label: 'OBV', values: obv(closes, volumes), marker: 'none' }], valueFormat: 'compact' };
376
+ case 'volume':
377
+ default: {
378
+ // Up bars in the up colour, down bars in the down colour, like the candles.
379
+ const colors = ohlc.map((b, i) => {
380
+ if (!b)
381
+ return null;
382
+ const prev = i > 0 ? ohlc[i - 1] : null;
383
+ const up = prev ? b.c >= prev.c : b.c >= b.o;
384
+ return up ? '#16a34a' : '#ef4444';
385
+ });
386
+ return { ...base, type: 'bar', title: 'Volume', series: [{ label: 'Volume', values: volumes.slice(), colors }], yAxis: { min: 0 }, valueFormat: 'compact' };
387
+ }
388
+ }
389
+ }
@@ -0,0 +1,95 @@
1
+ /** Every localizable chart string, grouped by area in the comments. */
2
+ export type ChartMessages = {
3
+ /** The SVG's accessible name. `{type}` is the chart type. */
4
+ chartLabel: string;
5
+ /** The hidden data table's caption. `{type}` is the chart type. */
6
+ dataTable: string;
7
+ /** Appended to the caption when the table is capped. `{shown}` / `{total}`. */
8
+ dataTableCapped: string;
9
+ noData: string;
10
+ /** A point with no value, in labels and the live region. */
11
+ noValue: string;
12
+ /** The live region while stepping through series: `{series}`, `{value}`, `{category}`. */
13
+ seriesAtCategory: string;
14
+ /** A dense chart's single hit zone: `{count}`, `{from}`, `{to}`. */
15
+ densePoints: string;
16
+ /** Appended to a slice or cell label that drills further. */
17
+ drillIn: string;
18
+ /** A sankey node's label: `{label}`, `{in}`, `{out}`. */
19
+ sankeyNode: string;
20
+ /** The donut's centre caption under the total. */
21
+ total: string;
22
+ /** A regression overlay's goodness of fit in the tooltip. `{value}` is 0..1. */
23
+ rSquared: string;
24
+ /** The context menu item that copies the chart's plain-language summary. */
25
+ describeChart: string;
26
+ /** The live region after the summary was copied. */
27
+ describeCopied: string;
28
+ drillPath: string;
29
+ drillRoot: string;
30
+ dateRange: string;
31
+ /** A preset button's title. `{preset}` is its label. */
32
+ showLast: string;
33
+ pan: string;
34
+ panTitle: string;
35
+ resetZoom: string;
36
+ resetZoomTitle: string;
37
+ annotate: string;
38
+ annotateOnTitle: string;
39
+ annotateOffTitle: string;
40
+ drawingTools: string;
41
+ /** A drawing tool button's title. `{tool}` is the tool's label. */
42
+ draw: string;
43
+ toolTrend: string;
44
+ toolRay: string;
45
+ toolFib: string;
46
+ toolRect: string;
47
+ toolArrow: string;
48
+ toolText: string;
49
+ /** A drawing's accessible name. `{tool}` is the tool's label. */
50
+ drawing: string;
51
+ deleteDrawing: string;
52
+ deleteDrawingTitle: string;
53
+ clearDrawings: string;
54
+ clearDrawingsTitle: string;
55
+ noteText: string;
56
+ /** A note marker's name while annotating. `{label}` is the note. */
57
+ removeNote: string;
58
+ png: string;
59
+ downloadPng: string;
60
+ svg: string;
61
+ downloadSvg: string;
62
+ pdf: string;
63
+ downloadPdf: string;
64
+ downloadCsv: string;
65
+ print: string;
66
+ printTitle: string;
67
+ copy: string;
68
+ copied: string;
69
+ copyTitle: string;
70
+ copyAsImage: string;
71
+ range: string;
72
+ series: string;
73
+ showSeries: string;
74
+ hideSeries: string;
75
+ isolateHint: string;
76
+ /** The overflow toggle. `{n}` is how many chips are folded away. */
77
+ showMore: string;
78
+ showLess: string;
79
+ rangeBrush: string;
80
+ /** The brush's value text. `{from}` / `{to}` are the window's ends. */
81
+ brushRange: string;
82
+ };
83
+ /** The English defaults. */
84
+ export declare const defaultChartMessages: ChartMessages;
85
+ /**
86
+ * Merge a consumer's `localeText` over the English defaults. Undefined or
87
+ * empty values fall back to the default, so a partial map only replaces the
88
+ * keys it sets.
89
+ */
90
+ export declare function resolveChartMessages(overrides?: Partial<ChartMessages> | null): ChartMessages;
91
+ /**
92
+ * Fill a message's `{name}` placeholders. Unknown placeholders are left as
93
+ * they are, so a translation that drops one loses nothing.
94
+ */
95
+ export declare function chartMessage(template: string, vars: Record<string, string | number>): string;
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Chart chrome localization. Every string `SvChart` renders on its own -
3
+ * toolbar buttons and their titles, the context menu, the legend's overflow
4
+ * and hint text, the empty state, and everything it says to assistive
5
+ * technology (the image label, the data table caption, the live region, the
6
+ * marks' labels) - lives here as a flat map with English defaults. Pass
7
+ * `localeText` on `<SvChart>` to override any subset; unset keys fall back
8
+ * to English, so omitting it is a no-op. The same `resolveMessages` idiom as
9
+ * the grid's `localeText` (grid-messages.ts) and the editors'.
10
+ *
11
+ * Placeholders in braces (`{type}`, `{n}`) are filled by {@link chartMessage}.
12
+ */
13
+ import { resolveMessages } from './editor-contract.js';
14
+ /** The English defaults. */
15
+ export const defaultChartMessages = {
16
+ chartLabel: '{type} chart',
17
+ dataTable: '{type} chart data',
18
+ dataTableCapped: ', first {shown} of {total} rows',
19
+ noData: 'No data',
20
+ noValue: 'no value',
21
+ seriesAtCategory: '{series}: {value} at {category}',
22
+ densePoints: '{count} points, {from} to {to}. Arrow keys step through values.',
23
+ drillIn: ', drill in',
24
+ sankeyNode: '{label}: in {in}, out {out}',
25
+ total: 'Total',
26
+ rSquared: 'R\u00b2 {value}',
27
+ describeChart: 'Describe chart',
28
+ describeCopied: 'Description copied',
29
+ drillPath: 'Drilldown path',
30
+ drillRoot: 'All',
31
+ dateRange: 'Date range',
32
+ showLast: 'Show the last {preset}',
33
+ pan: 'Pan',
34
+ panTitle: 'Drag the plot to pan the zoomed window',
35
+ resetZoom: 'Reset zoom',
36
+ resetZoomTitle: 'Reset zoom (or double-click the plot)',
37
+ annotate: 'Annotate',
38
+ annotateOnTitle: 'Click the plot to pin a note, or a marker to remove it',
39
+ annotateOffTitle: 'Pin notes on the chart',
40
+ drawingTools: 'Drawing tools',
41
+ draw: 'Draw: {tool}',
42
+ toolTrend: 'Trend',
43
+ toolRay: 'Ray',
44
+ toolFib: 'Fib',
45
+ toolRect: 'Rect',
46
+ toolArrow: 'Arrow',
47
+ toolText: 'Text',
48
+ drawing: '{tool} drawing',
49
+ deleteDrawing: 'Delete',
50
+ deleteDrawingTitle: 'Remove the selected drawing (Delete)',
51
+ clearDrawings: 'Clear',
52
+ clearDrawingsTitle: 'Remove every drawing',
53
+ noteText: 'Note text',
54
+ removeNote: 'Remove note: {label}',
55
+ png: 'PNG',
56
+ downloadPng: 'Download PNG',
57
+ svg: 'SVG',
58
+ downloadSvg: 'Download SVG',
59
+ pdf: 'PDF',
60
+ downloadPdf: 'Download PDF',
61
+ downloadCsv: 'Download CSV',
62
+ print: 'Print',
63
+ printTitle: 'Print the chart',
64
+ copy: 'Copy',
65
+ copied: '✓ Copied',
66
+ copyTitle: 'Copy chart as image',
67
+ copyAsImage: 'Copy as image',
68
+ range: 'Range',
69
+ series: 'Series',
70
+ showSeries: 'Show',
71
+ hideSeries: 'Hide',
72
+ isolateHint: 'double-click to isolate',
73
+ showMore: '+{n} more',
74
+ showLess: 'Show less',
75
+ rangeBrush: 'Range brush',
76
+ brushRange: '{from} to {to}',
77
+ };
78
+ /**
79
+ * Merge a consumer's `localeText` over the English defaults. Undefined or
80
+ * empty values fall back to the default, so a partial map only replaces the
81
+ * keys it sets.
82
+ */
83
+ export function resolveChartMessages(overrides) {
84
+ return resolveMessages(defaultChartMessages, overrides);
85
+ }
86
+ /**
87
+ * Fill a message's `{name}` placeholders. Unknown placeholders are left as
88
+ * they are, so a translation that drops one loses nothing.
89
+ */
90
+ export function chartMessage(template, vars) {
91
+ return template.replace(/\{(\w+)\}/g, (m, k) => (k in vars ? String(vars[k]) : m));
92
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Data-update tweens: a geometry part-way between two laid-out charts, so a
3
+ * value change slides the marks into place instead of snapping them.
4
+ *
5
+ * Marks are matched by identity (series + category, or node path), and a mark
6
+ * with no counterpart on the other side grows from / shrinks to nothing.
7
+ * Anything not worth tweening (ticks' labels, the legend, the hit layer) is
8
+ * taken from the target as-is. Pure, and cheap enough to run per frame on the
9
+ * sizes the chart draws without decimation.
10
+ */
11
+ import type { ChartGeometry } from './chart-types.js';
12
+ /**
13
+ * The geometry at fraction `t` (0..1) between `from` and `to`. At `t >= 1`
14
+ * this is `to` itself, so a finished tween costs nothing.
15
+ */
16
+ export declare function interpolateGeometry(from: ChartGeometry, to: ChartGeometry, t: number): ChartGeometry;