@luxalgo/vela 0.6.15 → 0.6.16

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 (34) hide show
  1. package/README.md +8 -1
  2. package/dist/chunk-FE6V2YAQ.js +1 -0
  3. package/dist/{chunk-J5QIR2Z3.js → chunk-GIJYO4LO.js} +2436 -115
  4. package/dist/{chunk-32A7KHFM.js → chunk-I77F2SVE.js} +7 -1
  5. package/dist/{chunk-MWDNPGU3.js → chunk-MGU3USTP.js} +22 -15
  6. package/dist/{chunk-MWS4CUJL.js → chunk-Q3TFJNJW.js} +1 -1
  7. package/dist/{chunk-7C7NWEXH.js → chunk-SEOO6YG7.js} +82 -55
  8. package/dist/{contributions-x7iamlnd.d.cts → contributions-BEkxwA5F.d.cts} +27 -11
  9. package/dist/{contributions-h5BzEiV2.d.ts → contributions-CoAX7_SF.d.ts} +27 -11
  10. package/dist/index.cjs +2665 -337
  11. package/dist/index.d.cts +3 -3
  12. package/dist/index.d.ts +3 -3
  13. package/dist/index.js +4 -4
  14. package/dist/{plugin-BLaNPh55.d.ts → plugin-CNUgbRNq.d.ts} +1 -1
  15. package/dist/{plugin-2sJcf5mk.d.cts → plugin-Dxyq35Zf.d.cts} +1 -1
  16. package/dist/plugin.d.cts +2 -2
  17. package/dist/plugin.d.ts +2 -2
  18. package/dist/plugin.js +2 -2
  19. package/dist/{statusline-model--tH0VWCy.d.cts → statusline-model-CbPza-Wc.d.cts} +1 -1
  20. package/dist/{statusline-model-CLpaO3Dl.d.ts → statusline-model-D7cWkw8A.d.ts} +1 -1
  21. package/dist/ui.cjs +22 -15
  22. package/dist/ui.js +2 -2
  23. package/dist/vela.global.js +2501 -173
  24. package/dist/vela.global.min.js +52 -52
  25. package/dist/widget.cjs +2563 -202
  26. package/dist/widget.d.cts +4 -4
  27. package/dist/widget.d.ts +4 -4
  28. package/dist/widget.js +6 -6
  29. package/dist/workspace.cjs +2563 -202
  30. package/dist/workspace.d.cts +23 -12
  31. package/dist/workspace.d.ts +23 -12
  32. package/dist/workspace.js +5 -5
  33. package/package.json +1 -1
  34. package/dist/chunk-H26BEHF4.js +0 -7
package/dist/index.cjs CHANGED
@@ -135,7 +135,7 @@ var LiveSession = class {
135
135
 
136
136
  // src/core/engine/IndicatorHandleImpl.ts
137
137
  var IndicatorHandleImpl = class {
138
- constructor(id, title, controller, source) {
138
+ constructor(id, title, controller, source, nativeType) {
139
139
  this.controller = controller;
140
140
  this.schema = [];
141
141
  this.propsSchema = [];
@@ -144,6 +144,7 @@ var IndicatorHandleImpl = class {
144
144
  this.id = id;
145
145
  this.title = title;
146
146
  if (source !== void 0) this.source = source;
147
+ if (nativeType !== void 0) this.nativeType = nativeType;
147
148
  }
148
149
  get inputs() {
149
150
  return this.schema;
@@ -234,24 +235,24 @@ function summarizeModel(model) {
234
235
  }
235
236
  function inspectModels(models) {
236
237
  const indicators = models.map(summarizeModel);
237
- const sum = (pick) => indicators.reduce((n, s) => n + pick(s), 0);
238
+ const sum2 = (pick) => indicators.reduce((n, s) => n + pick(s), 0);
238
239
  return {
239
240
  indicators,
240
241
  totals: {
241
242
  panes: new Set(indicators.map((s) => s.paneId ?? "price")).size,
242
- series: sum((s) => Object.values(s.series).reduce((a, b) => a + b, 0)),
243
- fills: sum((s) => s.fills),
244
- backgrounds: sum((s) => s.backgrounds),
245
- priceLines: sum((s) => s.priceLines),
246
- lines: sum((s) => s.lines),
247
- boxes: sum((s) => s.boxes),
248
- labels: sum((s) => s.labels),
249
- polylines: sum((s) => s.polylines),
250
- linefills: sum((s) => s.linefills),
251
- tables: sum((s) => s.tables),
252
- barColors: sum((s) => s.barColors),
253
- trades: sum((s) => s.trades),
254
- forcedOverlay: sum((s) => s.forcedOverlay)
243
+ series: sum2((s) => Object.values(s.series).reduce((a, b) => a + b, 0)),
244
+ fills: sum2((s) => s.fills),
245
+ backgrounds: sum2((s) => s.backgrounds),
246
+ priceLines: sum2((s) => s.priceLines),
247
+ lines: sum2((s) => s.lines),
248
+ boxes: sum2((s) => s.boxes),
249
+ labels: sum2((s) => s.labels),
250
+ polylines: sum2((s) => s.polylines),
251
+ linefills: sum2((s) => s.linefills),
252
+ tables: sum2((s) => s.tables),
253
+ barColors: sum2((s) => s.barColors),
254
+ trades: sum2((s) => s.trades),
255
+ forcedOverlay: sum2((s) => s.forcedOverlay)
255
256
  }
256
257
  };
257
258
  }
@@ -6731,8 +6732,8 @@ var DrawingController = class {
6731
6732
  this.port?.setExternalGhost?.(doc);
6732
6733
  }
6733
6734
  /** Push per-tool shortcut hints (pre-formatted display strings) to the toolbar flyouts. */
6734
- setToolShortcuts(map) {
6735
- this.port?.setToolShortcuts?.(map);
6735
+ setToolShortcuts(map2) {
6736
+ this.port?.setToolShortcuts?.(map2);
6736
6737
  }
6737
6738
  // ── programmatic CRUD (facade-facing) ── each is one undo step
6738
6739
  add(type, init = {}) {
@@ -8456,16 +8457,20 @@ var EngineOrchestrator = class _EngineOrchestrator {
8456
8457
  /**
8457
8458
  * Add a NATIVE (core-computed, no Pine engine) indicator by registered type — a first-class
8458
8459
  * indicator that shares the registry, handle, legend, settings, and lifecycle events. SINGLE
8459
- * INSTANCE per type: a second add returns the existing handle. Unknown type ⇒ a fail-soft handle
8460
- * that never mounts (mirrors addIndicator). Built-in types: `'volume'`, `'vpvr'`.
8460
+ * INSTANCE per type unless the descriptor opts into `multiInstance`: a second add of a
8461
+ * single-instance type returns the existing handle; a multi-instance type gets a fresh
8462
+ * instance every time. Unknown type ⇒ a fail-soft handle that never mounts (mirrors
8463
+ * addIndicator). Built-in single-instance types: `'volume'`, `'vpvr'`.
8461
8464
  */
8462
8465
  addNativeIndicator(type, options = {}) {
8463
- const existing = this.registry.all().find((r) => r.native?.type === type);
8464
- if (existing) return this.handles.get(existing.id) ?? new IndicatorHandleImpl(existing.id, existing.title, this);
8465
8466
  const descriptor = getNativeIndicator(type);
8467
+ if (!descriptor?.multiInstance) {
8468
+ const existing = this.registry.all().find((r) => r.native?.type === type);
8469
+ if (existing) return this.handles.get(existing.id) ?? new IndicatorHandleImpl(existing.id, existing.title, this, void 0, type);
8470
+ }
8466
8471
  const id = this.registry.nextId("native");
8467
8472
  const title = descriptor?.title ?? type;
8468
- const handle = new IndicatorHandleImpl(id, title, this);
8473
+ const handle = new IndicatorHandleImpl(id, title, this, void 0, type);
8469
8474
  this.handles.set(id, handle);
8470
8475
  if (!descriptor) {
8471
8476
  console.warn(`[vela] addNativeIndicator("${type}") \u2014 no native indicator registered for this type.`);
@@ -8477,16 +8482,18 @@ var EngineOrchestrator = class _EngineOrchestrator {
8477
8482
  void this.startNativeIndicator(id, handle);
8478
8483
  return handle;
8479
8484
  }
8480
- /**
8481
- * The catalog of registered native-indicator types with their live state on THIS chart: each
8482
- * entry's `supported` (applies to the current symbol) and `present` (already added — a second
8483
- * add is a no-op). Powers a host "add native indicator" menu that can gate + de-duplicate. Async
8484
- * because a descriptor's `isSupported` may probe the provider for a required capability.
8485
- */
8486
- /** The native types present on the chart — SYNC (registry state; no support probe). */
8485
+ /** The native types present on the chart, one entry per INSTANCE in registry order (a
8486
+ * multi-instance type repeats) — SYNC (registry state; no support probe). */
8487
8487
  presentNativeIndicators() {
8488
8488
  return this.registry.all().map((r) => r.native?.type).filter((t) => !!t);
8489
8489
  }
8490
+ /**
8491
+ * The catalog of registered native-indicator types with their live state on THIS chart: each
8492
+ * entry's `supported` (applies to the current symbol) and `present` (at least one instance
8493
+ * added — a second add of a single-instance type is a no-op). Powers a host "add native
8494
+ * indicator" menu that can gate + de-duplicate. Async because a descriptor's `isSupported` may
8495
+ * probe the provider for a required capability.
8496
+ */
8490
8497
  async availableNativeIndicators() {
8491
8498
  const symbol = this.qualifiedSymbol();
8492
8499
  const present = new Set(this.presentNativeIndicators());
@@ -8496,7 +8503,8 @@ var EngineOrchestrator = class _EngineOrchestrator {
8496
8503
  title: d.title,
8497
8504
  supported: await this.isNativeSupported(d, symbol),
8498
8505
  present: present.has(d.type),
8499
- beta: d.beta
8506
+ beta: d.beta,
8507
+ ...d.multiInstance ? { multiInstance: true } : {}
8500
8508
  }))
8501
8509
  );
8502
8510
  }
@@ -9780,7 +9788,7 @@ function attachForming(closed, forming) {
9780
9788
 
9781
9789
  // src/data/MultiProviderFeed.ts
9782
9790
  function intervalMs(timeframe) {
9783
- const map = {
9791
+ const map2 = {
9784
9792
  "1m": 6e4,
9785
9793
  "3m": 18e4,
9786
9794
  "5m": 3e5,
@@ -9802,7 +9810,7 @@ function intervalMs(timeframe) {
9802
9810
  W: 6048e5,
9803
9811
  M: 2592e6
9804
9812
  };
9805
- return map[timeframe] ?? 36e5;
9813
+ return map2[timeframe] ?? 36e5;
9806
9814
  }
9807
9815
  var MultiProviderFeed = class {
9808
9816
  constructor(store = sharedBarStore) {
@@ -10189,8 +10197,8 @@ var DrawingsControl = class {
10189
10197
  /** Show per-tool shortcut hints in the toolbar flyouts. Values are PRE-FORMATTED
10190
10198
  * display strings (e.g. `'Alt+T'`, `'⌥T'`) — the host owns the keymap and the
10191
10199
  * platform formatting, so hints always match the host's actual bindings. */
10192
- setToolShortcuts(map) {
10193
- if (this.ok("setToolShortcuts")) this.ctrl.setToolShortcuts(map);
10200
+ setToolShortcuts(map2) {
10201
+ if (this.ok("setToolShortcuts")) this.ctrl.setToolShortcuts(map2);
10194
10202
  return this;
10195
10203
  }
10196
10204
  /** Display another chart's in-progress placement as a GHOST at reduced opacity
@@ -10751,19 +10759,19 @@ var Surface = class _Surface {
10751
10759
  }
10752
10760
  setItems(items) {
10753
10761
  this.items = items;
10754
- for (const sub of this.subs.values()) sub.destroy();
10762
+ for (const sub2 of this.subs.values()) sub2.destroy();
10755
10763
  this.subs.clear();
10756
10764
  this.build();
10757
10765
  const api = this.api;
10758
- for (const sub of this.subs.values()) {
10759
- api.setChild(sub.handle.service);
10760
- sub.api.setParent(this.handle.service);
10766
+ for (const sub2 of this.subs.values()) {
10767
+ api.setChild(sub2.handle.service);
10768
+ sub2.api.setParent(this.handle.service);
10761
10769
  }
10762
10770
  this.handle.flush();
10763
10771
  if (this.subs.size > 0) queueMicrotask(() => this.handle.flush());
10764
10772
  }
10765
10773
  destroy() {
10766
- for (const sub of this.subs.values()) sub.destroy();
10774
+ for (const sub2 of this.subs.values()) sub2.destroy();
10767
10775
  this.subs.clear();
10768
10776
  this.handle.stop();
10769
10777
  this.positioner.remove();
@@ -10777,9 +10785,9 @@ var Surface = class _Surface {
10777
10785
  for (const li of this.list.children) {
10778
10786
  const id = li.dataset.veiId;
10779
10787
  if (!id) continue;
10780
- const sub = this.subs.get(id);
10781
- if (sub) {
10782
- vanilla.spreadProps(li, api.getTriggerItemProps(sub.api), this.mid);
10788
+ const sub2 = this.subs.get(id);
10789
+ if (sub2) {
10790
+ vanilla.spreadProps(li, api.getTriggerItemProps(sub2.api), this.mid);
10783
10791
  continue;
10784
10792
  }
10785
10793
  const item = this.byId(id);
@@ -10856,7 +10864,7 @@ var Surface = class _Surface {
10856
10864
  const arrow = iconEl("chevron-right", doc);
10857
10865
  arrow.classList.add("vela-menu-arrow");
10858
10866
  li.appendChild(arrow);
10859
- const sub = new _Surface(doc, {
10867
+ const sub2 = new _Surface(doc, {
10860
10868
  host: this.host,
10861
10869
  placement: "right-start",
10862
10870
  onSelect: this.onSelect,
@@ -10864,8 +10872,8 @@ var Surface = class _Surface {
10864
10872
  checkmarks: this.checkmarks,
10865
10873
  id: `${this.mid}--${item.id}`
10866
10874
  });
10867
- sub.setItems(item.submenu ?? []);
10868
- this.subs.set(item.id, sub);
10875
+ sub2.setItems(item.submenu ?? []);
10876
+ this.subs.set(item.id, sub2);
10869
10877
  }
10870
10878
  this.list.appendChild(li);
10871
10879
  }
@@ -12631,8 +12639,21 @@ function buildColorPicker(color, theme, onChange) {
12631
12639
  };
12632
12640
  const recentRow = document.createElement("div");
12633
12641
  recentRow.style.cssText = "display:flex;align-items:center;gap:5px;flex-wrap:wrap;border-top:1px solid var(--vela-border);padding-top:9px;";
12642
+ const recentSwatches = document.createElement("div");
12643
+ recentSwatches.style.cssText = "display:contents;";
12644
+ const add = document.createElement("label");
12645
+ add.style.cssText = `width:17px;height:17px;border-radius:var(--vela-radius-sm);border:1px dashed var(--vela-border-strong);cursor:pointer;display:flex;align-items:center;justify-content:center;color:${theme.textColor};font:14px ${theme.fontFamily};position:relative;`;
12646
+ add.textContent = "+";
12647
+ const customInput = document.createElement("input");
12648
+ customInput.type = "color";
12649
+ customInput.value = curHex;
12650
+ customInput.style.cssText = "position:absolute;inset:0;opacity:0;cursor:pointer;";
12651
+ customInput.addEventListener("input", () => previewHex(customInput.value));
12652
+ customInput.addEventListener("change", () => pickHex(customInput.value, true));
12653
+ add.appendChild(customInput);
12654
+ recentRow.append(recentSwatches, add);
12634
12655
  const renderRecents = () => {
12635
- recentRow.replaceChildren();
12656
+ recentSwatches.replaceChildren();
12636
12657
  for (const c of recents) {
12637
12658
  const sw = document.createElement("button");
12638
12659
  sw.type = "button";
@@ -12641,18 +12662,8 @@ function buildColorPicker(color, theme, onChange) {
12641
12662
  e.stopPropagation();
12642
12663
  pickHex(c);
12643
12664
  });
12644
- recentRow.appendChild(sw);
12665
+ recentSwatches.appendChild(sw);
12645
12666
  }
12646
- const add = document.createElement("label");
12647
- add.style.cssText = `width:17px;height:17px;border-radius:var(--vela-radius-sm);border:1px dashed var(--vela-border-strong);cursor:pointer;display:flex;align-items:center;justify-content:center;color:${theme.textColor};font:14px ${theme.fontFamily};position:relative;`;
12648
- add.textContent = "+";
12649
- const input = document.createElement("input");
12650
- input.type = "color";
12651
- input.value = curHex;
12652
- input.style.cssText = "position:absolute;inset:0;opacity:0;cursor:pointer;";
12653
- input.addEventListener("input", () => pickHex(input.value, true));
12654
- add.appendChild(input);
12655
- recentRow.appendChild(add);
12656
12667
  };
12657
12668
  const opLabel = document.createElement("div");
12658
12669
  opLabel.textContent = "Opacity";
@@ -12692,14 +12703,18 @@ function buildColorPicker(color, theme, onChange) {
12692
12703
  function emit() {
12693
12704
  onChange(combineColor(curHex, curAlpha));
12694
12705
  }
12695
- function pickHex(hex, custom = false) {
12706
+ function previewHex(hex) {
12696
12707
  curHex = splitColor(hex).hex6;
12697
- addRecent(curHex);
12698
12708
  paintSelection();
12699
12709
  paintOpacity();
12700
- if (custom) renderRecents();
12701
12710
  emit();
12702
12711
  }
12712
+ function pickHex(hex, custom = false) {
12713
+ previewHex(hex);
12714
+ addRecent(curHex);
12715
+ customInput.value = curHex;
12716
+ if (custom) renderRecents();
12717
+ }
12703
12718
  renderRecents();
12704
12719
  paintSelection();
12705
12720
  paintOpacity();
@@ -14605,8 +14620,8 @@ var InputsUI = class {
14605
14620
  lg.style.top = `${top}px`;
14606
14621
  }
14607
14622
  /** Set which panes are collapsed and, for each, the master indicator to keep in its strip. */
14608
- setCollapsedPanes(map) {
14609
- this.paneCollapse = map;
14623
+ setCollapsedPanes(map2) {
14624
+ this.paneCollapse = map2;
14610
14625
  }
14611
14626
  /**
14612
14627
  * Keep the fold toggle in step with the indicator count: with 2+ indicators anywhere
@@ -15569,9 +15584,9 @@ function factoryResetConfig(factory) {
15569
15584
  if (inst.enableKey) defaults2[inst.enableKey] = false;
15570
15585
  addRows(inst.rows);
15571
15586
  }
15572
- for (const sub of section.subsections ?? []) {
15573
- if (sub.enableKey) defaults2[sub.enableKey] = false;
15574
- addRows(sub.rows);
15587
+ for (const sub2 of section.subsections ?? []) {
15588
+ if (sub2.enableKey) defaults2[sub2.enableKey] = false;
15589
+ addRows(sub2.rows);
15575
15590
  }
15576
15591
  if (!section.instances) addRows(section.rows);
15577
15592
  bag[t.id] = defaults2;
@@ -16900,11 +16915,11 @@ function mergeBarColors(indicators) {
16900
16915
  }
16901
16916
  }
16902
16917
  if (!any) return EMPTY_BARCOLORS;
16903
- const map = /* @__PURE__ */ new Map();
16918
+ const map2 = /* @__PURE__ */ new Map();
16904
16919
  for (const m of indicators.values()) {
16905
- if (m.barColors) for (const bc of m.barColors) map.set(bc.time, bc.color);
16920
+ if (m.barColors) for (const bc of m.barColors) map2.set(bc.time, bc.color);
16906
16921
  }
16907
- return map;
16922
+ return map2;
16908
16923
  }
16909
16924
 
16910
16925
  // src/renderers/native/core/ViewportState.ts
@@ -17201,9 +17216,9 @@ function isHorizontalWheel(deltaX, deltaY) {
17201
17216
  function wheelPanRightOffset(rightOffset, deltaX, barSpacing) {
17202
17217
  return rightOffset + deltaX / barSpacing;
17203
17218
  }
17204
- function wheelPanDelta(deltaX, deltaY, shift) {
17219
+ function wheelPanDelta(deltaX, deltaY, shift2) {
17205
17220
  if (isHorizontalWheel(deltaX, deltaY)) return deltaX;
17206
- if (shift) return deltaY;
17221
+ if (shift2) return deltaY;
17207
17222
  return null;
17208
17223
  }
17209
17224
  function effectiveSnapMode(momentaryOverride, sticky) {
@@ -18825,11 +18840,11 @@ function mergeBarColors2(indicators) {
18825
18840
  }
18826
18841
  }
18827
18842
  if (!any) return EMPTY_BARCOLORS2;
18828
- const map = /* @__PURE__ */ new Map();
18843
+ const map2 = /* @__PURE__ */ new Map();
18829
18844
  for (const m of indicators.values()) {
18830
- if (m.barColors) for (const bc of m.barColors) map.set(bc.time, bc.color);
18845
+ if (m.barColors) for (const bc of m.barColors) map2.set(bc.time, bc.color);
18831
18846
  }
18832
- return map;
18847
+ return map2;
18833
18848
  }
18834
18849
 
18835
18850
  // src/renderers/shared/drawing-geometry.ts
@@ -20545,9 +20560,9 @@ function settingsIdCatalog(hostSections) {
20545
20560
  };
20546
20561
  if (section.rows) addRows(section.rows);
20547
20562
  for (const inst of section.instances ?? []) addRows(inst.rows);
20548
- for (const sub of section.subsections ?? []) {
20549
- ids.add(`${scope}.${settingsIdSlug(sub.title)}`);
20550
- addRows(sub.rows);
20563
+ for (const sub2 of section.subsections ?? []) {
20564
+ ids.add(`${scope}.${settingsIdSlug(sub2.title)}`);
20565
+ addRows(sub2.rows);
20551
20566
  }
20552
20567
  }
20553
20568
  for (const hs of hostSections) {
@@ -21106,14 +21121,14 @@ var SettingsDialog = class {
21106
21121
  };
21107
21122
  if (section.rows) seed(section.rows);
21108
21123
  for (const inst of section.instances ?? []) seed(inst.rows);
21109
- for (const sub of section.subsections ?? []) seed(sub.rows);
21124
+ for (const sub2 of section.subsections ?? []) seed(sub2.rows);
21110
21125
  for (const inst of section.instances ?? []) {
21111
21126
  if (!inst.enableKey || inst.enableKey in bag) continue;
21112
21127
  bag[inst.enableKey] = values[inst.enableKey] === true;
21113
21128
  }
21114
- for (const sub of section.subsections ?? []) {
21115
- if (!sub.enableKey || sub.enableKey in bag) continue;
21116
- bag[sub.enableKey] = values[sub.enableKey] === true;
21129
+ for (const sub2 of section.subsections ?? []) {
21130
+ if (!sub2.enableKey || sub2.enableKey in bag) continue;
21131
+ bag[sub2.enableKey] = values[sub2.enableKey] === true;
21117
21132
  }
21118
21133
  const refreshers = [];
21119
21134
  const put = (key, v) => {
@@ -21130,16 +21145,16 @@ var SettingsDialog = class {
21130
21145
  if (section.layout === "grouped") body.append(this.groupedRows(`${typeId}/rows`, rows, bag, put, refreshers));
21131
21146
  else this.flatTypeRows(rows, bag, put, refreshers, body);
21132
21147
  }
21133
- for (const sub of section.subsections ?? []) {
21134
- if (settingsIdHidden(`${scope}.${settingsIdSlug(sub.title)}`, this.hiddenSettings)) continue;
21135
- const rows = filterHiddenRows(sub.rows, scope, this.hiddenSettings);
21148
+ for (const sub2 of section.subsections ?? []) {
21149
+ if (settingsIdHidden(`${scope}.${settingsIdSlug(sub2.title)}`, this.hiddenSettings)) continue;
21150
+ const rows = filterHiddenRows(sub2.rows, scope, this.hiddenSettings);
21136
21151
  if (rows.length === 0) continue;
21137
- const subMarker = this.section(sub.title);
21152
+ const subMarker = this.section(sub2.title);
21138
21153
  subMarker.dataset.sdStyle = typeId;
21139
21154
  subMarker.dataset.sdVisibility = section.visibility ?? "active";
21140
21155
  subMarker.dataset.sdSub = "1";
21141
21156
  body.append(subMarker);
21142
- body.append(this.groupedRows(`${typeId}/${sub.title}`, rows, bag, put, refreshers, sub.enableKey));
21157
+ body.append(this.groupedRows(`${typeId}/${sub2.title}`, rows, bag, put, refreshers, sub2.enableKey));
21143
21158
  }
21144
21159
  for (const r of refreshers) r();
21145
21160
  }
@@ -23247,11 +23262,11 @@ var DrawingInteraction = class {
23247
23262
  /** A press: place the next anchor, or grab a drawing/handle (resolved as click or drag on release).
23248
23263
  * `shift` over a drawing toggles it in/out of the selection (multi-select) instead of dragging;
23249
23264
  * while placing a line tool it locks the segment angle to 45° steps. */
23250
- down(x, y, mode = "off", shift = false) {
23265
+ down(x, y, mode = "off", shift2 = false) {
23251
23266
  const proj = this.deps.projector();
23252
23267
  if (this.state.kind === "placing") {
23253
23268
  if (this.state.draft.placementMode() !== "click") return;
23254
- const pivot = shift ? this.placingPivot(this.state.draft) : null;
23269
+ const pivot = shift2 ? this.placingPivot(this.state.draft) : null;
23255
23270
  const point = pivot ? this.resolveAngleSnapped(pivot, this.state.draft.paneId, x, y) : this.resolve(x, y, this.state.draft.paneId, mode);
23256
23271
  this.state.draft.anchors.push(this.placementAnchor(this.state.draft, point));
23257
23272
  if (this.state.draft.anchors.length >= this.state.need) this.finalize();
@@ -23270,7 +23285,7 @@ var DrawingInteraction = class {
23270
23285
  }
23271
23286
  const hit = this.hitAt(x, y);
23272
23287
  if (hit) {
23273
- if (shift) {
23288
+ if (shift2) {
23274
23289
  this.deps.emit({ kind: "select", ids: [hit.id], additive: true });
23275
23290
  return;
23276
23291
  }
@@ -23280,7 +23295,7 @@ var DrawingInteraction = class {
23280
23295
  }
23281
23296
  /** Pointer move: advance the placing ghost, or live-preview a drag once past the slop.
23282
23297
  * `shift` on a line tool locks the segment angle to 45° steps (placing + handle drags). */
23283
- move(x, y, mode = "off", shift = false) {
23298
+ move(x, y, mode = "off", shift2 = false) {
23284
23299
  if (this.state.kind === "idle") {
23285
23300
  const tool = this.deps.activeTool();
23286
23301
  const had = this.snapAt != null;
@@ -23294,7 +23309,7 @@ var DrawingInteraction = class {
23294
23309
  this.captureFreehand(x, y);
23295
23310
  return;
23296
23311
  }
23297
- const pivot = shift ? this.placingPivot(this.state.draft) : null;
23312
+ const pivot = shift2 ? this.placingPivot(this.state.draft) : null;
23298
23313
  this.state.cursor = pivot ? this.resolveAngleSnapped(pivot, this.state.draft.paneId, x, y) : this.resolve(x, y, this.state.draft.paneId, mode);
23299
23314
  this.deps.changed();
23300
23315
  return;
@@ -23307,7 +23322,7 @@ var DrawingInteraction = class {
23307
23322
  if (this.state.handle >= 0) {
23308
23323
  const a = d.anchors[this.state.handle];
23309
23324
  if (a) {
23310
- const pivot = shift && ANGLE_SNAP_TYPES.has(d.type) && d.anchors.length === 2 ? d.anchors[1 - this.state.handle] : void 0;
23325
+ const pivot = shift2 && ANGLE_SNAP_TYPES.has(d.type) && d.anchors.length === 2 ? d.anchors[1 - this.state.handle] : void 0;
23311
23326
  const point = pivot ? this.resolveAngleSnapped(pivot, d.paneId, x, y) : this.resolve(x, y, d.paneId, mode);
23312
23327
  applyFree(a, point, d.anchorSchema().slots[this.state.handle]?.free ?? "both");
23313
23328
  }
@@ -24005,21 +24020,21 @@ var DrawingSettingsPopup = class {
24005
24020
  bar.appendChild(this.colorButton("Lower fill", BUCKET_ICON, reg.lowerFill, (v) => actions.patch({ "reg.lowerFill": v })));
24006
24021
  bar.appendChild(this.toggle("Show R\xB2", R2_ICON, reg.showR2, (v) => actions.patch({ "reg.showR2": v })));
24007
24022
  }
24008
- const vwap = drawing.vwap;
24009
- if (paths.has("vwap.midColor") && vwap) {
24023
+ const vwap2 = drawing.vwap;
24024
+ if (paths.has("vwap.midColor") && vwap2) {
24010
24025
  const styles = LINE_STYLE_OPTIONS.map((o) => o.value);
24011
24026
  const MULTS = [0.5, 1, 1.5, 2, 2.5, 3, 4, 5];
24012
- bar.appendChild(this.colorButton("VWAP color", BRUSH_ICON, vwap.midColor, (v) => actions.patch({ "vwap.midColor": v })));
24013
- bar.appendChild(this.dropdown("VWAP style", styles, vwap.midStyle, (s) => lineIcon(2, s), (v) => actions.patch({ "vwap.midStyle": v }), { label: styleLabel2 }));
24027
+ bar.appendChild(this.colorButton("VWAP color", BRUSH_ICON, vwap2.midColor, (v) => actions.patch({ "vwap.midColor": v })));
24028
+ bar.appendChild(this.dropdown("VWAP style", styles, vwap2.midStyle, (s) => lineIcon(2, s), (v) => actions.patch({ "vwap.midStyle": v }), { label: styleLabel2 }));
24014
24029
  bar.appendChild(
24015
- this.dropdown("Band multiplier", MULTS, vwap.multiplier, () => BANDS_ICON, (v) => actions.patch({ "vwap.multiplier": v }), {
24030
+ this.dropdown("Band multiplier", MULTS, vwap2.multiplier, () => BANDS_ICON, (v) => actions.patch({ "vwap.multiplier": v }), {
24016
24031
  label: (v) => `${v}\u03C3`,
24017
24032
  labelInTrigger: true
24018
24033
  })
24019
24034
  );
24020
- bar.appendChild(this.colorButton("Upper band color", BRUSH_ICON, vwap.upperColor, (v) => actions.patch({ "vwap.upperColor": v })));
24021
- bar.appendChild(this.colorButton("Lower band color", BRUSH_ICON, vwap.lowerColor, (v) => actions.patch({ "vwap.lowerColor": v })));
24022
- bar.appendChild(this.colorButton("Band fill", BUCKET_ICON, vwap.bandFill, (v) => actions.patch({ "vwap.bandFill": v })));
24035
+ bar.appendChild(this.colorButton("Upper band color", BRUSH_ICON, vwap2.upperColor, (v) => actions.patch({ "vwap.upperColor": v })));
24036
+ bar.appendChild(this.colorButton("Lower band color", BRUSH_ICON, vwap2.lowerColor, (v) => actions.patch({ "vwap.lowerColor": v })));
24037
+ bar.appendChild(this.colorButton("Band fill", BUCKET_ICON, vwap2.bandFill, (v) => actions.patch({ "vwap.bandFill": v })));
24023
24038
  }
24024
24039
  if (paths.has("maxCurvature")) {
24025
24040
  const cur = drawing.maxCurvature ?? 24;
@@ -24765,8 +24780,8 @@ var DrawingToolbar = class {
24765
24780
  }
24766
24781
  /** Per-tool shortcut hints (pre-formatted display strings, e.g. `'Alt+T'`) shown at the
24767
24782
  * right edge of the flyout rows, beside the favorite star. */
24768
- setShortcuts(map) {
24769
- this.shortcuts = new Map(Object.entries(map));
24783
+ setShortcuts(map2) {
24784
+ this.shortcuts = new Map(Object.entries(map2));
24770
24785
  }
24771
24786
  /** Paint one star for the current favorite state (filled + gold, or outline). */
24772
24787
  paintStar(el, type) {
@@ -25668,8 +25683,8 @@ var UserDrawingController = class {
25668
25683
  this.toolbar.setFavorites(types);
25669
25684
  }
25670
25685
  /** Core push: per-tool shortcut hints (display strings) shown in the toolbar flyouts. */
25671
- setToolShortcuts(map) {
25672
- this.toolbar.setShortcuts(map);
25686
+ setToolShortcuts(map2) {
25687
+ this.toolbar.setShortcuts(map2);
25673
25688
  }
25674
25689
  /** Core push: set the sticky magnet mode. Applies to the renderer + reflects on the
25675
25690
  * in-chart toolbar WITHOUT notifying back (the caller already holds the value). */
@@ -25787,7 +25802,7 @@ var UserDrawingController = class {
25787
25802
  this.render();
25788
25803
  }
25789
25804
  }
25790
- pointerDown(x, y, snap = "off", shift = false) {
25805
+ pointerDown(x, y, snap = "off", shift2 = false) {
25791
25806
  if (this.eraserMode) {
25792
25807
  this.erasing = true;
25793
25808
  this.deleteAt(x, y);
@@ -25807,9 +25822,9 @@ var UserDrawingController = class {
25807
25822
  return;
25808
25823
  }
25809
25824
  }
25810
- this.interaction.down(x, y, snap, shift);
25825
+ this.interaction.down(x, y, snap, shift2);
25811
25826
  }
25812
- pointerMove(x, y, snap = "off", shift = false) {
25827
+ pointerMove(x, y, snap = "off", shift2 = false) {
25813
25828
  if (this.eraserMode) {
25814
25829
  if (this.erasing) this.deleteAt(x, y);
25815
25830
  return;
@@ -25820,7 +25835,7 @@ var UserDrawingController = class {
25820
25835
  this.render();
25821
25836
  return;
25822
25837
  }
25823
- this.interaction.move(x, y, snap, shift);
25838
+ this.interaction.move(x, y, snap, shift2);
25824
25839
  this.updateHover(x, y);
25825
25840
  }
25826
25841
  /** Track which drawing is hovered so its handles appear on hover (and only then). */
@@ -26497,14 +26512,14 @@ function layoutTable(ctx, t, args) {
26497
26512
  }
26498
26513
  for (const { box, w: w2, h: h2 } of spanning) {
26499
26514
  if (box.cs > 1) {
26500
- let sum = 0;
26501
- for (let c = box.c; c < box.c + box.cs; c += 1) sum += colW[c];
26502
- if (w2 > sum) for (let c = box.c; c < box.c + box.cs; c += 1) colW[c] += (w2 - sum) / box.cs;
26515
+ let sum2 = 0;
26516
+ for (let c = box.c; c < box.c + box.cs; c += 1) sum2 += colW[c];
26517
+ if (w2 > sum2) for (let c = box.c; c < box.c + box.cs; c += 1) colW[c] += (w2 - sum2) / box.cs;
26503
26518
  }
26504
26519
  if (box.rs > 1) {
26505
- let sum = 0;
26506
- for (let r = box.r; r < box.r + box.rs; r += 1) sum += rowH[r];
26507
- if (h2 > sum) for (let r = box.r; r < box.r + box.rs; r += 1) rowH[r] += (h2 - sum) / box.rs;
26520
+ let sum2 = 0;
26521
+ for (let r = box.r; r < box.r + box.rs; r += 1) sum2 += rowH[r];
26522
+ if (h2 > sum2) for (let r = box.r; r < box.r + box.rs; r += 1) rowH[r] += (h2 - sum2) / box.rs;
26508
26523
  }
26509
26524
  }
26510
26525
  let w = 0;
@@ -27227,24 +27242,24 @@ function rasterizeOverlay(ctx, root, frame) {
27227
27242
  drawSvgShapes(el, alpha);
27228
27243
  continue;
27229
27244
  }
27230
- const num3 = (name) => parseFloat(el.getAttribute(name) ?? "0") || 0;
27245
+ const num4 = (name) => parseFloat(el.getAttribute(name) ?? "0") || 0;
27231
27246
  let path = null;
27232
27247
  if (tag === "path") {
27233
27248
  const d = el.getAttribute("d");
27234
27249
  if (d) path = new Path2D(d);
27235
27250
  } else if (tag === "circle") {
27236
27251
  path = new Path2D();
27237
- path.arc(num3("cx"), num3("cy"), num3("r"), 0, Math.PI * 2);
27252
+ path.arc(num4("cx"), num4("cy"), num4("r"), 0, Math.PI * 2);
27238
27253
  } else if (tag === "ellipse") {
27239
27254
  path = new Path2D();
27240
- path.ellipse(num3("cx"), num3("cy"), num3("rx"), num3("ry"), 0, 0, Math.PI * 2);
27255
+ path.ellipse(num4("cx"), num4("cy"), num4("rx"), num4("ry"), 0, 0, Math.PI * 2);
27241
27256
  } else if (tag === "rect") {
27242
27257
  path = new Path2D();
27243
- path.rect(num3("x"), num3("y"), num3("width"), num3("height"));
27258
+ path.rect(num4("x"), num4("y"), num4("width"), num4("height"));
27244
27259
  } else if (tag === "line") {
27245
27260
  path = new Path2D();
27246
- path.moveTo(num3("x1"), num3("y1"));
27247
- path.lineTo(num3("x2"), num3("y2"));
27261
+ path.moveTo(num4("x1"), num4("y1"));
27262
+ path.lineTo(num4("x2"), num4("y2"));
27248
27263
  } else if (tag === "polyline" || tag === "polygon") {
27249
27264
  const pts = (el.getAttribute("points") ?? "").trim().split(/[\s,]+/).map(Number);
27250
27265
  if (pts.length < 4 || pts.some(Number.isNaN)) continue;
@@ -28661,8 +28676,8 @@ var NativeRenderer = class {
28661
28676
  drawingsDeleteAt: (x, y) => this.userDrawings?.deleteAt(x, y) ?? false,
28662
28677
  drawingsCancelPlacement: () => this.userDrawings?.cancelPlacement() ?? false,
28663
28678
  drawingsSnapMode: () => this.snapMode,
28664
- drawingsPointerDown: (x, y, snap, shift) => this.userDrawings?.pointerDown(x, y, snap, shift),
28665
- drawingsPointerMove: (x, y, snap, shift) => this.userDrawings?.pointerMove(x, y, snap, shift),
28679
+ drawingsPointerDown: (x, y, snap, shift2) => this.userDrawings?.pointerDown(x, y, snap, shift2),
28680
+ drawingsPointerMove: (x, y, snap, shift2) => this.userDrawings?.pointerMove(x, y, snap, shift2),
28666
28681
  drawingsPointerUp: (x, y, snap) => this.userDrawings?.pointerUp(x, y, snap),
28667
28682
  drawingsCursor: (x, y) => this.userDrawings?.cursorAt(x, y) ?? null,
28668
28683
  drawingsDblClick: (x, y) => this.userDrawings?.dblClick(x, y) ?? false,
@@ -28979,18 +28994,18 @@ var NativeRenderer = class {
28979
28994
  for (const model of this.scene.indicators.values()) this.refreshAnchorOffset(model);
28980
28995
  if (opts?.preserveView === true && prevHeadTime != null && this.bars.length > 0) {
28981
28996
  const newHeadTime = this.bars[0].time;
28982
- let shift = 0;
28997
+ let shift2 = 0;
28983
28998
  if (newHeadTime !== prevHeadTime) {
28984
28999
  const prepended = this.bars.findIndex((b) => b.time === prevHeadTime);
28985
- if (prepended > 0) shift = prepended;
29000
+ if (prepended > 0) shift2 = prepended;
28986
29001
  else {
28987
29002
  const trimmed = prevBars.findIndex((b) => b.time === newHeadTime);
28988
- if (trimmed > 0) shift = -trimmed;
29003
+ if (trimmed > 0) shift2 = -trimmed;
28989
29004
  }
28990
29005
  }
28991
- if (shift !== 0) {
28992
- this.zoomAnchorLogical += shift;
28993
- if (this.hoverLogical != null) this.hoverLogical += shift;
29006
+ if (shift2 !== 0) {
29007
+ this.zoomAnchorLogical += shift2;
29008
+ if (this.hoverLogical != null) this.hoverLogical += shift2;
28994
29009
  }
28995
29010
  }
28996
29011
  const skipFit = opts?.preserveView === true && this.didInitialFit;
@@ -30596,15 +30611,15 @@ var NativeRenderer = class {
30596
30611
  /** For each collapsed pane, the master (master-scale) indicator id whose legend row stays in the
30597
30612
  * strip — merged own-scale indicators are hidden while collapsed. */
30598
30613
  collapsedMasterMap() {
30599
- const map = /* @__PURE__ */ new Map();
30614
+ const map2 = /* @__PURE__ */ new Map();
30600
30615
  for (const pane of this.scene.panes.values()) {
30601
30616
  if (!pane.collapsed) continue;
30602
30617
  const models = this.scene.orderedIndicatorsForPane(pane.id);
30603
30618
  const merged = new Set(this.scene.ownScaleIndicatorsForPane(pane.id).map((m) => m.id));
30604
30619
  const master = models.find((m) => !merged.has(m.id)) ?? models[0];
30605
- map.set(pane.id, master?.id ?? null);
30620
+ map2.set(pane.id, master?.id ?? null);
30606
30621
  }
30607
- return map;
30622
+ return map2;
30608
30623
  }
30609
30624
  /** Reserve `px` of left gutter for the docked drawings toolbar (0 releases it) + re-lay-out the plot. */
30610
30625
  setToolbarGutter(px) {
@@ -31000,220 +31015,2539 @@ function registerVpvr() {
31000
31015
  registerNativeIndicator(vpvrDescriptor);
31001
31016
  }
31002
31017
 
31003
- // src/Vela.ts
31004
- var Vela = class {
31005
- constructor(container, options = {}, deps = {}) {
31006
- registerBuiltinChartTypes();
31007
- registerVolume();
31008
- registerVpvr();
31009
- const element = resolveElement(container);
31010
- const theme = resolveTheme(options.theme);
31011
- let animZoom = true;
31012
- let animPan = true;
31013
- if (typeof options.animations === "boolean") {
31014
- animZoom = options.animations;
31015
- animPan = options.animations;
31016
- } else if (options.animations) {
31017
- animZoom = options.animations.zoom ?? true;
31018
- animPan = options.animations.pan ?? true;
31019
- }
31020
- const display = {
31021
- currentPriceLine: options.currentPriceLine ?? true,
31022
- logScale: options.logScale ?? false,
31023
- nativeBackend: options.nativeBackend ?? "auto",
31024
- animZoom,
31025
- animPan,
31026
- glow: options.glow ?? 0,
31027
- upColor: options.upColor ?? BULLISH,
31028
- downColor: options.downColor ?? BEARISH,
31029
- priceStyle: options.priceStyle ?? "candles"
31030
- };
31031
- const RendererClass = options.renderer ?? NativeRenderer;
31032
- if (typeof RendererClass !== "function") {
31033
- throw new Error(
31034
- "[vela] options.renderer must be a renderer class, e.g. import { LwcRenderer } from 'vela/renderers/lwc'. The 'lwc'/'native' string options were removed."
31035
- );
31036
- }
31037
- const renderer = deps.renderer ?? new RendererClass(display);
31038
- const engines = deps.engines ?? [];
31039
- const feed = deps.dataFeed ?? new MultiProviderFeed();
31040
- const config = {
31041
- market: {
31042
- symbol: options.symbol,
31043
- timeframe: options.timeframe,
31044
- bars: options.bars,
31045
- session: options.session,
31046
- visibleRange: options.visibleRange,
31047
- data: options.data
31048
- },
31049
- live: options.live ?? false,
31050
- theme,
31051
- defaultLanguage: options.defaultLanguage ?? engines[0]?.language ?? "pine",
31052
- drawings: options.drawings,
31053
- volume: options.volume ?? true
31054
- // the volume indicator is on by default
31055
- };
31056
- this.rendererControl = new RendererControl(renderer);
31057
- if (options.settings) this.rendererControl.setSettingsVisibility(options.settings);
31058
- this.dataControl = new DataControl(feed);
31059
- this.orchestrator = new EngineOrchestrator(element, renderer, feed, engines, config, this.dataControl);
31060
- const defaults2 = rendererDefaults();
31061
- if (Object.keys(defaults2).length > 0) this.rendererControl.set(defaults2);
31062
- this.panesControl = new PanesControl(this.orchestrator);
31063
- this.drawingsControl = new DrawingsControl(this.orchestrator.drawings);
31018
+ // src/core/model/identity.ts
31019
+ function stableSeriesId(parts) {
31020
+ const normTitle = parts.title.trim().toLowerCase().replace(/\s+/g, "-");
31021
+ return `${parts.instanceId}:${parts.kind}:${normTitle}#${parts.ordinal}`;
31022
+ }
31023
+
31024
+ // src/core/native-indicators/classics/define.ts
31025
+ function num3(inputs, key, fallback) {
31026
+ const v = inputs[key];
31027
+ return typeof v === "number" && Number.isFinite(v) ? v : fallback;
31028
+ }
31029
+ function str3(inputs, key, fallback) {
31030
+ const v = inputs[key];
31031
+ return typeof v === "string" && v !== "" ? v : fallback;
31032
+ }
31033
+ function toPoints(bars, values, colors) {
31034
+ const pts = new Array(bars.length);
31035
+ for (let i = 0; i < bars.length; i++) {
31036
+ const v = values[i];
31037
+ const p = { time: bars[i].time, value: v != null && Number.isFinite(v) ? v : null };
31038
+ const c = colors?.[i];
31039
+ if (c != null) p.color = c;
31040
+ pts[i] = p;
31064
31041
  }
31065
- /**
31066
- * Register a scripting engine so `addIndicator({ language })` can run that
31067
- * language. Vela ships NO engine — install the one you need (Pine Script:
31068
- * `@luxalgo/vela-pinets`) and register it, e.g.
31069
- * `chart.registerEngine('pine', new PineEngine())`; without one the chart
31070
- * displays candles, drawings and native indicators only. Re-registering a
31071
- * language replaces it.
31072
- */
31073
- registerEngine(language, engine) {
31074
- this.orchestrator.registerEngine(language, engine);
31075
- return this;
31042
+ return pts;
31043
+ }
31044
+ var ClassicIndicator = class {
31045
+ constructor(spec) {
31046
+ this.spec = spec;
31047
+ this.inputs = {};
31076
31048
  }
31077
- /** Run an indicator script on the chart's market data and render it. */
31078
- addIndicator(source, options) {
31079
- return this.orchestrator.addIndicator(source, options);
31049
+ start(ctx, inputs) {
31050
+ this.ctx = ctx;
31051
+ this.inputs = inputs;
31052
+ this.recompute();
31080
31053
  }
31081
- /**
31082
- * Add a built-in NATIVE indicator (core-computed, no scripting engine) by registered `type` —
31083
- * e.g. `'vpvr'`. It becomes a first-class indicator (legend row, settings, hide, remove,
31084
- * events) and is single-instance per type (a second call returns the existing handle). Returns
31085
- * a fail-soft handle for an unregistered type. Native renderer only.
31086
- */
31087
- addNativeIndicator(type, options) {
31088
- return this.orchestrator.addNativeIndicator(type, options);
31054
+ onBars() {
31055
+ this.recompute();
31089
31056
  }
31090
- /**
31091
- * The catalog of built-in native indicators with their live state on this chart — each entry's
31092
- * `type`, `title`, whether it `supported`s the current symbol, and whether it's already `present`
31093
- * (native indicators are single-instance per type, so a second `addNativeIndicator` is a no-op).
31094
- * Lets a host "add indicator" UI list them, gate unsupported ones, and avoid duplicates. Async
31095
- * because support may probe the provider (a type may need data the symbol lacks).
31096
- */
31097
- availableNativeIndicators() {
31098
- return this.orchestrator.availableNativeIndicators();
31057
+ onViewport() {
31099
31058
  }
31100
- /**
31101
- * The native-indicator types PRESENT on the chart right now — the synchronous slice of
31102
- * {@link availableNativeIndicators} (only support probing is async; presence never is).
31103
- * Persistence snapshots read this: an unload-time flush must see an add/remove that
31104
- * happened microseconds ago, which an async catalog mirror cannot guarantee.
31105
- */
31106
- presentNativeIndicators() {
31107
- return this.orchestrator.presentNativeIndicators();
31059
+ setInputs(inputs) {
31060
+ this.inputs = inputs;
31061
+ this.recompute();
31108
31062
  }
31109
- /** Live handles of every indicator currently on the chart (script + native) — drive
31110
- * host panels (object trees, indicator lists) with per-id visibility/removal. */
31111
- indicators() {
31112
- return this.orchestrator.listIndicators();
31063
+ /** Nothing to free — the compute reads the live bar accessor on demand. */
31064
+ suspend() {
31113
31065
  }
31114
- /**
31115
- * Execute an indicator script and INJECT it only if the run succeeds — the seam for
31116
- * host editors and consoles. Resolves `{ ok: true, handle }` after the first
31117
- * successful evaluation, or `{ ok: false, error }` after a compile/runtime failure —
31118
- * in which case the failed indicator is removed again (no dead legend row).
31119
- * Never rejects.
31120
- */
31121
- /**
31122
- * Execute a script and resolve its FIRST computed run — the data-out door for host
31123
- * editors, consoles and dashboards. The script is injected only if it runs (a failure
31124
- * removes it again, leaving no dead legend row), and the result carries the run itself
31125
- * plus the controls for what it put on the chart: `onUpdate` to follow later runs,
31126
- * `remove` to take it off. Never rejects.
31127
- *
31128
- * `runScript` is `runIndicator` with the run as its payload rather than a handle to go
31129
- * fetch from — the same relationship `script:run` has to `context:changed`.
31130
- */
31131
- runScript(source, options) {
31132
- const handle = this.addIndicator(source, options);
31133
- const updates = /* @__PURE__ */ new Set();
31134
- const drop = () => {
31135
- try {
31136
- handle.remove();
31137
- } catch {
31138
- }
31139
- };
31140
- return new Promise((resolve) => {
31141
- let settled = false;
31142
- const offRun = this.on("script:run", (run) => {
31143
- if (run.id !== handle.id) return;
31144
- if (settled) {
31145
- for (const handler of updates) handler(run);
31146
- return;
31147
- }
31148
- settled = true;
31149
- offError();
31150
- resolve({
31151
- ok: true,
31152
- run,
31153
- error: null,
31154
- onUpdate: (handler) => {
31155
- updates.add(handler);
31156
- return () => updates.delete(handler);
31157
- },
31158
- remove: () => {
31159
- offRun();
31160
- updates.clear();
31161
- drop();
31162
- }
31163
- });
31164
- });
31165
- const offError = handle.on("error", ({ error }) => {
31166
- if (settled) return;
31167
- settled = true;
31168
- offRun();
31169
- offError();
31170
- drop();
31171
- resolve({ ok: false, run: null, error, onUpdate: () => () => void 0, remove: () => void 0 });
31172
- });
31173
- });
31066
+ resume() {
31067
+ this.recompute();
31174
31068
  }
31175
- runIndicator(source, options) {
31176
- const handle = this.addIndicator(source, options);
31177
- return new Promise((resolve) => {
31178
- const offReady = handle.on("ready", () => {
31179
- offReady();
31180
- offError();
31181
- resolve({ ok: true, handle, error: null, context: null });
31069
+ stop() {
31070
+ }
31071
+ recompute() {
31072
+ const bars = this.ctx.bars();
31073
+ const out = this.spec.compute(bars, this.inputs);
31074
+ const instanceId = this.spec.type;
31075
+ const series = [];
31076
+ const idsByKey = /* @__PURE__ */ new Map();
31077
+ out.plots.forEach((plot, ordinal) => {
31078
+ const kind = plot.kind ?? "line";
31079
+ const id = stableSeriesId({ instanceId, kind, title: plot.title, ordinal });
31080
+ idsByKey.set(plot.key, id);
31081
+ series.push({
31082
+ id,
31083
+ title: plot.title,
31084
+ paneId: "",
31085
+ // stamped by the orchestrator
31086
+ kind,
31087
+ points: toPoints(bars, plot.values, plot.colors),
31088
+ style: {
31089
+ color: plot.color,
31090
+ width: plot.width ?? 1,
31091
+ lineStyle: plot.lineStyle ?? "solid",
31092
+ ...plot.base != null ? { base: plot.base } : {}
31093
+ },
31094
+ ...plot.overlay ? { overlay: true } : {}
31182
31095
  });
31183
- const offError = handle.on("error", ({ error }) => {
31184
- offReady();
31185
- offError();
31186
- void handle.context().catch(() => null).then((context) => {
31187
- try {
31188
- handle.remove();
31189
- } catch {
31190
- }
31191
- resolve({ ok: false, handle: null, error, context });
31192
- });
31096
+ });
31097
+ if (out.markers && out.markers.length > 0) {
31098
+ series.push({
31099
+ id: stableSeriesId({ instanceId, kind: "markers", title: "markers", ordinal: 0 }),
31100
+ title: this.spec.shortTitle ?? this.spec.title,
31101
+ paneId: "",
31102
+ kind: "markers",
31103
+ markers: out.markers
31193
31104
  });
31105
+ }
31106
+ const fills = (out.bands ?? []).flatMap((band, ordinal) => {
31107
+ const from = idsByKey.get(band.from);
31108
+ const to = idsByKey.get(band.to);
31109
+ if (!from || !to) return [];
31110
+ return [
31111
+ {
31112
+ id: stableSeriesId({ instanceId, kind: "fill", title: band.key, ordinal }),
31113
+ paneId: "",
31114
+ fromSeriesId: from,
31115
+ toSeriesId: to,
31116
+ color: band.color
31117
+ }
31118
+ ];
31194
31119
  });
31120
+ const priceLines = (out.levels ?? []).map((level, ordinal) => ({
31121
+ id: stableSeriesId({ instanceId, kind: "hline", title: level.key, ordinal }),
31122
+ paneId: "",
31123
+ price: level.price,
31124
+ ...level.color != null ? { color: level.color } : {},
31125
+ lineStyle: level.lineStyle ?? "dashed",
31126
+ width: 1,
31127
+ ...level.title != null ? { title: level.title } : {}
31128
+ }));
31129
+ const polylines = (out.polylines ?? []).map((poly, ordinal) => {
31130
+ const { key, ...rest } = poly;
31131
+ return {
31132
+ id: stableSeriesId({ instanceId, kind: "polyline", title: key, ordinal }),
31133
+ paneId: "",
31134
+ ...rest
31135
+ };
31136
+ });
31137
+ this.ctx.emit({ series, fills, priceLines, polylines });
31138
+ this.ctx.setStatus("idle");
31195
31139
  }
31196
- /**
31197
- * Switch the chart's market IN PLACE — symbol, provider, timeframe, depth, or offline
31198
- * data — WITHOUT destroying the chart: indicators re-execute over the new bars, native
31199
- * indicators restart, and panes, user drawings, renderer config and event
31200
- * subscriptions all survive. Resolves once the new market's history is painted (a
31201
- * deep backfill continues behind it — await {@link historyComplete}); a call
31202
- * superseded by a newer `setMarket` resolves silently. Emits `market:changed`
31203
- * (with the previous identity) when the market identity changed.
31204
- */
31205
- setMarket(next) {
31206
- return this.orchestrator.setMarket(next);
31207
- }
31208
- /** The current market identity — the read counterpart of {@link setMarket}. A snapshot
31209
- * of the REQUESTED market: it reflects an in-flight switch immediately (before the
31210
- * new bars land). Listen to `market:changed` for committed identity changes. */
31211
- get market() {
31212
- return this.orchestrator.marketSnapshot();
31140
+ };
31141
+ function classicDescriptor(spec) {
31142
+ return {
31143
+ type: spec.type,
31144
+ title: spec.title,
31145
+ ...spec.shortTitle != null ? { shortTitle: spec.shortTitle } : {},
31146
+ paneHint: spec.overlay ? "price" : "new",
31147
+ overlay: spec.overlay,
31148
+ // Studies stack (a 20 and a 200 moving average side by side) — unlike the layer-backed natives.
31149
+ multiInstance: true,
31150
+ inputsSchema: () => spec.inputs,
31151
+ defaultInputs: () => Object.fromEntries(spec.inputs.map((i) => [i.key, i.defval])),
31152
+ create: () => new ClassicIndicator(spec)
31153
+ };
31154
+ }
31155
+
31156
+ // src/core/native-indicators/classics/math.ts
31157
+ var SOURCES2 = ["Close", "Open", "High", "Low", "HL2", "HLC3", "OHLC4", "HLCC4"];
31158
+ function sourceValues(bars, source) {
31159
+ const out = new Array(bars.length);
31160
+ for (let i = 0; i < bars.length; i++) {
31161
+ const b = bars[i];
31162
+ switch (source) {
31163
+ case "Open":
31164
+ out[i] = b.open;
31165
+ break;
31166
+ case "High":
31167
+ out[i] = b.high;
31168
+ break;
31169
+ case "Low":
31170
+ out[i] = b.low;
31171
+ break;
31172
+ case "HL2":
31173
+ out[i] = (b.high + b.low) / 2;
31174
+ break;
31175
+ case "HLC3":
31176
+ out[i] = (b.high + b.low + b.close) / 3;
31177
+ break;
31178
+ case "OHLC4":
31179
+ out[i] = (b.open + b.high + b.low + b.close) / 4;
31180
+ break;
31181
+ case "HLCC4":
31182
+ out[i] = (b.high + b.low + b.close + b.close) / 4;
31183
+ break;
31184
+ default:
31185
+ out[i] = b.close;
31186
+ }
31213
31187
  }
31214
- /** Resolves once the chart is painted and interactive. For a symbol-backed chart this
31215
- * awaits a provider being registered that resolves the symbol (the parked load). On a
31216
- * ranged feed the first paint is a small recent head (~200 bars) and the rest of the
31188
+ return out;
31189
+ }
31190
+ function volumes(bars) {
31191
+ return bars.map((b) => b.volume != null && Number.isFinite(b.volume) ? b.volume : Number.NaN);
31192
+ }
31193
+ function highs(bars) {
31194
+ return bars.map((b) => b.high);
31195
+ }
31196
+ function lows(bars) {
31197
+ return bars.map((b) => b.low);
31198
+ }
31199
+ function closes(bars) {
31200
+ return bars.map((b) => b.close);
31201
+ }
31202
+ function opens(bars) {
31203
+ return bars.map((b) => b.open);
31204
+ }
31205
+ function zip(a, b, f) {
31206
+ const out = new Array(a.length);
31207
+ for (let i = 0; i < a.length; i++) {
31208
+ const x = a[i];
31209
+ const y = b[i];
31210
+ out[i] = Number.isFinite(x) && Number.isFinite(y) ? f(x, y) : Number.NaN;
31211
+ }
31212
+ return out;
31213
+ }
31214
+ function map(a, f) {
31215
+ const out = new Array(a.length);
31216
+ for (let i = 0; i < a.length; i++) {
31217
+ const x = a[i];
31218
+ out[i] = Number.isFinite(x) ? f(x) : Number.NaN;
31219
+ }
31220
+ return out;
31221
+ }
31222
+ function sub(a, b) {
31223
+ return zip(a, b, (x, y) => x - y);
31224
+ }
31225
+ function windowed(v, len, fold) {
31226
+ const out = new Array(v.length).fill(Number.NaN);
31227
+ if (len <= 0) return out;
31228
+ let run = 0;
31229
+ for (let i = 0; i < v.length; i++) {
31230
+ run = Number.isFinite(v[i]) ? run + 1 : 0;
31231
+ if (run >= len) out[i] = fold(v, i - len + 1, i);
31232
+ }
31233
+ return out;
31234
+ }
31235
+ function sum(v, len) {
31236
+ return windowed(v, len, (w, from, to) => {
31237
+ let s = 0;
31238
+ for (let k = from; k <= to; k++) s += w[k];
31239
+ return s;
31240
+ });
31241
+ }
31242
+ function sma(v, len) {
31243
+ return windowed(v, len, (w, from, to) => {
31244
+ let s = 0;
31245
+ for (let k = from; k <= to; k++) s += w[k];
31246
+ return s / len;
31247
+ });
31248
+ }
31249
+ function wma(v, len) {
31250
+ const norm = len * (len + 1) / 2;
31251
+ return windowed(v, len, (w, from, to) => {
31252
+ let s = 0;
31253
+ for (let k = from; k <= to; k++) s += w[k] * (k - from + 1);
31254
+ return s / norm;
31255
+ });
31256
+ }
31257
+ function vwma(src, vol, len) {
31258
+ const pv = zip(src, vol, (p, q) => p * q);
31259
+ return zip(sum(pv, len), sum(vol, len), (a, b) => b > 0 ? a / b : Number.NaN);
31260
+ }
31261
+ function stdev(v, len) {
31262
+ return windowed(v, len, (w, from, to) => {
31263
+ let s = 0;
31264
+ for (let k = from; k <= to; k++) s += w[k];
31265
+ const mean = s / len;
31266
+ let sq = 0;
31267
+ for (let k = from; k <= to; k++) {
31268
+ const d = w[k] - mean;
31269
+ sq += d * d;
31270
+ }
31271
+ return Math.sqrt(sq / len);
31272
+ });
31273
+ }
31274
+ function highest(v, len) {
31275
+ return windowed(v, len, (w, from, to) => {
31276
+ let m = -Infinity;
31277
+ for (let k = from; k <= to; k++) if (w[k] > m) m = w[k];
31278
+ return m;
31279
+ });
31280
+ }
31281
+ function lowest(v, len) {
31282
+ return windowed(v, len, (w, from, to) => {
31283
+ let m = Infinity;
31284
+ for (let k = from; k <= to; k++) if (w[k] < m) m = w[k];
31285
+ return m;
31286
+ });
31287
+ }
31288
+ function barsSinceHighest(v, len) {
31289
+ return windowed(v, len, (w, from, to) => {
31290
+ let m = -Infinity;
31291
+ let at = to;
31292
+ for (let k = from; k <= to; k++) {
31293
+ if (w[k] >= m) {
31294
+ m = w[k];
31295
+ at = k;
31296
+ }
31297
+ }
31298
+ return to - at;
31299
+ });
31300
+ }
31301
+ function barsSinceLowest(v, len) {
31302
+ return windowed(v, len, (w, from, to) => {
31303
+ let m = Infinity;
31304
+ let at = to;
31305
+ for (let k = from; k <= to; k++) {
31306
+ if (w[k] <= m) {
31307
+ m = w[k];
31308
+ at = k;
31309
+ }
31310
+ }
31311
+ return to - at;
31312
+ });
31313
+ }
31314
+ function recursive(v, len, alpha) {
31315
+ const out = new Array(v.length).fill(Number.NaN);
31316
+ let run = 0;
31317
+ let prev2 = Number.NaN;
31318
+ for (let i = 0; i < v.length; i++) {
31319
+ const x = v[i];
31320
+ if (!Number.isFinite(x)) {
31321
+ run = 0;
31322
+ prev2 = Number.NaN;
31323
+ continue;
31324
+ }
31325
+ run += 1;
31326
+ if (Number.isFinite(prev2)) {
31327
+ prev2 = alpha * x + (1 - alpha) * prev2;
31328
+ out[i] = prev2;
31329
+ } else if (run >= len) {
31330
+ let s = 0;
31331
+ for (let k = i - len + 1; k <= i; k++) s += v[k];
31332
+ prev2 = s / len;
31333
+ out[i] = prev2;
31334
+ }
31335
+ }
31336
+ return out;
31337
+ }
31338
+ function ema(v, len) {
31339
+ return recursive(v, len, 2 / (len + 1));
31340
+ }
31341
+ function rma(v, len) {
31342
+ return recursive(v, len, 1 / len);
31343
+ }
31344
+ function change(v, n = 1) {
31345
+ const out = new Array(v.length).fill(Number.NaN);
31346
+ for (let i = n; i < v.length; i++) {
31347
+ const a = v[i];
31348
+ const b = v[i - n];
31349
+ if (Number.isFinite(a) && Number.isFinite(b)) out[i] = a - b;
31350
+ }
31351
+ return out;
31352
+ }
31353
+ function roc(v, len) {
31354
+ const out = new Array(v.length).fill(Number.NaN);
31355
+ for (let i = len; i < v.length; i++) {
31356
+ const a = v[i];
31357
+ const b = v[i - len];
31358
+ if (Number.isFinite(a) && Number.isFinite(b) && b !== 0) out[i] = 100 * (a - b) / b;
31359
+ }
31360
+ return out;
31361
+ }
31362
+ function shift(v, n) {
31363
+ const out = new Array(v.length).fill(Number.NaN);
31364
+ for (let i = 0; i < v.length; i++) {
31365
+ const j = i - n;
31366
+ if (j >= 0 && j < v.length) out[i] = v[j];
31367
+ }
31368
+ return out;
31369
+ }
31370
+ function trueRange(bars) {
31371
+ const out = new Array(bars.length).fill(Number.NaN);
31372
+ for (let i = 0; i < bars.length; i++) {
31373
+ const b = bars[i];
31374
+ if (i === 0) {
31375
+ out[i] = b.high - b.low;
31376
+ continue;
31377
+ }
31378
+ const pc = bars[i - 1].close;
31379
+ out[i] = Math.max(b.high - b.low, Math.abs(b.high - pc), Math.abs(b.low - pc));
31380
+ }
31381
+ return out;
31382
+ }
31383
+ function atr(bars, len) {
31384
+ return rma(trueRange(bars), len);
31385
+ }
31386
+ function rsi(v, len) {
31387
+ const d = change(v);
31388
+ const up = map(d, (x) => Math.max(x, 0));
31389
+ const dn = map(d, (x) => Math.max(-x, 0));
31390
+ return zip(rma(up, len), rma(dn, len), (u, w) => w === 0 ? 100 : 100 - 100 / (1 + u / w));
31391
+ }
31392
+ function stoch(v, hi, lo, len) {
31393
+ const hh = highest(hi, len);
31394
+ const ll = lowest(lo, len);
31395
+ const out = new Array(v.length).fill(Number.NaN);
31396
+ for (let i = 0; i < v.length; i++) {
31397
+ const x = v[i];
31398
+ const h = hh[i];
31399
+ const l = ll[i];
31400
+ if (!Number.isFinite(x) || !Number.isFinite(h) || !Number.isFinite(l)) continue;
31401
+ out[i] = h === l ? 50 : 100 * (x - l) / (h - l);
31402
+ }
31403
+ return out;
31404
+ }
31405
+ function linreg(v, len) {
31406
+ const sx = (len - 1) * len / 2;
31407
+ const sxx = (len - 1) * len * (2 * len - 1) / 6;
31408
+ const denom = len * sxx - sx * sx;
31409
+ return windowed(v, len, (w, from, to) => {
31410
+ let sy = 0;
31411
+ let sxy = 0;
31412
+ for (let k = from; k <= to; k++) {
31413
+ const x = k - from;
31414
+ sy += w[k];
31415
+ sxy += x * w[k];
31416
+ }
31417
+ const slope = denom === 0 ? 0 : (len * sxy - sx * sy) / denom;
31418
+ const intercept = (sy - slope * sx) / len;
31419
+ return intercept + slope * (len - 1);
31420
+ });
31421
+ }
31422
+ function swma(v) {
31423
+ const out = new Array(v.length).fill(Number.NaN);
31424
+ for (let i = 3; i < v.length; i++) {
31425
+ const a = v[i - 3];
31426
+ const b = v[i - 2];
31427
+ const c = v[i - 1];
31428
+ const d = v[i];
31429
+ if (Number.isFinite(a) && Number.isFinite(b) && Number.isFinite(c) && Number.isFinite(d)) {
31430
+ out[i] = (a + 2 * b + 2 * c + d) / 6;
31431
+ }
31432
+ }
31433
+ return out;
31434
+ }
31435
+ function percentRank(v, len) {
31436
+ const out = new Array(v.length).fill(Number.NaN);
31437
+ for (let i = len; i < v.length; i++) {
31438
+ const x = v[i];
31439
+ if (!Number.isFinite(x)) continue;
31440
+ let below = 0;
31441
+ let count = 0;
31442
+ for (let k = i - len; k < i; k++) {
31443
+ const y = v[k];
31444
+ if (!Number.isFinite(y)) {
31445
+ count = -1;
31446
+ break;
31447
+ }
31448
+ count += 1;
31449
+ if (y <= x) below += 1;
31450
+ }
31451
+ if (count === len) out[i] = 100 * below / len;
31452
+ }
31453
+ return out;
31454
+ }
31455
+ function cumSum(v) {
31456
+ const out = new Array(v.length).fill(Number.NaN);
31457
+ let acc = 0;
31458
+ let seen = false;
31459
+ for (let i = 0; i < v.length; i++) {
31460
+ const x = v[i];
31461
+ if (Number.isFinite(x)) {
31462
+ acc += x;
31463
+ seen = true;
31464
+ }
31465
+ if (seen) out[i] = acc;
31466
+ }
31467
+ return out;
31468
+ }
31469
+
31470
+ // src/core/native-indicators/classics/shared.ts
31471
+ function lengthInput(defval, key = "length", title = "Length", max = 5e3) {
31472
+ return { key, title, type: "int", defval, min: 1, max, step: 1 };
31473
+ }
31474
+ function sourceInput(defval = "Close") {
31475
+ return { key: "source", title: "Source", type: "string", defval, options: SOURCES2 };
31476
+ }
31477
+ function colorInput(defval = SERIES_LINE, title = "Color", key = "color") {
31478
+ return { key, title, type: "color", defval };
31479
+ }
31480
+ function withAlpha2(hex, alpha = 0.08) {
31481
+ const m = /^#([0-9a-f]{6})$/i.exec(hex.trim());
31482
+ if (!m) return hex;
31483
+ const a = Math.round(alpha * 255).toString(16).padStart(2, "0");
31484
+ return `#${m[1]}${a}`;
31485
+ }
31486
+
31487
+ // src/core/native-indicators/classics/averages.ts
31488
+ var MA_KINDS = ["SMA", "EMA", "WMA", "RMA", "VWMA"];
31489
+ var offsetInput = { key: "offset", title: "Offset", type: "int", defval: 0, min: -500, max: 500, step: 1 };
31490
+ function fixedAverage(type, title, shortTitle, smooth) {
31491
+ return {
31492
+ type,
31493
+ title,
31494
+ shortTitle,
31495
+ overlay: true,
31496
+ inputs: [lengthInput(20), sourceInput(), offsetInput, colorInput()],
31497
+ compute: (bars, inputs) => {
31498
+ const len = num3(inputs, "length", 20);
31499
+ let values = smooth(sourceValues(bars, str3(inputs, "source", "Close")), len);
31500
+ const offset = Math.trunc(num3(inputs, "offset", 0));
31501
+ if (offset !== 0) values = shift(values, offset);
31502
+ return { plots: [{ key: shortTitle.toLowerCase(), title: shortTitle, values, color: str3(inputs, "color", SERIES_LINE), width: 2 }] };
31503
+ }
31504
+ };
31505
+ }
31506
+ var simpleMa = fixedAverage("sma", "Simple Moving Average", "SMA", sma);
31507
+ var exponentialMa = fixedAverage("ema", "Exponential Moving Average", "EMA", ema);
31508
+ var movingAverage = {
31509
+ type: "moving-average",
31510
+ title: "Moving Average",
31511
+ shortTitle: "MA",
31512
+ overlay: true,
31513
+ inputs: [
31514
+ { key: "maType", title: "Type", type: "string", defval: "SMA", options: MA_KINDS },
31515
+ lengthInput(20),
31516
+ sourceInput(),
31517
+ offsetInput,
31518
+ colorInput()
31519
+ ],
31520
+ compute: (bars, inputs) => {
31521
+ const len = num3(inputs, "length", 20);
31522
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
31523
+ const kind = str3(inputs, "maType", "SMA");
31524
+ let values;
31525
+ switch (kind) {
31526
+ case "EMA":
31527
+ values = ema(src, len);
31528
+ break;
31529
+ case "WMA":
31530
+ values = wma(src, len);
31531
+ break;
31532
+ case "RMA":
31533
+ values = rma(src, len);
31534
+ break;
31535
+ case "VWMA":
31536
+ values = vwma(src, volumes(bars), len);
31537
+ break;
31538
+ default:
31539
+ values = sma(src, len);
31540
+ }
31541
+ const offset = Math.trunc(num3(inputs, "offset", 0));
31542
+ if (offset !== 0) values = shift(values, offset);
31543
+ return { plots: [{ key: "ma", title: kind, values, color: str3(inputs, "color", SERIES_LINE), width: 2 }] };
31544
+ }
31545
+ };
31546
+ var smoothedMa = {
31547
+ type: "rma",
31548
+ title: "Smoothed Moving Average",
31549
+ shortTitle: "RMA",
31550
+ overlay: true,
31551
+ inputs: [lengthInput(14), sourceInput(), colorInput()],
31552
+ compute: (bars, inputs) => ({
31553
+ plots: [
31554
+ {
31555
+ key: "rma",
31556
+ title: "RMA",
31557
+ values: rma(sourceValues(bars, str3(inputs, "source", "Close")), num3(inputs, "length", 14)),
31558
+ color: str3(inputs, "color", SERIES_LINE),
31559
+ width: 2
31560
+ }
31561
+ ]
31562
+ })
31563
+ };
31564
+ var zlema = {
31565
+ type: "zlema",
31566
+ title: "Zero-Lag Exponential Moving Average",
31567
+ shortTitle: "ZLEMA",
31568
+ overlay: true,
31569
+ inputs: [lengthInput(14), sourceInput(), colorInput()],
31570
+ compute: (bars, inputs) => {
31571
+ const len = num3(inputs, "length", 14);
31572
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
31573
+ const lag = Math.floor((len - 1) / 2);
31574
+ const lagged = shift(src, lag);
31575
+ const delagged = zip(src, lagged, (a, b) => 2 * a - b);
31576
+ return { plots: [{ key: "zlema", title: "ZLEMA", values: ema(delagged, len), color: str3(inputs, "color", SERIES_LINE), width: 2 }] };
31577
+ }
31578
+ };
31579
+ var vidya = {
31580
+ type: "vidya",
31581
+ title: "Variable Index Dynamic Average",
31582
+ shortTitle: "VIDYA",
31583
+ overlay: true,
31584
+ inputs: [lengthInput(14), { key: "cmoLength", title: "Momentum length", type: "int", defval: 9, min: 1, max: 500, step: 1 }, sourceInput(), colorInput()],
31585
+ compute: (bars, inputs) => {
31586
+ const len = num3(inputs, "length", 14);
31587
+ const cmoLen = num3(inputs, "cmoLength", 9);
31588
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
31589
+ const d = change(src);
31590
+ const su = new Array(src.length).fill(Number.NaN);
31591
+ const sd = new Array(src.length).fill(Number.NaN);
31592
+ for (let i = cmoLen; i < src.length; i++) {
31593
+ let u = 0;
31594
+ let w = 0;
31595
+ let ok = true;
31596
+ for (let k = i - cmoLen + 1; k <= i; k++) {
31597
+ const x = d[k];
31598
+ if (!Number.isFinite(x)) {
31599
+ ok = false;
31600
+ break;
31601
+ }
31602
+ if (x > 0) u += x;
31603
+ else w -= x;
31604
+ }
31605
+ if (ok) {
31606
+ su[i] = u;
31607
+ sd[i] = w;
31608
+ }
31609
+ }
31610
+ const alpha = 2 / (len + 1);
31611
+ const out = new Array(src.length).fill(Number.NaN);
31612
+ let prev2 = Number.NaN;
31613
+ for (let i = 0; i < src.length; i++) {
31614
+ const x = src[i];
31615
+ const u = su[i];
31616
+ const w = sd[i];
31617
+ if (!Number.isFinite(x) || !Number.isFinite(u) || !Number.isFinite(w)) continue;
31618
+ const total = u + w;
31619
+ const k = total === 0 ? 0 : Math.abs((u - w) / total);
31620
+ prev2 = Number.isFinite(prev2) ? alpha * k * x + (1 - alpha * k) * prev2 : x;
31621
+ out[i] = prev2;
31622
+ }
31623
+ return { plots: [{ key: "vidya", title: "VIDYA", values: out, color: str3(inputs, "color", SERIES_LINE), width: 2 }] };
31624
+ }
31625
+ };
31626
+ var maEnvelope = {
31627
+ type: "ma-envelope",
31628
+ title: "Moving Average Envelope",
31629
+ shortTitle: "MA Env",
31630
+ overlay: true,
31631
+ inputs: [
31632
+ { key: "maType", title: "Type", type: "string", defval: "SMA", options: ["SMA", "EMA"] },
31633
+ lengthInput(20),
31634
+ sourceInput(),
31635
+ { key: "percent", title: "Percent", type: "float", defval: 2.5, min: 0.01, max: 50, step: 0.1 },
31636
+ colorInput()
31637
+ ],
31638
+ compute: (bars, inputs) => {
31639
+ const len = num3(inputs, "length", 20);
31640
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
31641
+ const basis = str3(inputs, "maType", "SMA") === "EMA" ? ema(src, len) : sma(src, len);
31642
+ const k = num3(inputs, "percent", 2.5) / 100;
31643
+ const ink = str3(inputs, "color", SERIES_LINE);
31644
+ return {
31645
+ plots: [
31646
+ { key: "basis", title: "Basis", values: basis, color: ink, width: 2 },
31647
+ { key: "upper", title: "Upper", values: map(basis, (x) => x * (1 + k)), color: ink },
31648
+ { key: "lower", title: "Lower", values: map(basis, (x) => x * (1 - k)), color: ink }
31649
+ ],
31650
+ bands: [{ key: "envelope", from: "upper", to: "lower", color: withAlpha2(ink) }]
31651
+ };
31652
+ }
31653
+ };
31654
+ var linearRegression = {
31655
+ type: "linear-regression",
31656
+ title: "Linear Regression Curve",
31657
+ shortTitle: "LinReg",
31658
+ overlay: true,
31659
+ inputs: [lengthInput(14), sourceInput(), colorInput()],
31660
+ compute: (bars, inputs) => ({
31661
+ plots: [
31662
+ {
31663
+ key: "linreg",
31664
+ title: "LinReg",
31665
+ values: linreg(sourceValues(bars, str3(inputs, "source", "Close")), num3(inputs, "length", 14)),
31666
+ color: str3(inputs, "color", SERIES_LINE),
31667
+ width: 2
31668
+ }
31669
+ ]
31670
+ })
31671
+ };
31672
+ var averageSpecs = [simpleMa, exponentialMa, movingAverage, smoothedMa, zlema, vidya, maEnvelope, linearRegression];
31673
+
31674
+ // src/core/native-indicators/classics/bands.ts
31675
+ function bollinger(bars, inputs) {
31676
+ const len = num3(inputs, "length", 20);
31677
+ const mult = num3(inputs, "mult", 2);
31678
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
31679
+ const basis = sma(src, len);
31680
+ const dev = map(stdev(src, len), (x) => x * mult);
31681
+ return { basis, upper: zip(basis, dev, (b, d) => b + d), lower: zip(basis, dev, (b, d) => b - d) };
31682
+ }
31683
+ var BB_INPUTS = [lengthInput(20), sourceInput(), { key: "mult", title: "StdDev", type: "float", defval: 2, min: 0.1, max: 50, step: 0.1 }];
31684
+ var bollingerBands = {
31685
+ type: "bollinger-bands",
31686
+ title: "Bollinger Bands",
31687
+ shortTitle: "BB",
31688
+ overlay: true,
31689
+ inputs: [...BB_INPUTS, colorInput()],
31690
+ compute: (bars, inputs) => {
31691
+ const { basis, upper, lower } = bollinger(bars, inputs);
31692
+ const ink = str3(inputs, "color", SERIES_LINE);
31693
+ return {
31694
+ plots: [
31695
+ { key: "basis", title: "Basis", values: basis, color: WARNING },
31696
+ { key: "upper", title: "Upper", values: upper, color: ink },
31697
+ { key: "lower", title: "Lower", values: lower, color: ink }
31698
+ ],
31699
+ bands: [{ key: "bb", from: "upper", to: "lower", color: withAlpha2(ink) }]
31700
+ };
31701
+ }
31702
+ };
31703
+ var percentB = {
31704
+ type: "percent-b",
31705
+ title: "Bollinger Bands %B",
31706
+ shortTitle: "%B",
31707
+ overlay: false,
31708
+ inputs: [...BB_INPUTS, colorInput()],
31709
+ compute: (bars, inputs) => {
31710
+ const { upper, lower } = bollinger(bars, inputs);
31711
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
31712
+ const values = src.map((x, i) => {
31713
+ const u = upper[i];
31714
+ const l = lower[i];
31715
+ return Number.isFinite(u) && Number.isFinite(l) && u !== l ? (x - l) / (u - l) : Number.NaN;
31716
+ });
31717
+ return {
31718
+ plots: [{ key: "pb", title: "%B", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
31719
+ levels: [
31720
+ { key: "upper", price: 1, color: NEUTRAL },
31721
+ { key: "middle", price: 0.5, color: NEUTRAL, lineStyle: "dotted" },
31722
+ { key: "lower", price: 0, color: NEUTRAL }
31723
+ ]
31724
+ };
31725
+ }
31726
+ };
31727
+ var bandwidth = {
31728
+ type: "bandwidth",
31729
+ title: "Bollinger Bands Width",
31730
+ shortTitle: "BandWidth",
31731
+ overlay: false,
31732
+ inputs: [...BB_INPUTS, colorInput()],
31733
+ compute: (bars, inputs) => {
31734
+ const { basis, upper, lower } = bollinger(bars, inputs);
31735
+ const values = basis.map((b, i) => {
31736
+ const u = upper[i];
31737
+ const l = lower[i];
31738
+ return Number.isFinite(b) && b !== 0 && Number.isFinite(u) && Number.isFinite(l) ? (u - l) / b * 100 : Number.NaN;
31739
+ });
31740
+ return { plots: [{ key: "bbw", title: "BBW", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }] };
31741
+ }
31742
+ };
31743
+ var keltner = {
31744
+ type: "keltner-channels",
31745
+ title: "Keltner Channels",
31746
+ shortTitle: "KC",
31747
+ overlay: true,
31748
+ inputs: [
31749
+ lengthInput(20),
31750
+ { key: "mult", title: "Multiplier", type: "float", defval: 2, min: 0.1, max: 50, step: 0.1 },
31751
+ { key: "atrLength", title: "ATR length", type: "int", defval: 10, min: 1, max: 500, step: 1 },
31752
+ sourceInput(),
31753
+ colorInput()
31754
+ ],
31755
+ compute: (bars, inputs) => {
31756
+ const len = num3(inputs, "length", 20);
31757
+ const mult = num3(inputs, "mult", 2);
31758
+ const basis = ema(sourceValues(bars, str3(inputs, "source", "Close")), len);
31759
+ const range = map(atr(bars, num3(inputs, "atrLength", 10)), (x) => x * mult);
31760
+ const ink = str3(inputs, "color", SERIES_LINE);
31761
+ return {
31762
+ plots: [
31763
+ { key: "basis", title: "Basis", values: basis, color: WARNING },
31764
+ { key: "upper", title: "Upper", values: zip(basis, range, (b, r) => b + r), color: ink },
31765
+ { key: "lower", title: "Lower", values: zip(basis, range, (b, r) => b - r), color: ink }
31766
+ ],
31767
+ bands: [{ key: "kc", from: "upper", to: "lower", color: withAlpha2(ink) }]
31768
+ };
31769
+ }
31770
+ };
31771
+ var donchian = {
31772
+ type: "donchian-channels",
31773
+ title: "Donchian Channels",
31774
+ shortTitle: "DC",
31775
+ overlay: true,
31776
+ inputs: [lengthInput(20), colorInput()],
31777
+ compute: (bars, inputs) => {
31778
+ const len = num3(inputs, "length", 20);
31779
+ const upper = highest(highs(bars), len);
31780
+ const lower = lowest(lows(bars), len);
31781
+ const ink = str3(inputs, "color", SERIES_LINE);
31782
+ return {
31783
+ plots: [
31784
+ { key: "basis", title: "Basis", values: zip(upper, lower, (u, l) => (u + l) / 2), color: WARNING },
31785
+ { key: "upper", title: "Upper", values: upper, color: ink },
31786
+ { key: "lower", title: "Lower", values: lower, color: ink }
31787
+ ],
31788
+ bands: [{ key: "dc", from: "upper", to: "lower", color: withAlpha2(ink) }]
31789
+ };
31790
+ }
31791
+ };
31792
+ var chandelier = {
31793
+ type: "chandelier-exit",
31794
+ title: "Chandelier Exit",
31795
+ shortTitle: "CE",
31796
+ overlay: true,
31797
+ inputs: [lengthInput(22), { key: "mult", title: "ATR multiplier", type: "float", defval: 3, min: 0.1, max: 50, step: 0.1 }],
31798
+ compute: (bars, inputs) => {
31799
+ const len = num3(inputs, "length", 22);
31800
+ const range = map(atr(bars, len), (x) => x * num3(inputs, "mult", 3));
31801
+ const long = zip(highest(highs(bars), len), range, (h, r) => h - r);
31802
+ const short = zip(lowest(lows(bars), len), range, (l, r) => l + r);
31803
+ return {
31804
+ plots: [
31805
+ { key: "long", title: "Long stop", values: long, color: BULLISH },
31806
+ { key: "short", title: "Short stop", values: short, color: BEARISH }
31807
+ ]
31808
+ };
31809
+ }
31810
+ };
31811
+ var chandeKroll = {
31812
+ type: "chande-kroll-stop",
31813
+ title: "Chande Kroll Stop",
31814
+ shortTitle: "CKS",
31815
+ overlay: true,
31816
+ inputs: [
31817
+ { key: "p", title: "ATR length", type: "int", defval: 10, min: 1, max: 500, step: 1 },
31818
+ { key: "x", title: "ATR multiplier", type: "float", defval: 1, min: 0.1, max: 50, step: 0.1 },
31819
+ { key: "q", title: "Stop length", type: "int", defval: 9, min: 1, max: 500, step: 1 }
31820
+ ],
31821
+ compute: (bars, inputs) => {
31822
+ const p = num3(inputs, "p", 10);
31823
+ const x = num3(inputs, "x", 1);
31824
+ const q = num3(inputs, "q", 9);
31825
+ const range = map(atr(bars, p), (v) => v * x);
31826
+ const firstHigh = zip(highest(highs(bars), p), range, (h, r) => h - r);
31827
+ const firstLow = zip(lowest(lows(bars), p), range, (l, r) => l + r);
31828
+ return {
31829
+ plots: [
31830
+ { key: "stopShort", title: "Stop short", values: highest(firstHigh, q), color: BEARISH },
31831
+ { key: "stopLong", title: "Stop long", values: lowest(firstLow, q), color: BULLISH }
31832
+ ]
31833
+ };
31834
+ }
31835
+ };
31836
+ var supertrend = {
31837
+ type: "supertrend",
31838
+ title: "SuperTrend",
31839
+ shortTitle: "SuperTrend",
31840
+ overlay: true,
31841
+ inputs: [
31842
+ { key: "atrLength", title: "ATR length", type: "int", defval: 10, min: 1, max: 500, step: 1 },
31843
+ { key: "mult", title: "Factor", type: "float", defval: 3, min: 0.1, max: 50, step: 0.1 }
31844
+ ],
31845
+ compute: (bars, inputs) => {
31846
+ const len = num3(inputs, "atrLength", 10);
31847
+ const mult = num3(inputs, "mult", 3);
31848
+ const range = atr(bars, len);
31849
+ const n = bars.length;
31850
+ const st = new Array(n).fill(Number.NaN);
31851
+ const dir = new Array(n).fill(1);
31852
+ let up = Number.NaN;
31853
+ let dn = Number.NaN;
31854
+ let trend = 1;
31855
+ for (let i = 0; i < n; i++) {
31856
+ const r = range[i];
31857
+ if (!Number.isFinite(r)) continue;
31858
+ const b = bars[i];
31859
+ const mid = (b.high + b.low) / 2;
31860
+ const basicUp = mid - mult * r;
31861
+ const basicDn = mid + mult * r;
31862
+ const prevClose = i > 0 ? bars[i - 1].close : b.close;
31863
+ up = Number.isFinite(up) && prevClose > up ? Math.max(basicUp, up) : basicUp;
31864
+ dn = Number.isFinite(dn) && prevClose < dn ? Math.min(basicDn, dn) : basicDn;
31865
+ if (trend === 1 && b.close < up) trend = -1;
31866
+ else if (trend === -1 && b.close > dn) trend = 1;
31867
+ dir[i] = trend;
31868
+ st[i] = trend === 1 ? up : dn;
31869
+ }
31870
+ const colors = dir.map((d, i) => Number.isFinite(st[i]) ? d === 1 ? BULLISH : BEARISH : null);
31871
+ return { plots: [{ key: "st", title: "SuperTrend", values: st, color: BULLISH, width: 2, colors }] };
31872
+ }
31873
+ };
31874
+ var alligator = {
31875
+ type: "williams-alligator",
31876
+ title: "Williams Alligator",
31877
+ shortTitle: "Alligator",
31878
+ overlay: true,
31879
+ inputs: [
31880
+ { key: "jawLength", title: "Jaw length", type: "int", defval: 13, min: 1, max: 500, step: 1 },
31881
+ { key: "teethLength", title: "Teeth length", type: "int", defval: 8, min: 1, max: 500, step: 1 },
31882
+ { key: "lipsLength", title: "Lips length", type: "int", defval: 5, min: 1, max: 500, step: 1 },
31883
+ { key: "jawOffset", title: "Jaw offset", type: "int", defval: 8, min: 0, max: 100, step: 1 },
31884
+ { key: "teethOffset", title: "Teeth offset", type: "int", defval: 5, min: 0, max: 100, step: 1 },
31885
+ { key: "lipsOffset", title: "Lips offset", type: "int", defval: 3, min: 0, max: 100, step: 1 }
31886
+ ],
31887
+ compute: (bars, inputs) => {
31888
+ const hl2 = sourceValues(bars, "HL2");
31889
+ const jaw = shift(rma(hl2, num3(inputs, "jawLength", 13)), num3(inputs, "jawOffset", 8));
31890
+ const teeth = shift(rma(hl2, num3(inputs, "teethLength", 8)), num3(inputs, "teethOffset", 5));
31891
+ const lips = shift(rma(hl2, num3(inputs, "lipsLength", 5)), num3(inputs, "lipsOffset", 3));
31892
+ return {
31893
+ plots: [
31894
+ { key: "jaw", title: "Jaw", values: jaw, color: SERIES_LINE },
31895
+ { key: "teeth", title: "Teeth", values: teeth, color: BEARISH },
31896
+ { key: "lips", title: "Lips", values: lips, color: BULLISH }
31897
+ ]
31898
+ };
31899
+ }
31900
+ };
31901
+ var gator = {
31902
+ type: "gator-oscillator",
31903
+ title: "Gator Oscillator",
31904
+ shortTitle: "Gator Oscillator",
31905
+ overlay: false,
31906
+ inputs: [],
31907
+ compute: (bars) => {
31908
+ const hl2 = sourceValues(bars, "HL2");
31909
+ const jaw = shift(rma(hl2, 13), 8);
31910
+ const teeth = shift(rma(hl2, 8), 5);
31911
+ const lips = shift(rma(hl2, 5), 3);
31912
+ const upper = map(sub(jaw, teeth), Math.abs);
31913
+ const lower = map(sub(teeth, lips), (x) => -Math.abs(x));
31914
+ const colorByExpansion = (v) => v.map((x, i) => {
31915
+ if (!Number.isFinite(x)) return null;
31916
+ const prev2 = i > 0 ? v[i - 1] : Number.NaN;
31917
+ return Number.isFinite(prev2) && Math.abs(x) > Math.abs(prev2) ? BULLISH : BEARISH;
31918
+ });
31919
+ return {
31920
+ plots: [
31921
+ { key: "upper", title: "Upper", values: upper, kind: "histogram", color: BULLISH, colors: colorByExpansion(upper), base: 0 },
31922
+ { key: "lower", title: "Lower", values: lower, kind: "histogram", color: BEARISH, colors: colorByExpansion(lower), base: 0 }
31923
+ ]
31924
+ };
31925
+ }
31926
+ };
31927
+ var bandSpecs = [bollingerBands, percentB, bandwidth, keltner, donchian, chandelier, chandeKroll, supertrend, alligator, gator];
31928
+
31929
+ // src/core/native-indicators/classics/oscillators.ts
31930
+ function guideLevels(low, high, mid) {
31931
+ const levels = [
31932
+ { key: "upper", price: high, color: NEUTRAL },
31933
+ { key: "lower", price: low, color: NEUTRAL }
31934
+ ];
31935
+ if (mid != null) levels.splice(1, 0, { key: "middle", price: mid, color: NEUTRAL, lineStyle: "dotted" });
31936
+ return levels;
31937
+ }
31938
+ var ZERO_LEVEL = [{ key: "zero", price: 0, color: NEUTRAL }];
31939
+ function histColors(values) {
31940
+ return values.map((x, i) => {
31941
+ if (!Number.isFinite(x)) return null;
31942
+ const prev2 = i > 0 ? values[i - 1] : Number.NaN;
31943
+ const rising = Number.isFinite(prev2) ? x >= prev2 : true;
31944
+ if (x >= 0) return rising ? BULLISH : `${BULLISH}80`;
31945
+ return rising ? `${BEARISH}80` : BEARISH;
31946
+ });
31947
+ }
31948
+ var rsiSpec = {
31949
+ type: "rsi",
31950
+ title: "Relative Strength Index",
31951
+ shortTitle: "RSI",
31952
+ overlay: false,
31953
+ inputs: [lengthInput(14), sourceInput(), colorInput()],
31954
+ compute: (bars, inputs) => ({
31955
+ plots: [
31956
+ {
31957
+ key: "rsi",
31958
+ title: "RSI",
31959
+ values: rsi(sourceValues(bars, str3(inputs, "source", "Close")), num3(inputs, "length", 14)),
31960
+ color: str3(inputs, "color", SERIES_LINE),
31961
+ width: 2
31962
+ }
31963
+ ],
31964
+ levels: guideLevels(30, 70, 50)
31965
+ })
31966
+ };
31967
+ var stochastic = {
31968
+ type: "stochastic",
31969
+ title: "Stochastic",
31970
+ shortTitle: "Stoch",
31971
+ overlay: false,
31972
+ inputs: [
31973
+ lengthInput(14, "kLength", "%K length"),
31974
+ lengthInput(1, "kSmoothing", "%K smoothing", 500),
31975
+ lengthInput(3, "dLength", "%D smoothing", 500)
31976
+ ],
31977
+ compute: (bars, inputs) => {
31978
+ const k = sma(stoch(closes(bars), highs(bars), lows(bars), num3(inputs, "kLength", 14)), num3(inputs, "kSmoothing", 1));
31979
+ const d = sma(k, num3(inputs, "dLength", 3));
31980
+ return {
31981
+ plots: [
31982
+ { key: "k", title: "%K", values: k, color: SERIES_LINE, width: 2 },
31983
+ { key: "d", title: "%D", values: d, color: WARNING }
31984
+ ],
31985
+ levels: guideLevels(20, 80)
31986
+ };
31987
+ }
31988
+ };
31989
+ var stochasticRsi = {
31990
+ type: "stochastic-rsi",
31991
+ title: "Stochastic RSI",
31992
+ shortTitle: "Stoch RSI",
31993
+ overlay: false,
31994
+ inputs: [
31995
+ lengthInput(14, "rsiLength", "RSI length"),
31996
+ lengthInput(14, "stochLength", "Stochastic length"),
31997
+ lengthInput(3, "kSmoothing", "%K smoothing", 500),
31998
+ lengthInput(3, "dLength", "%D smoothing", 500),
31999
+ sourceInput()
32000
+ ],
32001
+ compute: (bars, inputs) => {
32002
+ const r = rsi(sourceValues(bars, str3(inputs, "source", "Close")), num3(inputs, "rsiLength", 14));
32003
+ const k = sma(stoch(r, r, r, num3(inputs, "stochLength", 14)), num3(inputs, "kSmoothing", 3));
32004
+ const d = sma(k, num3(inputs, "dLength", 3));
32005
+ return {
32006
+ plots: [
32007
+ { key: "k", title: "%K", values: k, color: SERIES_LINE, width: 2 },
32008
+ { key: "d", title: "%D", values: d, color: WARNING }
32009
+ ],
32010
+ levels: guideLevels(20, 80)
32011
+ };
32012
+ }
32013
+ };
32014
+ var macd = {
32015
+ type: "macd",
32016
+ title: "MACD",
32017
+ overlay: false,
32018
+ inputs: [
32019
+ lengthInput(12, "fastLength", "Fast length"),
32020
+ lengthInput(26, "slowLength", "Slow length"),
32021
+ lengthInput(9, "signalLength", "Signal smoothing", 500),
32022
+ sourceInput()
32023
+ ],
32024
+ compute: (bars, inputs) => {
32025
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
32026
+ const line = sub(ema(src, num3(inputs, "fastLength", 12)), ema(src, num3(inputs, "slowLength", 26)));
32027
+ const signal = ema(line, num3(inputs, "signalLength", 9));
32028
+ const hist = sub(line, signal);
32029
+ return {
32030
+ plots: [
32031
+ { key: "hist", title: "Histogram", values: hist, kind: "histogram", color: BULLISH, colors: histColors(hist), base: 0 },
32032
+ { key: "macd", title: "MACD", values: line, color: SERIES_LINE, width: 2 },
32033
+ { key: "signal", title: "Signal", values: signal, color: WARNING }
32034
+ ],
32035
+ levels: ZERO_LEVEL
32036
+ };
32037
+ }
32038
+ };
32039
+ var ppo = {
32040
+ type: "ppo",
32041
+ title: "Percentage Price Oscillator",
32042
+ shortTitle: "PPO",
32043
+ overlay: false,
32044
+ inputs: [
32045
+ lengthInput(12, "fastLength", "Fast length"),
32046
+ lengthInput(26, "slowLength", "Slow length"),
32047
+ lengthInput(9, "signalLength", "Signal smoothing", 500),
32048
+ sourceInput()
32049
+ ],
32050
+ compute: (bars, inputs) => {
32051
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
32052
+ const fast = ema(src, num3(inputs, "fastLength", 12));
32053
+ const slow = ema(src, num3(inputs, "slowLength", 26));
32054
+ const line = zip(fast, slow, (f, s) => s === 0 ? Number.NaN : (f - s) / s * 100);
32055
+ const signal = ema(line, num3(inputs, "signalLength", 9));
32056
+ const hist = sub(line, signal);
32057
+ return {
32058
+ plots: [
32059
+ { key: "hist", title: "Histogram", values: hist, kind: "histogram", color: BULLISH, colors: histColors(hist), base: 0 },
32060
+ { key: "ppo", title: "PPO", values: line, color: SERIES_LINE, width: 2 },
32061
+ { key: "signal", title: "Signal", values: signal, color: WARNING }
32062
+ ],
32063
+ levels: ZERO_LEVEL
32064
+ };
32065
+ }
32066
+ };
32067
+ var awesome = {
32068
+ type: "awesome-oscillator",
32069
+ title: "Awesome Oscillator",
32070
+ shortTitle: "AO",
32071
+ overlay: false,
32072
+ inputs: [],
32073
+ compute: (bars) => {
32074
+ const hl2 = sourceValues(bars, "HL2");
32075
+ const ao = sub(sma(hl2, 5), sma(hl2, 34));
32076
+ const colors = ao.map((x, i) => {
32077
+ if (!Number.isFinite(x)) return null;
32078
+ const prev2 = i > 0 ? ao[i - 1] : Number.NaN;
32079
+ return Number.isFinite(prev2) && x < prev2 ? BEARISH : BULLISH;
32080
+ });
32081
+ return {
32082
+ plots: [{ key: "ao", title: "AO", values: ao, kind: "histogram", color: BULLISH, colors, base: 0 }],
32083
+ levels: ZERO_LEVEL
32084
+ };
32085
+ }
32086
+ };
32087
+ var cci = {
32088
+ type: "commodity-channel-index",
32089
+ title: "Commodity Channel Index",
32090
+ shortTitle: "CCI",
32091
+ overlay: false,
32092
+ inputs: [lengthInput(20), sourceInput("HLC3"), colorInput()],
32093
+ compute: (bars, inputs) => {
32094
+ const len = num3(inputs, "length", 20);
32095
+ const src = sourceValues(bars, str3(inputs, "source", "HLC3"));
32096
+ const basis = sma(src, len);
32097
+ const dev = new Array(src.length).fill(Number.NaN);
32098
+ for (let i = len - 1; i < src.length; i++) {
32099
+ const mean = basis[i];
32100
+ if (!Number.isFinite(mean)) continue;
32101
+ let d = 0;
32102
+ for (let k = i - len + 1; k <= i; k++) d += Math.abs(src[k] - mean);
32103
+ dev[i] = d / len;
32104
+ }
32105
+ const values = src.map((x, i) => {
32106
+ const b = basis[i];
32107
+ const d = dev[i];
32108
+ return Number.isFinite(b) && Number.isFinite(d) && d !== 0 ? (x - b) / (0.015 * d) : Number.NaN;
32109
+ });
32110
+ return {
32111
+ plots: [{ key: "cci", title: "CCI", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
32112
+ levels: guideLevels(-100, 100, 0)
32113
+ };
32114
+ }
32115
+ };
32116
+ var williamsR = {
32117
+ type: "williams-percent-r",
32118
+ title: "Williams %R",
32119
+ shortTitle: "%R",
32120
+ overlay: false,
32121
+ inputs: [lengthInput(14), colorInput()],
32122
+ compute: (bars, inputs) => {
32123
+ const values = map(stoch(closes(bars), highs(bars), lows(bars), num3(inputs, "length", 14)), (x) => x - 100);
32124
+ return {
32125
+ plots: [{ key: "wr", title: "%R", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
32126
+ levels: guideLevels(-80, -20)
32127
+ };
32128
+ }
32129
+ };
32130
+ var cmo = {
32131
+ type: "chande-momentum-oscillator",
32132
+ title: "Chande Momentum Oscillator",
32133
+ shortTitle: "CMO",
32134
+ overlay: false,
32135
+ inputs: [lengthInput(9), sourceInput(), colorInput()],
32136
+ compute: (bars, inputs) => {
32137
+ const len = num3(inputs, "length", 9);
32138
+ const d = change(sourceValues(bars, str3(inputs, "source", "Close")));
32139
+ const su = sum(map(d, (x) => Math.max(x, 0)), len);
32140
+ const sd = sum(map(d, (x) => Math.max(-x, 0)), len);
32141
+ const values = zip(su, sd, (u, w) => u + w === 0 ? 0 : 100 * (u - w) / (u + w));
32142
+ return {
32143
+ plots: [{ key: "cmo", title: "CMO", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
32144
+ levels: guideLevels(-50, 50, 0)
32145
+ };
32146
+ }
32147
+ };
32148
+ var connorsRsi = {
32149
+ type: "connors-rsi",
32150
+ title: "Connors RSI",
32151
+ shortTitle: "CRSI",
32152
+ overlay: false,
32153
+ inputs: [
32154
+ lengthInput(3, "rsiLength", "RSI length"),
32155
+ lengthInput(2, "streakLength", "Streak RSI length"),
32156
+ lengthInput(100, "rankLength", "Percent-rank length"),
32157
+ colorInput()
32158
+ ],
32159
+ compute: (bars, inputs) => {
32160
+ const c = closes(bars);
32161
+ const streak = new Array(c.length).fill(0);
32162
+ for (let i = 1; i < c.length; i++) {
32163
+ const d = c[i] - c[i - 1];
32164
+ streak[i] = d > 0 ? Math.max(streak[i - 1], 0) + 1 : d < 0 ? Math.min(streak[i - 1], 0) - 1 : 0;
32165
+ }
32166
+ const a = rsi(c, num3(inputs, "rsiLength", 3));
32167
+ const b = rsi(streak, num3(inputs, "streakLength", 2));
32168
+ const r = percentRank(roc(c, 1), num3(inputs, "rankLength", 100));
32169
+ const values = c.map((_, i) => {
32170
+ const x = a[i];
32171
+ const y = b[i];
32172
+ const z = r[i];
32173
+ return Number.isFinite(x) && Number.isFinite(y) && Number.isFinite(z) ? (x + y + z) / 3 : Number.NaN;
32174
+ });
32175
+ return {
32176
+ plots: [{ key: "crsi", title: "CRSI", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
32177
+ levels: guideLevels(20, 80, 50)
32178
+ };
32179
+ }
32180
+ };
32181
+ var fisher = {
32182
+ type: "fisher-transform",
32183
+ title: "Fisher Transform",
32184
+ shortTitle: "Fisher",
32185
+ overlay: false,
32186
+ inputs: [lengthInput(9)],
32187
+ compute: (bars, inputs) => {
32188
+ const len = num3(inputs, "length", 9);
32189
+ const hl2 = sourceValues(bars, "HL2");
32190
+ const hi = highest(hl2, len);
32191
+ const lo = lowest(hl2, len);
32192
+ const n = bars.length;
32193
+ const out = new Array(n).fill(Number.NaN);
32194
+ let value = 0;
32195
+ let fish = 0;
32196
+ for (let i = 0; i < n; i++) {
32197
+ const h = hi[i];
32198
+ const l = lo[i];
32199
+ if (!Number.isFinite(h) || !Number.isFinite(l)) continue;
32200
+ const norm = h === l ? 0 : (hl2[i] - l) / (h - l) - 0.5;
32201
+ value = Math.max(-0.999, Math.min(0.999, 0.66 * norm + 0.67 * value));
32202
+ fish = 0.5 * Math.log((1 + value) / (1 - value)) + 0.5 * fish;
32203
+ out[i] = fish;
32204
+ }
32205
+ return {
32206
+ plots: [
32207
+ { key: "fisher", title: "Fisher", values: out, color: SERIES_LINE, width: 2 },
32208
+ { key: "trigger", title: "Trigger", values: shift(out, 1), color: WARNING }
32209
+ ],
32210
+ levels: ZERO_LEVEL
32211
+ };
32212
+ }
32213
+ };
32214
+ var trix = {
32215
+ type: "trix",
32216
+ title: "TRIX",
32217
+ overlay: false,
32218
+ inputs: [lengthInput(18), colorInput()],
32219
+ compute: (bars, inputs) => {
32220
+ const len = num3(inputs, "length", 18);
32221
+ const logClose = map(closes(bars), Math.log);
32222
+ const smooth = ema(ema(ema(logClose, len), len), len);
32223
+ const values = map(change(smooth), (x) => x * 1e4);
32224
+ return {
32225
+ plots: [{ key: "trix", title: "TRIX", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
32226
+ levels: ZERO_LEVEL
32227
+ };
32228
+ }
32229
+ };
32230
+ function tsiLine(src, long, short) {
32231
+ const mom = change(src);
32232
+ const numer = ema(ema(mom, long), short);
32233
+ const denom = ema(ema(map(mom, Math.abs), long), short);
32234
+ return zip(numer, denom, (a, b) => b === 0 ? 0 : 100 * a / b);
32235
+ }
32236
+ var tsi = {
32237
+ type: "true-strength-index",
32238
+ title: "True Strength Index",
32239
+ shortTitle: "TSI",
32240
+ overlay: false,
32241
+ inputs: [lengthInput(25, "longLength", "Long length"), lengthInput(13, "shortLength", "Short length"), lengthInput(13, "signalLength", "Signal length")],
32242
+ compute: (bars, inputs) => {
32243
+ const line = tsiLine(closes(bars), num3(inputs, "longLength", 25), num3(inputs, "shortLength", 13));
32244
+ return {
32245
+ plots: [
32246
+ { key: "tsi", title: "TSI", values: line, color: SERIES_LINE, width: 2 },
32247
+ { key: "signal", title: "Signal", values: ema(line, num3(inputs, "signalLength", 13)), color: WARNING }
32248
+ ],
32249
+ levels: ZERO_LEVEL
32250
+ };
32251
+ }
32252
+ };
32253
+ var smiErgodic = {
32254
+ type: "smi-ergodic",
32255
+ title: "SMI Ergodic Oscillator",
32256
+ shortTitle: "SMIE",
32257
+ overlay: false,
32258
+ inputs: [lengthInput(5, "shortLength", "Short length"), lengthInput(20, "longLength", "Long length"), lengthInput(5, "signalLength", "Signal length")],
32259
+ compute: (bars, inputs) => {
32260
+ const line = tsiLine(closes(bars), num3(inputs, "longLength", 20), num3(inputs, "shortLength", 5));
32261
+ const signal = ema(line, num3(inputs, "signalLength", 5));
32262
+ const osc = sub(line, signal);
32263
+ return {
32264
+ plots: [
32265
+ { key: "osc", title: "Oscillator", values: osc, kind: "histogram", color: BULLISH, colors: histColors(osc), base: 0 },
32266
+ { key: "smi", title: "SMI", values: line, color: SERIES_LINE, width: 2 },
32267
+ { key: "signal", title: "Signal", values: signal, color: WARNING }
32268
+ ],
32269
+ levels: ZERO_LEVEL
32270
+ };
32271
+ }
32272
+ };
32273
+ var stc = {
32274
+ type: "schaff-trend-cycle",
32275
+ title: "Schaff Trend Cycle",
32276
+ shortTitle: "Schaff Trend Cycle",
32277
+ overlay: false,
32278
+ inputs: [lengthInput(10, "cycleLength", "Cycle length"), lengthInput(23, "fastLength", "Fast length"), lengthInput(50, "slowLength", "Slow length")],
32279
+ compute: (bars, inputs) => {
32280
+ const len = num3(inputs, "cycleLength", 10);
32281
+ const src = closes(bars);
32282
+ const macdLine = sub(ema(src, num3(inputs, "fastLength", 23)), ema(src, num3(inputs, "slowLength", 50)));
32283
+ const stochOf = (v) => {
32284
+ const lo = lowest(v, len);
32285
+ const hi = highest(v, len);
32286
+ const out = new Array(v.length).fill(Number.NaN);
32287
+ let prevRaw = Number.NaN;
32288
+ let smoothed = Number.NaN;
32289
+ for (let i = 0; i < v.length; i++) {
32290
+ const x = v[i];
32291
+ const l = lo[i];
32292
+ const h = hi[i];
32293
+ if (!Number.isFinite(x) || !Number.isFinite(l) || !Number.isFinite(h)) continue;
32294
+ const raw = h > l ? (x - l) / (h - l) * 100 : Number.isFinite(prevRaw) ? prevRaw : 50;
32295
+ prevRaw = raw;
32296
+ smoothed = Number.isFinite(smoothed) ? smoothed + 0.5 * (raw - smoothed) : raw;
32297
+ out[i] = smoothed;
32298
+ }
32299
+ return out;
32300
+ };
32301
+ const values = stochOf(stochOf(macdLine));
32302
+ return {
32303
+ plots: [{ key: "stc", title: "STC", values, color: SERIES_LINE, width: 2 }],
32304
+ levels: guideLevels(25, 75)
32305
+ };
32306
+ }
32307
+ };
32308
+ var kst = {
32309
+ type: "know-sure-thing",
32310
+ title: "Know Sure Thing",
32311
+ shortTitle: "KST",
32312
+ overlay: false,
32313
+ inputs: [sourceInput(), lengthInput(9, "signalLength", "Signal length")],
32314
+ compute: (bars, inputs) => {
32315
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
32316
+ const line = [
32317
+ sma(roc(src, 10), 10),
32318
+ sma(roc(src, 15), 10),
32319
+ sma(roc(src, 20), 10),
32320
+ sma(roc(src, 30), 15)
32321
+ ].reduce((acc, r, i) => zip(acc, r, (a, b) => a + b * (i + 1)), new Array(src.length).fill(0));
32322
+ return {
32323
+ plots: [
32324
+ { key: "kst", title: "KST", values: line, color: SERIES_LINE, width: 2 },
32325
+ { key: "signal", title: "Signal", values: sma(line, num3(inputs, "signalLength", 9)), color: WARNING }
32326
+ ],
32327
+ levels: ZERO_LEVEL
32328
+ };
32329
+ }
32330
+ };
32331
+ var coppock = {
32332
+ type: "coppock-curve",
32333
+ title: "Coppock Curve",
32334
+ shortTitle: "Coppock",
32335
+ overlay: false,
32336
+ inputs: [
32337
+ lengthInput(10, "wmaLength", "WMA length"),
32338
+ lengthInput(14, "longRoc", "Long RoC length"),
32339
+ lengthInput(11, "shortRoc", "Short RoC length"),
32340
+ sourceInput(),
32341
+ colorInput()
32342
+ ],
32343
+ compute: (bars, inputs) => {
32344
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
32345
+ const values = wma(zip(roc(src, num3(inputs, "longRoc", 14)), roc(src, num3(inputs, "shortRoc", 11)), (a, b) => a + b), num3(inputs, "wmaLength", 10));
32346
+ return {
32347
+ plots: [{ key: "coppock", title: "Coppock", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
32348
+ levels: ZERO_LEVEL
32349
+ };
32350
+ }
32351
+ };
32352
+ var dpo = {
32353
+ type: "detrended-price-oscillator",
32354
+ title: "Detrended Price Oscillator",
32355
+ shortTitle: "DPO",
32356
+ overlay: false,
32357
+ inputs: [lengthInput(21), colorInput()],
32358
+ compute: (bars, inputs) => {
32359
+ const len = num3(inputs, "length", 21);
32360
+ const barsback = Math.floor(len / 2) + 1;
32361
+ const values = sub(closes(bars), shift(sma(closes(bars), len), barsback));
32362
+ return {
32363
+ plots: [{ key: "dpo", title: "DPO", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
32364
+ levels: ZERO_LEVEL
32365
+ };
32366
+ }
32367
+ };
32368
+ var rocSpec = {
32369
+ type: "rate-of-change",
32370
+ title: "Rate of Change",
32371
+ shortTitle: "ROC",
32372
+ overlay: false,
32373
+ inputs: [lengthInput(9), sourceInput(), colorInput()],
32374
+ compute: (bars, inputs) => ({
32375
+ plots: [
32376
+ {
32377
+ key: "roc",
32378
+ title: "RoC",
32379
+ values: roc(sourceValues(bars, str3(inputs, "source", "Close")), num3(inputs, "length", 9)),
32380
+ color: str3(inputs, "color", SERIES_LINE),
32381
+ width: 2
32382
+ }
32383
+ ],
32384
+ levels: ZERO_LEVEL
32385
+ })
32386
+ };
32387
+ var ultimate = {
32388
+ type: "ultimate-oscillator",
32389
+ title: "Ultimate Oscillator",
32390
+ shortTitle: "UO",
32391
+ overlay: false,
32392
+ inputs: [lengthInput(7, "fastLength", "Fast length"), lengthInput(14, "middleLength", "Middle length"), lengthInput(28, "slowLength", "Slow length"), colorInput()],
32393
+ compute: (bars, inputs) => {
32394
+ const n = bars.length;
32395
+ const bp = new Array(n).fill(Number.NaN);
32396
+ const tr = new Array(n).fill(Number.NaN);
32397
+ for (let i = 0; i < n; i++) {
32398
+ const b = bars[i];
32399
+ const pc = i > 0 ? bars[i - 1].close : b.close;
32400
+ const lo = Math.min(b.low, pc);
32401
+ const hi = Math.max(b.high, pc);
32402
+ bp[i] = b.close - lo;
32403
+ tr[i] = hi - lo;
32404
+ }
32405
+ const avg = (len) => zip(sum(bp, len), sum(tr, len), (a, b) => b === 0 ? Number.NaN : a / b);
32406
+ const a7 = avg(num3(inputs, "fastLength", 7));
32407
+ const a14 = avg(num3(inputs, "middleLength", 14));
32408
+ const a28 = avg(num3(inputs, "slowLength", 28));
32409
+ const values = a7.map((x, i) => {
32410
+ const y = a14[i];
32411
+ const z = a28[i];
32412
+ return Number.isFinite(x) && Number.isFinite(y) && Number.isFinite(z) ? 100 * (4 * x + 2 * y + z) / 7 : Number.NaN;
32413
+ });
32414
+ return {
32415
+ plots: [{ key: "uo", title: "UO", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
32416
+ levels: guideLevels(30, 70, 50)
32417
+ };
32418
+ }
32419
+ };
32420
+ var rvgi = {
32421
+ type: "relative-vigor-index",
32422
+ title: "Relative Vigor Index",
32423
+ shortTitle: "RVGI",
32424
+ overlay: false,
32425
+ inputs: [lengthInput(10)],
32426
+ compute: (bars, inputs) => {
32427
+ const len = num3(inputs, "length", 10);
32428
+ const co = swma(sub(closes(bars), opens(bars)));
32429
+ const hl = swma(sub(highs(bars), lows(bars)));
32430
+ const line = zip(sum(co, len), sum(hl, len), (a, b) => b === 0 ? 0 : a / b);
32431
+ return {
32432
+ plots: [
32433
+ { key: "rvgi", title: "RVGI", values: line, color: SERIES_LINE, width: 2 },
32434
+ { key: "signal", title: "Signal", values: swma(line), color: WARNING }
32435
+ ],
32436
+ levels: ZERO_LEVEL
32437
+ };
32438
+ }
32439
+ };
32440
+ var relVolatility = {
32441
+ type: "relative-volatility-index",
32442
+ title: "Relative Volatility Index",
32443
+ shortTitle: "RVI",
32444
+ overlay: false,
32445
+ inputs: [lengthInput(10, "stdevLength", "StdDev length"), lengthInput(14, "smoothLength", "Smoothing length"), sourceInput(), colorInput()],
32446
+ compute: (bars, inputs) => {
32447
+ const smoothLen = num3(inputs, "smoothLength", 14);
32448
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
32449
+ const dev = stdev(src, num3(inputs, "stdevLength", 10));
32450
+ const d = change(src);
32451
+ const up = rma(zip(dev, d, (s, x) => x > 0 ? s : 0), smoothLen);
32452
+ const dn = rma(zip(dev, d, (s, x) => x <= 0 ? s : 0), smoothLen);
32453
+ const values = zip(up, dn, (u, w) => u + w === 0 ? 50 : 100 * u / (u + w));
32454
+ return {
32455
+ plots: [{ key: "rvi", title: "RVI", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
32456
+ levels: guideLevels(20, 80, 50)
32457
+ };
32458
+ }
32459
+ };
32460
+ var bop = {
32461
+ type: "balance-of-power",
32462
+ title: "Balance of Power",
32463
+ shortTitle: "BOP",
32464
+ overlay: false,
32465
+ inputs: [colorInput()],
32466
+ compute: (bars, inputs) => {
32467
+ const values = bars.map((b) => b.high === b.low ? 0 : (b.close - b.open) / (b.high - b.low));
32468
+ return {
32469
+ plots: [{ key: "bop", title: "BOP", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
32470
+ levels: ZERO_LEVEL
32471
+ };
32472
+ }
32473
+ };
32474
+ var elderRay = {
32475
+ type: "elder-ray",
32476
+ title: "Elder Ray",
32477
+ overlay: false,
32478
+ inputs: [lengthInput(13)],
32479
+ compute: (bars, inputs) => {
32480
+ const basis = ema(closes(bars), num3(inputs, "length", 13));
32481
+ return {
32482
+ plots: [
32483
+ { key: "bull", title: "Bull power", values: sub(highs(bars), basis), kind: "histogram", color: BULLISH, base: 0 },
32484
+ { key: "bear", title: "Bear power", values: sub(lows(bars), basis), kind: "histogram", color: BEARISH, base: 0 }
32485
+ ],
32486
+ levels: ZERO_LEVEL
32487
+ };
32488
+ }
32489
+ };
32490
+ var ttmSqueeze = {
32491
+ type: "ttm-squeeze",
32492
+ title: "TTM Squeeze",
32493
+ overlay: false,
32494
+ inputs: [
32495
+ lengthInput(20),
32496
+ { key: "bbMult", title: "Bollinger multiplier", type: "float", defval: 2, min: 0.1, max: 50, step: 0.1 },
32497
+ { key: "kcMult", title: "Keltner multiplier", type: "float", defval: 1.5, min: 0.1, max: 50, step: 0.1 }
32498
+ ],
32499
+ compute: (bars, inputs) => {
32500
+ const len = num3(inputs, "length", 20);
32501
+ const c = closes(bars);
32502
+ const basis = sma(c, len);
32503
+ const dev = map(stdev(c, len), (x) => x * num3(inputs, "bbMult", 2));
32504
+ const kcRange = map(rma(trueRange(bars), len), (x) => x * num3(inputs, "kcMult", 1.5));
32505
+ const donchianMid = zip(highest(highs(bars), len), lowest(lows(bars), len), (h, l) => (h + l) / 2);
32506
+ const anchor = zip(donchianMid, basis, (d, b) => (d + b) / 2);
32507
+ const mom = linreg(sub(c, anchor), len);
32508
+ const squeezed = c.map((_, i) => {
32509
+ const d = dev[i];
32510
+ const r = kcRange[i];
32511
+ return Number.isFinite(d) && Number.isFinite(r) ? d < r : false;
32512
+ });
32513
+ const stateColors = squeezed.map((s, i) => Number.isFinite(mom[i]) ? s ? BEARISH : BULLISH : null);
32514
+ return {
32515
+ plots: [
32516
+ { key: "mom", title: "Momentum", values: mom, kind: "histogram", color: BULLISH, colors: histColors(mom), base: 0 },
32517
+ { key: "squeeze", title: "Squeeze", values: mom.map((x) => Number.isFinite(x) ? 0 : Number.NaN), kind: "cross", color: INFO, colors: stateColors }
32518
+ ]
32519
+ };
32520
+ }
32521
+ };
32522
+ var oscillatorSpecs = [
32523
+ rsiSpec,
32524
+ stochastic,
32525
+ stochasticRsi,
32526
+ macd,
32527
+ ppo,
32528
+ awesome,
32529
+ cci,
32530
+ williamsR,
32531
+ cmo,
32532
+ connorsRsi,
32533
+ fisher,
32534
+ trix,
32535
+ tsi,
32536
+ smiErgodic,
32537
+ stc,
32538
+ kst,
32539
+ coppock,
32540
+ dpo,
32541
+ rocSpec,
32542
+ ultimate,
32543
+ rvgi,
32544
+ relVolatility,
32545
+ bop,
32546
+ elderRay,
32547
+ ttmSqueeze
32548
+ ];
32549
+
32550
+ // src/core/native-indicators/classics/trend.ts
32551
+ var aroon = {
32552
+ type: "aroon",
32553
+ title: "Aroon",
32554
+ overlay: false,
32555
+ inputs: [lengthInput(14)],
32556
+ compute: (bars, inputs) => {
32557
+ const len = num3(inputs, "length", 14);
32558
+ const up = map(barsSinceHighest(highs(bars), len + 1), (x) => 100 * (len - x) / len);
32559
+ const down = map(barsSinceLowest(lows(bars), len + 1), (x) => 100 * (len - x) / len);
32560
+ return {
32561
+ plots: [
32562
+ { key: "up", title: "Aroon up", values: up, color: BULLISH, width: 2 },
32563
+ { key: "down", title: "Aroon down", values: down, color: BEARISH, width: 2 }
32564
+ ],
32565
+ levels: [
32566
+ { key: "upper", price: 100, color: NEUTRAL, lineStyle: "dotted" },
32567
+ { key: "lower", price: 0, color: NEUTRAL, lineStyle: "dotted" }
32568
+ ]
32569
+ };
32570
+ }
32571
+ };
32572
+ var adx = {
32573
+ type: "average-directional-index",
32574
+ title: "Average Directional Index",
32575
+ shortTitle: "ADX",
32576
+ overlay: false,
32577
+ inputs: [lengthInput(14, "diLength", "DI length"), lengthInput(14, "adxLength", "ADX smoothing")],
32578
+ compute: (bars, inputs) => {
32579
+ const diLen = num3(inputs, "diLength", 14);
32580
+ const adxLen = num3(inputs, "adxLength", 14);
32581
+ const n = bars.length;
32582
+ const plusDm = new Array(n).fill(Number.NaN);
32583
+ const minusDm = new Array(n).fill(Number.NaN);
32584
+ for (let i = 1; i < n; i++) {
32585
+ const up = bars[i].high - bars[i - 1].high;
32586
+ const dn = bars[i - 1].low - bars[i].low;
32587
+ plusDm[i] = up > dn && up > 0 ? up : 0;
32588
+ minusDm[i] = dn > up && dn > 0 ? dn : 0;
32589
+ }
32590
+ const atrLine = rma(trueRange(bars), diLen);
32591
+ const plusDi = zip(rma(plusDm, diLen), atrLine, (d, a) => a === 0 ? 0 : 100 * d / a);
32592
+ const minusDi = zip(rma(minusDm, diLen), atrLine, (d, a) => a === 0 ? 0 : 100 * d / a);
32593
+ const dx = zip(plusDi, minusDi, (p, m) => p + m === 0 ? 0 : 100 * Math.abs(p - m) / (p + m));
32594
+ return {
32595
+ plots: [
32596
+ { key: "adx", title: "ADX", values: rma(dx, adxLen), color: SERIES_LINE, width: 2 },
32597
+ { key: "plusDi", title: "+DI", values: plusDi, color: BULLISH },
32598
+ { key: "minusDi", title: "-DI", values: minusDi, color: BEARISH }
32599
+ ],
32600
+ levels: [{ key: "threshold", price: 25, color: NEUTRAL, lineStyle: "dotted" }]
32601
+ };
32602
+ }
32603
+ };
32604
+ var vortex = {
32605
+ type: "vortex-indicator",
32606
+ title: "Vortex Indicator",
32607
+ shortTitle: "VI",
32608
+ overlay: false,
32609
+ inputs: [lengthInput(14)],
32610
+ compute: (bars, inputs) => {
32611
+ const len = num3(inputs, "length", 14);
32612
+ const n = bars.length;
32613
+ const vmPlus = new Array(n).fill(Number.NaN);
32614
+ const vmMinus = new Array(n).fill(Number.NaN);
32615
+ for (let i = 1; i < n; i++) {
32616
+ vmPlus[i] = Math.abs(bars[i].high - bars[i - 1].low);
32617
+ vmMinus[i] = Math.abs(bars[i].low - bars[i - 1].high);
32618
+ }
32619
+ const trSum = sum(trueRange(bars), len);
32620
+ return {
32621
+ plots: [
32622
+ { key: "viPlus", title: "VI+", values: zip(sum(vmPlus, len), trSum, (v, t) => t === 0 ? Number.NaN : v / t), color: BULLISH, width: 2 },
32623
+ { key: "viMinus", title: "VI-", values: zip(sum(vmMinus, len), trSum, (v, t) => t === 0 ? Number.NaN : v / t), color: BEARISH, width: 2 }
32624
+ ],
32625
+ levels: [{ key: "one", price: 1, color: NEUTRAL, lineStyle: "dotted" }]
32626
+ };
32627
+ }
32628
+ };
32629
+ var trendSpecs = [aroon, adx, vortex];
32630
+
32631
+ // src/core/native-indicators/classics/volatility.ts
32632
+ var atrSpec = {
32633
+ type: "average-true-range",
32634
+ title: "Average True Range",
32635
+ shortTitle: "ATR",
32636
+ overlay: false,
32637
+ inputs: [lengthInput(14), colorInput()],
32638
+ compute: (bars, inputs) => ({
32639
+ plots: [{ key: "atr", title: "ATR", values: atr(bars, num3(inputs, "length", 14)), color: str3(inputs, "color", SERIES_LINE), width: 2 }]
32640
+ })
32641
+ };
32642
+ var historicalVolatility = {
32643
+ type: "historical-volatility",
32644
+ title: "Historical Volatility",
32645
+ shortTitle: "HV",
32646
+ overlay: false,
32647
+ inputs: [
32648
+ lengthInput(10),
32649
+ { key: "annual", title: "Periods per year", type: "int", defval: 365, min: 1, max: 1e5, step: 1, tooltip: "Annualization factor (365 for daily crypto bars, 252 for stock sessions)" },
32650
+ colorInput()
32651
+ ],
32652
+ compute: (bars, inputs) => {
32653
+ const logReturns = map(change(map(closes(bars), Math.log)), (x) => x);
32654
+ const values = map(stdev(logReturns, num3(inputs, "length", 10)), (x) => x * Math.sqrt(num3(inputs, "annual", 365)) * 100);
32655
+ return { plots: [{ key: "hv", title: "HV", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }] };
32656
+ }
32657
+ };
32658
+ var chaikinVolatility = {
32659
+ type: "chaikin-volatility",
32660
+ title: "Chaikin Volatility",
32661
+ shortTitle: "CHV",
32662
+ overlay: false,
32663
+ inputs: [lengthInput(10, "emaLength", "EMA length"), lengthInput(10, "rocLength", "RoC length"), colorInput()],
32664
+ compute: (bars, inputs) => {
32665
+ const range = ema(sub(highs(bars), lows(bars)), num3(inputs, "emaLength", 10));
32666
+ return {
32667
+ plots: [{ key: "cv", title: "Chaikin Vol", values: roc(range, num3(inputs, "rocLength", 10)), color: str3(inputs, "color", SERIES_LINE), width: 2 }],
32668
+ levels: [{ key: "zero", price: 0, color: NEUTRAL }]
32669
+ };
32670
+ }
32671
+ };
32672
+ var massIndex = {
32673
+ type: "mass-index",
32674
+ title: "Mass Index",
32675
+ shortTitle: "MI",
32676
+ overlay: false,
32677
+ inputs: [lengthInput(25), colorInput()],
32678
+ compute: (bars, inputs) => {
32679
+ const range = sub(highs(bars), lows(bars));
32680
+ const single = ema(range, 9);
32681
+ const ratio = zip(single, ema(single, 9), (a, b) => b === 0 ? Number.NaN : a / b);
32682
+ return {
32683
+ plots: [{ key: "mi", title: "Mass Index", values: sum(ratio, num3(inputs, "length", 25)), color: str3(inputs, "color", SERIES_LINE), width: 2 }],
32684
+ levels: [{ key: "reversal", price: 27, color: NEUTRAL, lineStyle: "dotted" }]
32685
+ };
32686
+ }
32687
+ };
32688
+ var standardDeviation = {
32689
+ type: "standard-deviation",
32690
+ title: "Standard Deviation",
32691
+ shortTitle: "StdDev",
32692
+ overlay: false,
32693
+ inputs: [lengthInput(20), sourceInput(), colorInput()],
32694
+ compute: (bars, inputs) => ({
32695
+ plots: [
32696
+ {
32697
+ key: "stdev",
32698
+ title: "StdDev",
32699
+ values: stdev(sourceValues(bars, str3(inputs, "source", "Close")), num3(inputs, "length", 20)),
32700
+ color: str3(inputs, "color", SERIES_LINE),
32701
+ width: 2
32702
+ }
32703
+ ]
32704
+ })
32705
+ };
32706
+ var ulcerIndex = {
32707
+ type: "ulcer-index",
32708
+ title: "Ulcer Index",
32709
+ overlay: false,
32710
+ inputs: [lengthInput(14), colorInput()],
32711
+ compute: (bars, inputs) => {
32712
+ const len = num3(inputs, "length", 14);
32713
+ const c = closes(bars);
32714
+ const drawdown = zip(c, highest(c, len), (x, h) => h === 0 ? 0 : 100 * (x - h) / h);
32715
+ const values = map(sma(map(drawdown, (d) => d * d), len), Math.sqrt);
32716
+ return { plots: [{ key: "ui", title: "Ulcer", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }] };
32717
+ }
32718
+ };
32719
+ var choppiness = {
32720
+ type: "choppiness-index",
32721
+ title: "Choppiness Index",
32722
+ shortTitle: "CHOP",
32723
+ overlay: false,
32724
+ inputs: [lengthInput(14), colorInput()],
32725
+ compute: (bars, inputs) => {
32726
+ const len = num3(inputs, "length", 14);
32727
+ const trSum = sum(trueRange(bars), len);
32728
+ const span = zip(highest(highs(bars), len), lowest(lows(bars), len), (h, l) => h - l);
32729
+ const values = zip(trSum, span, (t, s) => s > 0 && t > 0 ? 100 * Math.log10(t / s) / Math.log10(len) : Number.NaN);
32730
+ return {
32731
+ plots: [{ key: "chop", title: "CHOP", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
32732
+ levels: [
32733
+ { key: "upper", price: 61.8, color: NEUTRAL },
32734
+ { key: "lower", price: 38.2, color: NEUTRAL }
32735
+ ]
32736
+ };
32737
+ }
32738
+ };
32739
+ var volatilitySpecs = [atrSpec, historicalVolatility, chaikinVolatility, massIndex, standardDeviation, ulcerIndex, choppiness];
32740
+
32741
+ // src/core/native-indicators/classics/volume.ts
32742
+ function moneyFlowVolume(bars) {
32743
+ return bars.map((b) => {
32744
+ if (b.volume == null || !Number.isFinite(b.volume)) return Number.NaN;
32745
+ if (b.high === b.low) return 0;
32746
+ return (2 * b.close - b.high - b.low) / (b.high - b.low) * b.volume;
32747
+ });
32748
+ }
32749
+ var obv = {
32750
+ type: "on-balance-volume",
32751
+ title: "On Balance Volume",
32752
+ shortTitle: "OBV",
32753
+ overlay: false,
32754
+ inputs: [colorInput()],
32755
+ compute: (bars, inputs) => {
32756
+ const vol = volumes(bars);
32757
+ const d = change(closes(bars));
32758
+ const signed3 = zip(vol, d, (v, x) => x > 0 ? v : x < 0 ? -v : 0);
32759
+ return { plots: [{ key: "obv", title: "OBV", values: cumSum(signed3), color: str3(inputs, "color", SERIES_LINE), width: 2 }] };
32760
+ }
32761
+ };
32762
+ var accDist = {
32763
+ type: "accumulation-distribution",
32764
+ title: "Accumulation / Distribution",
32765
+ shortTitle: "A/D",
32766
+ overlay: false,
32767
+ inputs: [colorInput()],
32768
+ compute: (bars, inputs) => ({
32769
+ plots: [{ key: "ad", title: "A/D", values: cumSum(moneyFlowVolume(bars)), color: str3(inputs, "color", SERIES_LINE), width: 2 }]
32770
+ })
32771
+ };
32772
+ var cmf = {
32773
+ type: "chaikin-money-flow",
32774
+ title: "Chaikin Money Flow",
32775
+ shortTitle: "CMF",
32776
+ overlay: false,
32777
+ inputs: [lengthInput(20), colorInput()],
32778
+ compute: (bars, inputs) => {
32779
+ const len = num3(inputs, "length", 20);
32780
+ const values = zip(sum(moneyFlowVolume(bars), len), sum(volumes(bars), len), (m, v) => v === 0 ? 0 : m / v);
32781
+ return {
32782
+ plots: [{ key: "cmf", title: "CMF", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
32783
+ levels: [{ key: "zero", price: 0, color: NEUTRAL }]
32784
+ };
32785
+ }
32786
+ };
32787
+ var chaikinOscillator = {
32788
+ type: "chaikin-oscillator",
32789
+ title: "Chaikin Oscillator",
32790
+ shortTitle: "Chaikin Osc",
32791
+ overlay: false,
32792
+ inputs: [lengthInput(3, "fastLength", "Fast length"), lengthInput(10, "slowLength", "Slow length"), colorInput()],
32793
+ compute: (bars, inputs) => {
32794
+ const ad = cumSum(moneyFlowVolume(bars));
32795
+ const values = sub(ema(ad, num3(inputs, "fastLength", 3)), ema(ad, num3(inputs, "slowLength", 10)));
32796
+ return {
32797
+ plots: [{ key: "co", title: "Chaikin Osc", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
32798
+ levels: [{ key: "zero", price: 0, color: NEUTRAL }]
32799
+ };
32800
+ }
32801
+ };
32802
+ var eom = {
32803
+ type: "ease-of-movement",
32804
+ title: "Ease of Movement",
32805
+ shortTitle: "EOM",
32806
+ overlay: false,
32807
+ inputs: [lengthInput(14), { key: "divisor", title: "Divisor", type: "int", defval: 1e4, min: 1, max: 1e9, step: 1 }, colorInput()],
32808
+ compute: (bars, inputs) => {
32809
+ const div = num3(inputs, "divisor", 1e4);
32810
+ const hl2 = sourceValues(bars, "HL2");
32811
+ const move = change(hl2);
32812
+ const raw = bars.map((b, i) => {
32813
+ const m = move[i];
32814
+ if (b.volume == null || !Number.isFinite(b.volume) || b.volume === 0 || !Number.isFinite(m)) return Number.NaN;
32815
+ return div * m * (b.high - b.low) / b.volume;
32816
+ });
32817
+ return {
32818
+ plots: [{ key: "eom", title: "EOM", values: sma(raw, num3(inputs, "length", 14)), color: str3(inputs, "color", SERIES_LINE), width: 2 }],
32819
+ levels: [{ key: "zero", price: 0, color: NEUTRAL }]
32820
+ };
32821
+ }
32822
+ };
32823
+ var forceIndex = {
32824
+ type: "force-index",
32825
+ title: "Force Index",
32826
+ shortTitle: "FI",
32827
+ overlay: false,
32828
+ inputs: [lengthInput(13), colorInput()],
32829
+ compute: (bars, inputs) => {
32830
+ const raw = zip(change(closes(bars)), volumes(bars), (d, v) => d * v);
32831
+ return {
32832
+ plots: [{ key: "force", title: "Force", values: ema(raw, num3(inputs, "length", 13)), color: str3(inputs, "color", SERIES_LINE), width: 2 }],
32833
+ levels: [{ key: "zero", price: 0, color: NEUTRAL }]
32834
+ };
32835
+ }
32836
+ };
32837
+ var klinger = {
32838
+ type: "klinger-oscillator",
32839
+ title: "Klinger Oscillator",
32840
+ shortTitle: "KVO",
32841
+ overlay: false,
32842
+ inputs: [lengthInput(34, "fastLength", "Fast length"), lengthInput(55, "slowLength", "Slow length"), lengthInput(13, "signalLength", "Signal length")],
32843
+ compute: (bars, inputs) => {
32844
+ const hlc3 = sourceValues(bars, "HLC3");
32845
+ const trend = change(hlc3);
32846
+ const signedVolume = zip(volumes(bars), trend, (v, t) => t >= 0 ? v : -v);
32847
+ const line = sub(ema(signedVolume, num3(inputs, "fastLength", 34)), ema(signedVolume, num3(inputs, "slowLength", 55)));
32848
+ return {
32849
+ plots: [
32850
+ { key: "kvo", title: "KVO", values: line, color: SERIES_LINE, width: 2 },
32851
+ { key: "signal", title: "Signal", values: ema(line, num3(inputs, "signalLength", 13)), color: WARNING }
32852
+ ],
32853
+ levels: [{ key: "zero", price: 0, color: NEUTRAL }]
32854
+ };
32855
+ }
32856
+ };
32857
+ var mfi = {
32858
+ type: "money-flow-index",
32859
+ title: "Money Flow Index",
32860
+ shortTitle: "MFI",
32861
+ overlay: false,
32862
+ inputs: [lengthInput(14), colorInput()],
32863
+ compute: (bars, inputs) => {
32864
+ const len = num3(inputs, "length", 14);
32865
+ const tp = sourceValues(bars, "HLC3");
32866
+ const rawFlow = zip(tp, volumes(bars), (p, v) => p * v);
32867
+ const d = change(tp);
32868
+ const pos = sum(zip(rawFlow, d, (f, x) => x > 0 ? f : 0), len);
32869
+ const neg = sum(zip(rawFlow, d, (f, x) => x < 0 ? f : 0), len);
32870
+ const values = zip(pos, neg, (p, n) => n === 0 ? 100 : 100 - 100 / (1 + p / n));
32871
+ return {
32872
+ plots: [{ key: "mfi", title: "MFI", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
32873
+ levels: [
32874
+ { key: "upper", price: 80, color: NEUTRAL },
32875
+ { key: "lower", price: 20, color: NEUTRAL }
32876
+ ]
32877
+ };
32878
+ }
32879
+ };
32880
+ function volumeIndex(bars, when) {
32881
+ const out = new Array(bars.length).fill(Number.NaN);
32882
+ let index = 1e3;
32883
+ let started = false;
32884
+ for (let i = 1; i < bars.length; i++) {
32885
+ const v = bars[i].volume;
32886
+ const pv = bars[i - 1].volume;
32887
+ const pc = bars[i - 1].close;
32888
+ if (v == null || pv == null || !Number.isFinite(v) || !Number.isFinite(pv) || pc === 0) {
32889
+ if (started) out[i] = index;
32890
+ continue;
32891
+ }
32892
+ started = true;
32893
+ if (when(v, pv)) index += (bars[i].close - pc) / pc * index;
32894
+ out[i] = index;
32895
+ }
32896
+ return out;
32897
+ }
32898
+ var nvi = {
32899
+ type: "negative-volume-index",
32900
+ title: "Negative Volume Index",
32901
+ shortTitle: "Negative Volume Index",
32902
+ overlay: false,
32903
+ inputs: [lengthInput(255, "signalLength", "Signal length"), colorInput()],
32904
+ compute: (bars, inputs) => {
32905
+ const line = volumeIndex(bars, (v, pv) => v < pv);
32906
+ return {
32907
+ plots: [
32908
+ { key: "nvi", title: "NVI", values: line, color: str3(inputs, "color", SERIES_LINE), width: 2 },
32909
+ { key: "signal", title: "Signal", values: ema(line, num3(inputs, "signalLength", 255)), color: WARNING }
32910
+ ]
32911
+ };
32912
+ }
32913
+ };
32914
+ var pvi = {
32915
+ type: "positive-volume-index",
32916
+ title: "Positive Volume Index",
32917
+ shortTitle: "Positive Volume Index",
32918
+ overlay: false,
32919
+ inputs: [lengthInput(255, "signalLength", "Signal length"), colorInput()],
32920
+ compute: (bars, inputs) => {
32921
+ const line = volumeIndex(bars, (v, pv) => v > pv);
32922
+ return {
32923
+ plots: [
32924
+ { key: "pvi", title: "PVI", values: line, color: str3(inputs, "color", SERIES_LINE), width: 2 },
32925
+ { key: "signal", title: "Signal", values: ema(line, num3(inputs, "signalLength", 255)), color: WARNING }
32926
+ ]
32927
+ };
32928
+ }
32929
+ };
32930
+ var pvt = {
32931
+ type: "price-volume-trend",
32932
+ title: "Price Volume Trend",
32933
+ shortTitle: "PVT",
32934
+ overlay: false,
32935
+ inputs: [colorInput()],
32936
+ compute: (bars, inputs) => {
32937
+ const c = closes(bars);
32938
+ const raw = bars.map((b, i) => {
32939
+ if (i === 0 || b.volume == null || !Number.isFinite(b.volume)) return Number.NaN;
32940
+ const pc = c[i - 1];
32941
+ return pc === 0 ? 0 : (c[i] - pc) / pc * b.volume;
32942
+ });
32943
+ return { plots: [{ key: "pvt", title: "PVT", values: cumSum(raw), color: str3(inputs, "color", SERIES_LINE), width: 2 }] };
32944
+ }
32945
+ };
32946
+ var volumeOscillator = {
32947
+ type: "volume-oscillator",
32948
+ title: "Volume Oscillator",
32949
+ shortTitle: "Vol Osc",
32950
+ overlay: false,
32951
+ inputs: [lengthInput(5, "fastLength", "Fast length"), lengthInput(10, "slowLength", "Slow length"), colorInput()],
32952
+ compute: (bars, inputs) => {
32953
+ const vol = volumes(bars);
32954
+ const fast = ema(vol, num3(inputs, "fastLength", 5));
32955
+ const slow = ema(vol, num3(inputs, "slowLength", 10));
32956
+ const values = zip(fast, slow, (f, s) => s === 0 ? Number.NaN : 100 * (f - s) / s);
32957
+ return {
32958
+ plots: [{ key: "vo", title: "Volume Osc", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
32959
+ levels: [{ key: "zero", price: 0, color: NEUTRAL }]
32960
+ };
32961
+ }
32962
+ };
32963
+ var pvo = {
32964
+ type: "pvo",
32965
+ title: "Percentage Volume Oscillator",
32966
+ shortTitle: "PVO",
32967
+ overlay: false,
32968
+ inputs: [lengthInput(12, "fastLength", "Fast length"), lengthInput(26, "slowLength", "Slow length"), lengthInput(9, "signalLength", "Signal length")],
32969
+ compute: (bars, inputs) => {
32970
+ const vol = volumes(bars);
32971
+ const fast = ema(vol, num3(inputs, "fastLength", 12));
32972
+ const slow = ema(vol, num3(inputs, "slowLength", 26));
32973
+ const line = zip(fast, slow, (f, s) => s === 0 ? Number.NaN : 100 * (f - s) / s);
32974
+ const signal = ema(line, num3(inputs, "signalLength", 9));
32975
+ const hist = sub(line, signal);
32976
+ return {
32977
+ plots: [
32978
+ { key: "hist", title: "Histogram", values: hist, kind: "histogram", color: BULLISH, colors: hist.map((x) => Number.isFinite(x) ? x >= 0 ? BULLISH : BEARISH : null), base: 0 },
32979
+ { key: "pvo", title: "PVO", values: line, color: SERIES_LINE, width: 2 },
32980
+ { key: "signal", title: "Signal", values: signal, color: WARNING }
32981
+ ],
32982
+ levels: [{ key: "zero", price: 0, color: NEUTRAL }]
32983
+ };
32984
+ }
32985
+ };
32986
+ var vfi = {
32987
+ type: "volume-flow-indicator",
32988
+ title: "Volume Flow Indicator",
32989
+ shortTitle: "Volume Flow Indicator",
32990
+ overlay: false,
32991
+ inputs: [
32992
+ lengthInput(130),
32993
+ { key: "coef", title: "Cutoff coefficient", type: "float", defval: 0.2, min: 0.01, max: 10, step: 0.01 },
32994
+ { key: "volCoef", title: "Volume cap", type: "float", defval: 2.5, min: 0.1, max: 50, step: 0.1 },
32995
+ lengthInput(3, "smoothLength", "Smoothing length")
32996
+ ],
32997
+ compute: (bars, inputs) => {
32998
+ const len = num3(inputs, "length", 130);
32999
+ const coef = num3(inputs, "coef", 0.2);
33000
+ const volCoef = num3(inputs, "volCoef", 2.5);
33001
+ const typical = map(sourceValues(bars, "HLC3"), Math.log);
33002
+ const inter = change(typical);
33003
+ const vInter = stdev(inter, 30);
33004
+ const vol = volumes(bars);
33005
+ const vAve = sma(vol, len);
33006
+ const mf = change(sourceValues(bars, "HLC3"));
33007
+ const raw = bars.map((b, i) => {
33008
+ const cutoff = Number.isFinite(vInter[i]) ? coef * vInter[i] * b.close : Number.NaN;
33009
+ const v = vol[i];
33010
+ const ave = i > 0 ? vAve[i - 1] : Number.NaN;
33011
+ const m = mf[i];
33012
+ if (!Number.isFinite(cutoff) || !Number.isFinite(v) || !Number.isFinite(ave) || !Number.isFinite(m)) return Number.NaN;
33013
+ const capped = Math.min(v, ave * volCoef);
33014
+ return m > cutoff ? capped : m < -cutoff ? -capped : 0;
33015
+ });
33016
+ const values = bars.map((_, i) => {
33017
+ const ave = i > 0 ? vAve[i - 1] : Number.NaN;
33018
+ return Number.isFinite(ave) && ave !== 0 ? ave : Number.NaN;
33019
+ });
33020
+ const flowSum = sum(raw, len);
33021
+ const line = zip(flowSum, values, (f, a) => f / a);
33022
+ return {
33023
+ plots: [{ key: "vfi", title: "VFI", values: sma(line, num3(inputs, "smoothLength", 3)), color: SERIES_LINE, width: 2 }],
33024
+ levels: [{ key: "zero", price: 0, color: NEUTRAL }]
33025
+ };
33026
+ }
33027
+ };
33028
+ var intradayIntensity = {
33029
+ type: "intraday-intensity",
33030
+ title: "Intraday Intensity",
33031
+ shortTitle: "Intraday Intensity",
33032
+ overlay: false,
33033
+ inputs: [lengthInput(21), colorInput()],
33034
+ compute: (bars, inputs) => {
33035
+ const len = num3(inputs, "length", 21);
33036
+ const values = zip(sum(moneyFlowVolume(bars), len), sum(volumes(bars), len), (m, v) => v === 0 ? 0 : 100 * m / v);
33037
+ return {
33038
+ plots: [{ key: "ii", title: "II%", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
33039
+ levels: [{ key: "zero", price: 0, color: NEUTRAL }]
33040
+ };
33041
+ }
33042
+ };
33043
+ var volumeSpecs = [
33044
+ obv,
33045
+ accDist,
33046
+ cmf,
33047
+ chaikinOscillator,
33048
+ eom,
33049
+ forceIndex,
33050
+ klinger,
33051
+ mfi,
33052
+ nvi,
33053
+ pvi,
33054
+ pvt,
33055
+ volumeOscillator,
33056
+ pvo,
33057
+ vfi,
33058
+ intradayIntensity
33059
+ ];
33060
+
33061
+ // src/core/native-indicators/classics/overlays.ts
33062
+ var DAY_MS2 = 864e5;
33063
+ function periodKey(anchor, time) {
33064
+ if (anchor === "Week") return Math.floor((Math.floor(time / DAY_MS2) + 3) / 7);
33065
+ if (anchor === "Month") {
33066
+ const d = new Date(time);
33067
+ return d.getUTCFullYear() * 12 + d.getUTCMonth();
33068
+ }
33069
+ return Math.floor(time / DAY_MS2);
33070
+ }
33071
+ var parabolicSar = {
33072
+ type: "parabolic-sar",
33073
+ title: "Parabolic SAR",
33074
+ shortTitle: "SAR",
33075
+ overlay: true,
33076
+ inputs: [
33077
+ { key: "start", title: "Start", type: "float", defval: 0.02, min: 1e-3, max: 1, step: 1e-3 },
33078
+ { key: "increment", title: "Increment", type: "float", defval: 0.02, min: 1e-3, max: 1, step: 1e-3 },
33079
+ { key: "maximum", title: "Max value", type: "float", defval: 0.2, min: 0.01, max: 1, step: 0.01 }
33080
+ ],
33081
+ compute: (bars, inputs) => {
33082
+ const start = num3(inputs, "start", 0.02);
33083
+ const inc = num3(inputs, "increment", 0.02);
33084
+ const max = num3(inputs, "maximum", 0.2);
33085
+ const n = bars.length;
33086
+ const values = new Array(n).fill(Number.NaN);
33087
+ const colors = new Array(n).fill(null);
33088
+ if (n >= 2) {
33089
+ let long = bars[1].close >= bars[0].close;
33090
+ let sar = long ? bars[0].low : bars[0].high;
33091
+ let ep = long ? bars[0].high : bars[0].low;
33092
+ let af = start;
33093
+ for (let i = 1; i < n; i++) {
33094
+ const b = bars[i];
33095
+ sar += af * (ep - sar);
33096
+ if (long) sar = Math.min(sar, bars[i - 1].low, i >= 2 ? bars[i - 2].low : bars[i - 1].low);
33097
+ else sar = Math.max(sar, bars[i - 1].high, i >= 2 ? bars[i - 2].high : bars[i - 1].high);
33098
+ const reversed = long ? b.low < sar : b.high > sar;
33099
+ if (reversed) {
33100
+ long = !long;
33101
+ sar = ep;
33102
+ ep = long ? b.high : b.low;
33103
+ af = start;
33104
+ } else if (long && b.high > ep) {
33105
+ ep = b.high;
33106
+ af = Math.min(max, af + inc);
33107
+ } else if (!long && b.low < ep) {
33108
+ ep = b.low;
33109
+ af = Math.min(max, af + inc);
33110
+ }
33111
+ values[i] = sar;
33112
+ colors[i] = long ? BULLISH : BEARISH;
33113
+ }
33114
+ }
33115
+ return { plots: [{ key: "sar", title: "PSAR", values, kind: "circles", color: SERIES_LINE, colors }] };
33116
+ }
33117
+ };
33118
+ var vwap = {
33119
+ type: "vwap",
33120
+ title: "Volume Weighted Average Price",
33121
+ shortTitle: "VWAP",
33122
+ overlay: true,
33123
+ inputs: [
33124
+ { key: "anchor", title: "Anchor", type: "string", defval: "Day", options: ["Day", "Week", "Month"], tooltip: "Where the accumulation resets (UTC periods)" },
33125
+ { key: "source", title: "Source", type: "string", defval: "HLC3", options: ["HLC3", "OHLC4", "Close"] },
33126
+ colorInput(INFO)
33127
+ ],
33128
+ compute: (bars, inputs) => {
33129
+ const anchor = str3(inputs, "anchor", "Day");
33130
+ const source = str3(inputs, "source", "HLC3");
33131
+ const values = new Array(bars.length).fill(Number.NaN);
33132
+ let period = Number.NaN;
33133
+ let cumPV = 0;
33134
+ let cumV = 0;
33135
+ for (let i = 0; i < bars.length; i++) {
33136
+ const b = bars[i];
33137
+ const key = periodKey(anchor, b.time);
33138
+ if (key !== period) {
33139
+ period = key;
33140
+ cumPV = 0;
33141
+ cumV = 0;
33142
+ }
33143
+ const v = b.volume;
33144
+ if (v != null && Number.isFinite(v) && v > 0) {
33145
+ const tp = source === "Close" ? b.close : source === "OHLC4" ? (b.open + b.high + b.low + b.close) / 4 : (b.high + b.low + b.close) / 3;
33146
+ cumPV += tp * v;
33147
+ cumV += v;
33148
+ }
33149
+ if (cumV > 0) values[i] = cumPV / cumV;
33150
+ }
33151
+ return { plots: [{ key: "vwap", title: "VWAP", values, color: str3(inputs, "color", INFO), width: 2 }] };
33152
+ }
33153
+ };
33154
+ var pivotPoints = {
33155
+ type: "pivot-points",
33156
+ title: "Pivot Points",
33157
+ shortTitle: "Pivots",
33158
+ overlay: true,
33159
+ inputs: [{ key: "anchor", title: "Period", type: "string", defval: "Day", options: ["Day", "Week", "Month"] }],
33160
+ compute: (bars, inputs) => {
33161
+ const anchor = str3(inputs, "anchor", "Day");
33162
+ const aggs = /* @__PURE__ */ new Map();
33163
+ for (const b of bars) {
33164
+ const key = periodKey(anchor, b.time);
33165
+ const a = aggs.get(key);
33166
+ if (!a) aggs.set(key, { high: b.high, low: b.low, close: b.close });
33167
+ else {
33168
+ a.high = Math.max(a.high, b.high);
33169
+ a.low = Math.min(a.low, b.low);
33170
+ a.close = b.close;
33171
+ }
33172
+ }
33173
+ const n = bars.length;
33174
+ const mk = () => new Array(n).fill(Number.NaN);
33175
+ const levels = { p: mk(), r1: mk(), s1: mk(), r2: mk(), s2: mk(), r3: mk(), s3: mk() };
33176
+ for (let i = 0; i < n; i++) {
33177
+ const prev2 = aggs.get(periodKey(anchor, bars[i].time) - 1);
33178
+ if (!prev2) continue;
33179
+ const p = (prev2.high + prev2.low + prev2.close) / 3;
33180
+ levels.p[i] = p;
33181
+ levels.r1[i] = 2 * p - prev2.low;
33182
+ levels.s1[i] = 2 * p - prev2.high;
33183
+ levels.r2[i] = p + (prev2.high - prev2.low);
33184
+ levels.s2[i] = p - (prev2.high - prev2.low);
33185
+ levels.r3[i] = prev2.high + 2 * (p - prev2.low);
33186
+ levels.s3[i] = prev2.low - 2 * (prev2.high - p);
33187
+ }
33188
+ const plot = (key, title, color) => ({ key, title, kind: "step", values: levels[key], color });
33189
+ return {
33190
+ plots: [
33191
+ plot("p", "P", WARNING),
33192
+ plot("r1", "R1", BEARISH),
33193
+ plot("s1", "S1", BULLISH),
33194
+ plot("r2", "R2", BEARISH),
33195
+ plot("s2", "S2", BULLISH),
33196
+ plot("r3", "R3", BEARISH),
33197
+ plot("s3", "S3", BULLISH)
33198
+ ]
33199
+ };
33200
+ }
33201
+ };
33202
+ var fiftyTwoWeek = {
33203
+ type: "52-week-high-low",
33204
+ title: "52 Week High/Low",
33205
+ shortTitle: "52W H/L",
33206
+ overlay: true,
33207
+ inputs: [{ key: "weeks", title: "Weeks", type: "int", defval: 52, min: 1, max: 520, step: 1 }],
33208
+ compute: (bars, inputs) => {
33209
+ const span = num3(inputs, "weeks", 52) * 7 * DAY_MS2;
33210
+ const n = bars.length;
33211
+ const hi = new Array(n).fill(Number.NaN);
33212
+ const lo = new Array(n).fill(Number.NaN);
33213
+ const maxIdx = [];
33214
+ const minIdx = [];
33215
+ let from = 0;
33216
+ for (let i = 0; i < n; i++) {
33217
+ const b = bars[i];
33218
+ while (maxIdx.length > 0 && bars[maxIdx[maxIdx.length - 1]].high <= b.high) maxIdx.pop();
33219
+ maxIdx.push(i);
33220
+ while (minIdx.length > 0 && bars[minIdx[minIdx.length - 1]].low >= b.low) minIdx.pop();
33221
+ minIdx.push(i);
33222
+ while (bars[from].time < b.time - span) from += 1;
33223
+ while (maxIdx[0] < from) maxIdx.shift();
33224
+ while (minIdx[0] < from) minIdx.shift();
33225
+ hi[i] = bars[maxIdx[0]].high;
33226
+ lo[i] = bars[minIdx[0]].low;
33227
+ }
33228
+ return {
33229
+ plots: [
33230
+ { key: "high", title: "52W high", values: hi, kind: "step", color: BULLISH },
33231
+ { key: "low", title: "52W low", values: lo, kind: "step", color: BEARISH }
33232
+ ]
33233
+ };
33234
+ }
33235
+ };
33236
+ var zigzag = {
33237
+ type: "zigzag",
33238
+ title: "ZigZag",
33239
+ overlay: true,
33240
+ inputs: [
33241
+ { key: "deviation", title: "Deviation %", type: "float", defval: 5, min: 0.01, max: 100, step: 0.1 },
33242
+ { key: "depth", title: "Depth", type: "int", defval: 10, min: 1, max: 500, step: 1 },
33243
+ colorInput()
33244
+ ],
33245
+ compute: (bars, inputs) => {
33246
+ const dev = num3(inputs, "deviation", 5) / 100;
33247
+ const depth = num3(inputs, "depth", 10);
33248
+ const pivots = [];
33249
+ if (bars.length > 1) {
33250
+ let up = bars[1].close >= bars[0].close;
33251
+ let ext = up ? { i: 0, price: bars[0].high } : { i: 0, price: bars[0].low };
33252
+ for (let i = 1; i < bars.length; i++) {
33253
+ const b = bars[i];
33254
+ if (up) {
33255
+ if (b.high >= ext.price) ext = { i, price: b.high };
33256
+ else if (b.low <= ext.price * (1 - dev) && i - (pivots[pivots.length - 1]?.i ?? -depth) >= depth) {
33257
+ pivots.push(ext);
33258
+ up = false;
33259
+ ext = { i, price: b.low };
33260
+ }
33261
+ } else if (b.low <= ext.price) {
33262
+ ext = { i, price: b.low };
33263
+ } else if (b.high >= ext.price * (1 + dev) && i - (pivots[pivots.length - 1]?.i ?? -depth) >= depth) {
33264
+ pivots.push(ext);
33265
+ up = true;
33266
+ ext = { i, price: b.high };
33267
+ }
33268
+ }
33269
+ pivots.push(ext);
33270
+ }
33271
+ return {
33272
+ plots: [],
33273
+ polylines: [
33274
+ {
33275
+ key: "zigzag",
33276
+ points: pivots.map((p) => ({ xloc: "bar_time", x: bars[p.i].time, price: p.price })),
33277
+ curved: false,
33278
+ closed: false,
33279
+ lineColor: str3(inputs, "color", SERIES_LINE),
33280
+ lineWidth: 2,
33281
+ lineStyle: "solid",
33282
+ arrowLeft: false,
33283
+ arrowRight: false
33284
+ }
33285
+ ]
33286
+ };
33287
+ }
33288
+ };
33289
+ var williamsFractal = {
33290
+ type: "williams-fractal",
33291
+ title: "Williams Fractal",
33292
+ shortTitle: "Fractals",
33293
+ overlay: true,
33294
+ inputs: [{ key: "periods", title: "Periods", type: "int", defval: 2, min: 1, max: 50, step: 1 }],
33295
+ compute: (bars, inputs) => {
33296
+ const p = num3(inputs, "periods", 2);
33297
+ const markers = [];
33298
+ const isExtreme = (i, pick, better) => {
33299
+ const v = pick(bars[i]);
33300
+ for (let k = i - p; k <= i + p; k++) {
33301
+ if (k === i) continue;
33302
+ if (!better(v, pick(bars[k]))) return false;
33303
+ }
33304
+ return true;
33305
+ };
33306
+ for (let i = p; i < bars.length - p; i++) {
33307
+ if (isExtreme(i, (b) => b.high, (a, b) => a >= b)) {
33308
+ markers.push({ time: bars[i].time, position: "aboveBar", shape: "triangleup", color: BULLISH });
33309
+ }
33310
+ if (isExtreme(i, (b) => b.low, (a, b) => a <= b)) {
33311
+ markers.push({ time: bars[i].time, position: "belowBar", shape: "triangledown", color: BEARISH });
33312
+ }
33313
+ }
33314
+ return { plots: [], markers };
33315
+ }
33316
+ };
33317
+ var overlaySpecs = [parabolicSar, vwap, pivotPoints, fiftyTwoWeek, zigzag, williamsFractal];
33318
+
33319
+ // src/core/native-indicators/classics/index.ts
33320
+ var classicSpecs = [
33321
+ ...averageSpecs,
33322
+ ...bandSpecs,
33323
+ ...oscillatorSpecs,
33324
+ ...trendSpecs,
33325
+ ...volatilitySpecs,
33326
+ ...volumeSpecs,
33327
+ ...overlaySpecs
33328
+ ];
33329
+ function registerClassicIndicators() {
33330
+ for (const spec of classicSpecs) registerNativeIndicator(classicDescriptor(spec));
33331
+ }
33332
+
33333
+ // src/Vela.ts
33334
+ var Vela = class {
33335
+ constructor(container, options = {}, deps = {}) {
33336
+ registerBuiltinChartTypes();
33337
+ registerVolume();
33338
+ registerVpvr();
33339
+ registerClassicIndicators();
33340
+ const element = resolveElement(container);
33341
+ const theme = resolveTheme(options.theme);
33342
+ let animZoom = true;
33343
+ let animPan = true;
33344
+ if (typeof options.animations === "boolean") {
33345
+ animZoom = options.animations;
33346
+ animPan = options.animations;
33347
+ } else if (options.animations) {
33348
+ animZoom = options.animations.zoom ?? true;
33349
+ animPan = options.animations.pan ?? true;
33350
+ }
33351
+ const display = {
33352
+ currentPriceLine: options.currentPriceLine ?? true,
33353
+ logScale: options.logScale ?? false,
33354
+ nativeBackend: options.nativeBackend ?? "auto",
33355
+ animZoom,
33356
+ animPan,
33357
+ glow: options.glow ?? 0,
33358
+ upColor: options.upColor ?? BULLISH,
33359
+ downColor: options.downColor ?? BEARISH,
33360
+ priceStyle: options.priceStyle ?? "candles"
33361
+ };
33362
+ const RendererClass = options.renderer ?? NativeRenderer;
33363
+ if (typeof RendererClass !== "function") {
33364
+ throw new Error(
33365
+ "[vela] options.renderer must be a renderer class, e.g. import { LwcRenderer } from 'vela/renderers/lwc'. The 'lwc'/'native' string options were removed."
33366
+ );
33367
+ }
33368
+ const renderer = deps.renderer ?? new RendererClass(display);
33369
+ const engines = deps.engines ?? [];
33370
+ const feed = deps.dataFeed ?? new MultiProviderFeed();
33371
+ const config = {
33372
+ market: {
33373
+ symbol: options.symbol,
33374
+ timeframe: options.timeframe,
33375
+ bars: options.bars,
33376
+ session: options.session,
33377
+ visibleRange: options.visibleRange,
33378
+ data: options.data
33379
+ },
33380
+ live: options.live ?? false,
33381
+ theme,
33382
+ defaultLanguage: options.defaultLanguage ?? engines[0]?.language ?? "pine",
33383
+ drawings: options.drawings,
33384
+ volume: options.volume ?? true
33385
+ // the volume indicator is on by default
33386
+ };
33387
+ this.rendererControl = new RendererControl(renderer);
33388
+ if (options.settings) this.rendererControl.setSettingsVisibility(options.settings);
33389
+ this.dataControl = new DataControl(feed);
33390
+ this.orchestrator = new EngineOrchestrator(element, renderer, feed, engines, config, this.dataControl);
33391
+ const defaults2 = rendererDefaults();
33392
+ if (Object.keys(defaults2).length > 0) this.rendererControl.set(defaults2);
33393
+ this.panesControl = new PanesControl(this.orchestrator);
33394
+ this.drawingsControl = new DrawingsControl(this.orchestrator.drawings);
33395
+ }
33396
+ /**
33397
+ * Register a scripting engine so `addIndicator({ language })` can run that
33398
+ * language. Vela ships NO engine — install the one you need (Pine Script:
33399
+ * `@luxalgo/vela-pinets`) and register it, e.g.
33400
+ * `chart.registerEngine('pine', new PineEngine())`; without one the chart
33401
+ * displays candles, drawings and native indicators only. Re-registering a
33402
+ * language replaces it.
33403
+ */
33404
+ registerEngine(language, engine) {
33405
+ this.orchestrator.registerEngine(language, engine);
33406
+ return this;
33407
+ }
33408
+ /** Run an indicator script on the chart's market data and render it. */
33409
+ addIndicator(source, options) {
33410
+ return this.orchestrator.addIndicator(source, options);
33411
+ }
33412
+ /**
33413
+ * Add a built-in NATIVE indicator (core-computed, no scripting engine) by registered `type` —
33414
+ * e.g. `'vpvr'` or `'sma'`. It becomes a first-class indicator (legend row, settings, hide,
33415
+ * remove, events). Layer-backed types (`'volume'`, `'vpvr'`) are single-instance — a second
33416
+ * call returns the existing handle; the classic studies allow several instances, so each call
33417
+ * adds one more. Returns a fail-soft handle for an unregistered type. Native renderer only.
33418
+ */
33419
+ addNativeIndicator(type, options) {
33420
+ return this.orchestrator.addNativeIndicator(type, options);
33421
+ }
33422
+ /**
33423
+ * The catalog of built-in native indicators with their live state on this chart — each entry's
33424
+ * `type`, `title`, whether it `supported`s the current symbol, whether it's already `present`,
33425
+ * and whether it allows several instances (`multiInstance`; otherwise a second
33426
+ * `addNativeIndicator` is a no-op). Lets a host "add indicator" UI list them, gate unsupported
33427
+ * ones, and de-duplicate the single-instance ones. Async because support may probe the
33428
+ * provider (a type may need data the symbol lacks).
33429
+ */
33430
+ availableNativeIndicators() {
33431
+ return this.orchestrator.availableNativeIndicators();
33432
+ }
33433
+ /**
33434
+ * The native-indicator types PRESENT on the chart right now, one entry per instance (a
33435
+ * multi-instance type repeats) — the synchronous slice of
33436
+ * {@link availableNativeIndicators} (only support probing is async; presence never is).
33437
+ * Persistence snapshots read this: an unload-time flush must see an add/remove that
33438
+ * happened microseconds ago, which an async catalog mirror cannot guarantee.
33439
+ */
33440
+ presentNativeIndicators() {
33441
+ return this.orchestrator.presentNativeIndicators();
33442
+ }
33443
+ /** Live handles of every indicator currently on the chart (script + native) — drive
33444
+ * host panels (object trees, indicator lists) with per-id visibility/removal. */
33445
+ indicators() {
33446
+ return this.orchestrator.listIndicators();
33447
+ }
33448
+ /**
33449
+ * Execute an indicator script and INJECT it only if the run succeeds — the seam for
33450
+ * host editors and consoles. Resolves `{ ok: true, handle }` after the first
33451
+ * successful evaluation, or `{ ok: false, error }` after a compile/runtime failure —
33452
+ * in which case the failed indicator is removed again (no dead legend row).
33453
+ * Never rejects.
33454
+ */
33455
+ /**
33456
+ * Execute a script and resolve its FIRST computed run — the data-out door for host
33457
+ * editors, consoles and dashboards. The script is injected only if it runs (a failure
33458
+ * removes it again, leaving no dead legend row), and the result carries the run itself
33459
+ * plus the controls for what it put on the chart: `onUpdate` to follow later runs,
33460
+ * `remove` to take it off. Never rejects.
33461
+ *
33462
+ * `runScript` is `runIndicator` with the run as its payload rather than a handle to go
33463
+ * fetch from — the same relationship `script:run` has to `context:changed`.
33464
+ */
33465
+ runScript(source, options) {
33466
+ const handle = this.addIndicator(source, options);
33467
+ const updates = /* @__PURE__ */ new Set();
33468
+ const drop = () => {
33469
+ try {
33470
+ handle.remove();
33471
+ } catch {
33472
+ }
33473
+ };
33474
+ return new Promise((resolve) => {
33475
+ let settled = false;
33476
+ const offRun = this.on("script:run", (run) => {
33477
+ if (run.id !== handle.id) return;
33478
+ if (settled) {
33479
+ for (const handler of updates) handler(run);
33480
+ return;
33481
+ }
33482
+ settled = true;
33483
+ offError();
33484
+ resolve({
33485
+ ok: true,
33486
+ run,
33487
+ error: null,
33488
+ onUpdate: (handler) => {
33489
+ updates.add(handler);
33490
+ return () => updates.delete(handler);
33491
+ },
33492
+ remove: () => {
33493
+ offRun();
33494
+ updates.clear();
33495
+ drop();
33496
+ }
33497
+ });
33498
+ });
33499
+ const offError = handle.on("error", ({ error }) => {
33500
+ if (settled) return;
33501
+ settled = true;
33502
+ offRun();
33503
+ offError();
33504
+ drop();
33505
+ resolve({ ok: false, run: null, error, onUpdate: () => () => void 0, remove: () => void 0 });
33506
+ });
33507
+ });
33508
+ }
33509
+ runIndicator(source, options) {
33510
+ const handle = this.addIndicator(source, options);
33511
+ return new Promise((resolve) => {
33512
+ const offReady = handle.on("ready", () => {
33513
+ offReady();
33514
+ offError();
33515
+ resolve({ ok: true, handle, error: null, context: null });
33516
+ });
33517
+ const offError = handle.on("error", ({ error }) => {
33518
+ offReady();
33519
+ offError();
33520
+ void handle.context().catch(() => null).then((context) => {
33521
+ try {
33522
+ handle.remove();
33523
+ } catch {
33524
+ }
33525
+ resolve({ ok: false, handle: null, error, context });
33526
+ });
33527
+ });
33528
+ });
33529
+ }
33530
+ /**
33531
+ * Switch the chart's market IN PLACE — symbol, provider, timeframe, depth, or offline
33532
+ * data — WITHOUT destroying the chart: indicators re-execute over the new bars, native
33533
+ * indicators restart, and panes, user drawings, renderer config and event
33534
+ * subscriptions all survive. Resolves once the new market's history is painted (a
33535
+ * deep backfill continues behind it — await {@link historyComplete}); a call
33536
+ * superseded by a newer `setMarket` resolves silently. Emits `market:changed`
33537
+ * (with the previous identity) when the market identity changed.
33538
+ */
33539
+ setMarket(next) {
33540
+ return this.orchestrator.setMarket(next);
33541
+ }
33542
+ /** The current market identity — the read counterpart of {@link setMarket}. A snapshot
33543
+ * of the REQUESTED market: it reflects an in-flight switch immediately (before the
33544
+ * new bars land). Listen to `market:changed` for committed identity changes. */
33545
+ get market() {
33546
+ return this.orchestrator.marketSnapshot();
33547
+ }
33548
+ /** Resolves once the chart is painted and interactive. For a symbol-backed chart this
33549
+ * awaits a provider being registered that resolves the symbol (the parked load). On a
33550
+ * ranged feed the first paint is a small recent head (~200 bars) and the rest of the
31217
33551
  * history keeps backfilling BEHIND this — await {@link historyComplete} for the full
31218
33552
  * depth. Distinct from `chart.data.ready()`, which awaits only the provider symbol
31219
33553
  * indexes. */
@@ -31465,12 +33799,6 @@ function clampPanelWidth(px, min = DEFAULT_PANEL_MIN_WIDTH, max = DEFAULT_PANEL_
31465
33799
  return Math.round(Math.min(Math.max(px, lo), hi));
31466
33800
  }
31467
33801
 
31468
- // src/core/model/identity.ts
31469
- function stableSeriesId(parts) {
31470
- const normTitle = parts.title.trim().toLowerCase().replace(/\s+/g, "-");
31471
- return `${parts.instanceId}:${parts.kind}:${normTitle}#${parts.ordinal}`;
31472
- }
31473
-
31474
33802
  exports.ACCENT = ACCENT;
31475
33803
  exports.ACCENT_BRIGHT = ACCENT_BRIGHT;
31476
33804
  exports.BEARISH = BEARISH;