@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
@@ -1,6 +1,6 @@
1
- import { deserializeDrawing, chartTypes, rendererLayers, foldBaseModulation, registerChartType, rendererDefaults, getDrawingType, settingsRowVisible, normalizeSettingsRow, Magnifier, TextLabel, Callout, resetDrawingSettings, registerNativeIndicator, chartType, getNativeIndicator, nativeIndicatorDescriptors, drawingTypes, settingsRowValueKeys, formatDuration, RegressionChannel, FixedRangeVolumeProfile, SegmentDrawing, ArrowMark, GlyphStamp, RadialFib, FibSpiral, DedekindTessellation, MachFigure, GannSquare, FibRatios, MeasureBox, PositionTool, AnchoredVwap, PatternDrawing, Comment, PriceNote, Signpost, Note, PriceLabel, GANN_SQUARE_ARCS, magnifierTimeframeLabel, lineSegmentIntersection, DEFAULT_DRAWING_COLOR, MAGNIFIER_TIMEFRAME_OPTIONS, GLYPH_OPTIONS, STAMP_SIZE_OPTIONS, LINE_STYLE_OPTIONS, DEDEKIND_CURVATURE_OPTIONS, MACH_NUMBER_OPTIONS, MACH_WAVE_COUNT_OPTIONS, TEXT_SIZE_OPTIONS, createDrawing, inputVisible, tickerModifierIds, CalloutBase, DIRECTION_OPTIONS } from './chunk-32A7KHFM.js';
2
- import { themeTokens, Dialog, closeOpenPopovers, closeWidthPopover, fieldSection, buildFieldControl, fieldSeparator, fieldGrid, fieldRow, blendOver, splitColor, CalloutBubble, Menu, TextArea, Popover, NumberInput, buildColorPicker, isPopoverOpen, eventDismissedPopover, toggleSelectList, openPopoverTrigger, fieldGridColumns, FIELD_GAP_PX, STATIC_TOKENS } from './chunk-MWDNPGU3.js';
3
- import { icon, iconAt, withAlpha, WARNING, ACCENT, NEUTRAL, BEARISH, BULLISH, isDarkColor, SERIES_LINE, CHIP_PLATE, CROSSHAIR, TRADE_EXIT, TRADE_SHORT, TRADE_LONG, overlayScrollbarCss, SLATE_DEEP, VALID, INVALID, SLATE, FIELD_FOCUS_CSS, FIELD_FOCUS_RING, svg24, svg24Solid } from './chunk-6DPAYEGO.js';
1
+ import { deserializeDrawing, chartTypes, rendererLayers, foldBaseModulation, registerChartType, rendererDefaults, getDrawingType, settingsRowVisible, normalizeSettingsRow, Magnifier, TextLabel, Callout, resetDrawingSettings, registerNativeIndicator, chartType, getNativeIndicator, nativeIndicatorDescriptors, drawingTypes, settingsRowValueKeys, formatDuration, RegressionChannel, FixedRangeVolumeProfile, SegmentDrawing, ArrowMark, GlyphStamp, RadialFib, FibSpiral, DedekindTessellation, MachFigure, GannSquare, FibRatios, MeasureBox, PositionTool, AnchoredVwap, PatternDrawing, Comment, PriceNote, Signpost, Note, PriceLabel, GANN_SQUARE_ARCS, magnifierTimeframeLabel, lineSegmentIntersection, DEFAULT_DRAWING_COLOR, MAGNIFIER_TIMEFRAME_OPTIONS, GLYPH_OPTIONS, STAMP_SIZE_OPTIONS, LINE_STYLE_OPTIONS, DEDEKIND_CURVATURE_OPTIONS, MACH_NUMBER_OPTIONS, MACH_WAVE_COUNT_OPTIONS, TEXT_SIZE_OPTIONS, createDrawing, inputVisible, tickerModifierIds, CalloutBase, DIRECTION_OPTIONS, stableSeriesId } from './chunk-I77F2SVE.js';
2
+ import { themeTokens, Dialog, closeOpenPopovers, closeWidthPopover, fieldSection, buildFieldControl, fieldSeparator, fieldGrid, fieldRow, blendOver, splitColor, CalloutBubble, Menu, TextArea, Popover, NumberInput, buildColorPicker, isPopoverOpen, eventDismissedPopover, toggleSelectList, openPopoverTrigger, fieldGridColumns, FIELD_GAP_PX, STATIC_TOKENS } from './chunk-MGU3USTP.js';
3
+ import { icon, iconAt, withAlpha, WARNING, ACCENT, NEUTRAL, BEARISH, BULLISH, isDarkColor, SERIES_LINE, CHIP_PLATE, CROSSHAIR, TRADE_EXIT, TRADE_SHORT, TRADE_LONG, overlayScrollbarCss, SLATE_DEEP, VALID, INVALID, SLATE, FIELD_FOCUS_CSS, FIELD_FOCUS_RING, INFO, svg24, svg24Solid } from './chunk-6DPAYEGO.js';
4
4
 
5
5
  // src/core/events/EventBus.ts
6
6
  var TypedEventBus = class {
@@ -1136,7 +1136,7 @@ function attachForming(closed, forming) {
1136
1136
 
1137
1137
  // src/data/MultiProviderFeed.ts
1138
1138
  function intervalMs(timeframe) {
1139
- const map = {
1139
+ const map2 = {
1140
1140
  "1m": 6e4,
1141
1141
  "3m": 18e4,
1142
1142
  "5m": 3e5,
@@ -1158,7 +1158,7 @@ function intervalMs(timeframe) {
1158
1158
  W: 6048e5,
1159
1159
  M: 2592e6
1160
1160
  };
1161
- return map[timeframe] ?? 36e5;
1161
+ return map2[timeframe] ?? 36e5;
1162
1162
  }
1163
1163
  var MultiProviderFeed = class {
1164
1164
  constructor(store = sharedBarStore) {
@@ -1545,8 +1545,8 @@ var DrawingsControl = class {
1545
1545
  /** Show per-tool shortcut hints in the toolbar flyouts. Values are PRE-FORMATTED
1546
1546
  * display strings (e.g. `'Alt+T'`, `'⌥T'`) — the host owns the keymap and the
1547
1547
  * platform formatting, so hints always match the host's actual bindings. */
1548
- setToolShortcuts(map) {
1549
- if (this.ok("setToolShortcuts")) this.ctrl.setToolShortcuts(map);
1548
+ setToolShortcuts(map2) {
1549
+ if (this.ok("setToolShortcuts")) this.ctrl.setToolShortcuts(map2);
1550
1550
  return this;
1551
1551
  }
1552
1552
  /** Display another chart's in-progress placement as a GHOST at reduced opacity
@@ -1987,8 +1987,8 @@ var DrawingController = class {
1987
1987
  this.port?.setExternalGhost?.(doc);
1988
1988
  }
1989
1989
  /** Push per-tool shortcut hints (pre-formatted display strings) to the toolbar flyouts. */
1990
- setToolShortcuts(map) {
1991
- this.port?.setToolShortcuts?.(map);
1990
+ setToolShortcuts(map2) {
1991
+ this.port?.setToolShortcuts?.(map2);
1992
1992
  }
1993
1993
  // ── programmatic CRUD (facade-facing) ── each is one undo step
1994
1994
  add(type, init = {}) {
@@ -2475,8 +2475,8 @@ var DrawingToolbar = class {
2475
2475
  }
2476
2476
  /** Per-tool shortcut hints (pre-formatted display strings, e.g. `'Alt+T'`) shown at the
2477
2477
  * right edge of the flyout rows, beside the favorite star. */
2478
- setShortcuts(map) {
2479
- this.shortcuts = new Map(Object.entries(map));
2478
+ setShortcuts(map2) {
2479
+ this.shortcuts = new Map(Object.entries(map2));
2480
2480
  }
2481
2481
  /** Paint one star for the current favorite state (filled + gold, or outline). */
2482
2482
  paintStar(el, type) {
@@ -4327,8 +4327,8 @@ var InputsUI = class {
4327
4327
  lg.style.top = `${top}px`;
4328
4328
  }
4329
4329
  /** Set which panes are collapsed and, for each, the master indicator to keep in its strip. */
4330
- setCollapsedPanes(map) {
4331
- this.paneCollapse = map;
4330
+ setCollapsedPanes(map2) {
4331
+ this.paneCollapse = map2;
4332
4332
  }
4333
4333
  /**
4334
4334
  * Keep the fold toggle in step with the indicator count: with 2+ indicators anywhere
@@ -5291,9 +5291,9 @@ function factoryResetConfig(factory) {
5291
5291
  if (inst.enableKey) defaults[inst.enableKey] = false;
5292
5292
  addRows(inst.rows);
5293
5293
  }
5294
- for (const sub of section.subsections ?? []) {
5295
- if (sub.enableKey) defaults[sub.enableKey] = false;
5296
- addRows(sub.rows);
5294
+ for (const sub2 of section.subsections ?? []) {
5295
+ if (sub2.enableKey) defaults[sub2.enableKey] = false;
5296
+ addRows(sub2.rows);
5297
5297
  }
5298
5298
  if (!section.instances) addRows(section.rows);
5299
5299
  bag[t.id] = defaults;
@@ -6622,11 +6622,11 @@ function mergeBarColors(indicators) {
6622
6622
  }
6623
6623
  }
6624
6624
  if (!any) return EMPTY_BARCOLORS;
6625
- const map = /* @__PURE__ */ new Map();
6625
+ const map2 = /* @__PURE__ */ new Map();
6626
6626
  for (const m of indicators.values()) {
6627
- if (m.barColors) for (const bc of m.barColors) map.set(bc.time, bc.color);
6627
+ if (m.barColors) for (const bc of m.barColors) map2.set(bc.time, bc.color);
6628
6628
  }
6629
- return map;
6629
+ return map2;
6630
6630
  }
6631
6631
 
6632
6632
  // src/renderers/native/core/ViewportState.ts
@@ -6923,9 +6923,9 @@ function isHorizontalWheel(deltaX, deltaY) {
6923
6923
  function wheelPanRightOffset(rightOffset, deltaX, barSpacing) {
6924
6924
  return rightOffset + deltaX / barSpacing;
6925
6925
  }
6926
- function wheelPanDelta(deltaX, deltaY, shift) {
6926
+ function wheelPanDelta(deltaX, deltaY, shift2) {
6927
6927
  if (isHorizontalWheel(deltaX, deltaY)) return deltaX;
6928
- if (shift) return deltaY;
6928
+ if (shift2) return deltaY;
6929
6929
  return null;
6930
6930
  }
6931
6931
  function effectiveSnapMode(momentaryOverride, sticky) {
@@ -8547,11 +8547,11 @@ function mergeBarColors2(indicators) {
8547
8547
  }
8548
8548
  }
8549
8549
  if (!any) return EMPTY_BARCOLORS2;
8550
- const map = /* @__PURE__ */ new Map();
8550
+ const map2 = /* @__PURE__ */ new Map();
8551
8551
  for (const m of indicators.values()) {
8552
- if (m.barColors) for (const bc of m.barColors) map.set(bc.time, bc.color);
8552
+ if (m.barColors) for (const bc of m.barColors) map2.set(bc.time, bc.color);
8553
8553
  }
8554
- return map;
8554
+ return map2;
8555
8555
  }
8556
8556
 
8557
8557
  // src/renderers/shared/drawing-geometry.ts
@@ -10190,9 +10190,9 @@ function settingsIdCatalog(hostSections) {
10190
10190
  };
10191
10191
  if (section.rows) addRows(section.rows);
10192
10192
  for (const inst of section.instances ?? []) addRows(inst.rows);
10193
- for (const sub of section.subsections ?? []) {
10194
- ids.add(`${scope}.${settingsIdSlug(sub.title)}`);
10195
- addRows(sub.rows);
10193
+ for (const sub2 of section.subsections ?? []) {
10194
+ ids.add(`${scope}.${settingsIdSlug(sub2.title)}`);
10195
+ addRows(sub2.rows);
10196
10196
  }
10197
10197
  }
10198
10198
  for (const hs of hostSections) {
@@ -10751,14 +10751,14 @@ var SettingsDialog = class {
10751
10751
  };
10752
10752
  if (section.rows) seed(section.rows);
10753
10753
  for (const inst of section.instances ?? []) seed(inst.rows);
10754
- for (const sub of section.subsections ?? []) seed(sub.rows);
10754
+ for (const sub2 of section.subsections ?? []) seed(sub2.rows);
10755
10755
  for (const inst of section.instances ?? []) {
10756
10756
  if (!inst.enableKey || inst.enableKey in bag) continue;
10757
10757
  bag[inst.enableKey] = values[inst.enableKey] === true;
10758
10758
  }
10759
- for (const sub of section.subsections ?? []) {
10760
- if (!sub.enableKey || sub.enableKey in bag) continue;
10761
- bag[sub.enableKey] = values[sub.enableKey] === true;
10759
+ for (const sub2 of section.subsections ?? []) {
10760
+ if (!sub2.enableKey || sub2.enableKey in bag) continue;
10761
+ bag[sub2.enableKey] = values[sub2.enableKey] === true;
10762
10762
  }
10763
10763
  const refreshers = [];
10764
10764
  const put = (key, v) => {
@@ -10775,16 +10775,16 @@ var SettingsDialog = class {
10775
10775
  if (section.layout === "grouped") body.append(this.groupedRows(`${typeId}/rows`, rows, bag, put, refreshers));
10776
10776
  else this.flatTypeRows(rows, bag, put, refreshers, body);
10777
10777
  }
10778
- for (const sub of section.subsections ?? []) {
10779
- if (settingsIdHidden(`${scope}.${settingsIdSlug(sub.title)}`, this.hiddenSettings)) continue;
10780
- const rows = filterHiddenRows(sub.rows, scope, this.hiddenSettings);
10778
+ for (const sub2 of section.subsections ?? []) {
10779
+ if (settingsIdHidden(`${scope}.${settingsIdSlug(sub2.title)}`, this.hiddenSettings)) continue;
10780
+ const rows = filterHiddenRows(sub2.rows, scope, this.hiddenSettings);
10781
10781
  if (rows.length === 0) continue;
10782
- const subMarker = this.section(sub.title);
10782
+ const subMarker = this.section(sub2.title);
10783
10783
  subMarker.dataset.sdStyle = typeId;
10784
10784
  subMarker.dataset.sdVisibility = section.visibility ?? "active";
10785
10785
  subMarker.dataset.sdSub = "1";
10786
10786
  body.append(subMarker);
10787
- body.append(this.groupedRows(`${typeId}/${sub.title}`, rows, bag, put, refreshers, sub.enableKey));
10787
+ body.append(this.groupedRows(`${typeId}/${sub2.title}`, rows, bag, put, refreshers, sub2.enableKey));
10788
10788
  }
10789
10789
  for (const r of refreshers) r();
10790
10790
  }
@@ -12891,11 +12891,11 @@ var DrawingInteraction = class {
12891
12891
  /** A press: place the next anchor, or grab a drawing/handle (resolved as click or drag on release).
12892
12892
  * `shift` over a drawing toggles it in/out of the selection (multi-select) instead of dragging;
12893
12893
  * while placing a line tool it locks the segment angle to 45° steps. */
12894
- down(x, y, mode = "off", shift = false) {
12894
+ down(x, y, mode = "off", shift2 = false) {
12895
12895
  const proj = this.deps.projector();
12896
12896
  if (this.state.kind === "placing") {
12897
12897
  if (this.state.draft.placementMode() !== "click") return;
12898
- const pivot = shift ? this.placingPivot(this.state.draft) : null;
12898
+ const pivot = shift2 ? this.placingPivot(this.state.draft) : null;
12899
12899
  const point = pivot ? this.resolveAngleSnapped(pivot, this.state.draft.paneId, x, y) : this.resolve(x, y, this.state.draft.paneId, mode);
12900
12900
  this.state.draft.anchors.push(this.placementAnchor(this.state.draft, point));
12901
12901
  if (this.state.draft.anchors.length >= this.state.need) this.finalize();
@@ -12914,7 +12914,7 @@ var DrawingInteraction = class {
12914
12914
  }
12915
12915
  const hit = this.hitAt(x, y);
12916
12916
  if (hit) {
12917
- if (shift) {
12917
+ if (shift2) {
12918
12918
  this.deps.emit({ kind: "select", ids: [hit.id], additive: true });
12919
12919
  return;
12920
12920
  }
@@ -12924,7 +12924,7 @@ var DrawingInteraction = class {
12924
12924
  }
12925
12925
  /** Pointer move: advance the placing ghost, or live-preview a drag once past the slop.
12926
12926
  * `shift` on a line tool locks the segment angle to 45° steps (placing + handle drags). */
12927
- move(x, y, mode = "off", shift = false) {
12927
+ move(x, y, mode = "off", shift2 = false) {
12928
12928
  if (this.state.kind === "idle") {
12929
12929
  const tool = this.deps.activeTool();
12930
12930
  const had = this.snapAt != null;
@@ -12938,7 +12938,7 @@ var DrawingInteraction = class {
12938
12938
  this.captureFreehand(x, y);
12939
12939
  return;
12940
12940
  }
12941
- const pivot = shift ? this.placingPivot(this.state.draft) : null;
12941
+ const pivot = shift2 ? this.placingPivot(this.state.draft) : null;
12942
12942
  this.state.cursor = pivot ? this.resolveAngleSnapped(pivot, this.state.draft.paneId, x, y) : this.resolve(x, y, this.state.draft.paneId, mode);
12943
12943
  this.deps.changed();
12944
12944
  return;
@@ -12951,7 +12951,7 @@ var DrawingInteraction = class {
12951
12951
  if (this.state.handle >= 0) {
12952
12952
  const a = d.anchors[this.state.handle];
12953
12953
  if (a) {
12954
- const pivot = shift && ANGLE_SNAP_TYPES.has(d.type) && d.anchors.length === 2 ? d.anchors[1 - this.state.handle] : void 0;
12954
+ const pivot = shift2 && ANGLE_SNAP_TYPES.has(d.type) && d.anchors.length === 2 ? d.anchors[1 - this.state.handle] : void 0;
12955
12955
  const point = pivot ? this.resolveAngleSnapped(pivot, d.paneId, x, y) : this.resolve(x, y, d.paneId, mode);
12956
12956
  applyFree(a, point, d.anchorSchema().slots[this.state.handle]?.free ?? "both");
12957
12957
  }
@@ -13649,21 +13649,21 @@ var DrawingSettingsPopup = class {
13649
13649
  bar.appendChild(this.colorButton("Lower fill", BUCKET_ICON, reg.lowerFill, (v) => actions.patch({ "reg.lowerFill": v })));
13650
13650
  bar.appendChild(this.toggle("Show R\xB2", R2_ICON, reg.showR2, (v) => actions.patch({ "reg.showR2": v })));
13651
13651
  }
13652
- const vwap = drawing.vwap;
13653
- if (paths.has("vwap.midColor") && vwap) {
13652
+ const vwap2 = drawing.vwap;
13653
+ if (paths.has("vwap.midColor") && vwap2) {
13654
13654
  const styles = LINE_STYLE_OPTIONS.map((o) => o.value);
13655
13655
  const MULTS = [0.5, 1, 1.5, 2, 2.5, 3, 4, 5];
13656
- bar.appendChild(this.colorButton("VWAP color", BRUSH_ICON, vwap.midColor, (v) => actions.patch({ "vwap.midColor": v })));
13657
- bar.appendChild(this.dropdown("VWAP style", styles, vwap.midStyle, (s) => lineIcon(2, s), (v) => actions.patch({ "vwap.midStyle": v }), { label: styleLabel2 }));
13656
+ bar.appendChild(this.colorButton("VWAP color", BRUSH_ICON, vwap2.midColor, (v) => actions.patch({ "vwap.midColor": v })));
13657
+ bar.appendChild(this.dropdown("VWAP style", styles, vwap2.midStyle, (s) => lineIcon(2, s), (v) => actions.patch({ "vwap.midStyle": v }), { label: styleLabel2 }));
13658
13658
  bar.appendChild(
13659
- this.dropdown("Band multiplier", MULTS, vwap.multiplier, () => BANDS_ICON, (v) => actions.patch({ "vwap.multiplier": v }), {
13659
+ this.dropdown("Band multiplier", MULTS, vwap2.multiplier, () => BANDS_ICON, (v) => actions.patch({ "vwap.multiplier": v }), {
13660
13660
  label: (v) => `${v}\u03C3`,
13661
13661
  labelInTrigger: true
13662
13662
  })
13663
13663
  );
13664
- bar.appendChild(this.colorButton("Upper band color", BRUSH_ICON, vwap.upperColor, (v) => actions.patch({ "vwap.upperColor": v })));
13665
- bar.appendChild(this.colorButton("Lower band color", BRUSH_ICON, vwap.lowerColor, (v) => actions.patch({ "vwap.lowerColor": v })));
13666
- bar.appendChild(this.colorButton("Band fill", BUCKET_ICON, vwap.bandFill, (v) => actions.patch({ "vwap.bandFill": v })));
13664
+ bar.appendChild(this.colorButton("Upper band color", BRUSH_ICON, vwap2.upperColor, (v) => actions.patch({ "vwap.upperColor": v })));
13665
+ bar.appendChild(this.colorButton("Lower band color", BRUSH_ICON, vwap2.lowerColor, (v) => actions.patch({ "vwap.lowerColor": v })));
13666
+ bar.appendChild(this.colorButton("Band fill", BUCKET_ICON, vwap2.bandFill, (v) => actions.patch({ "vwap.bandFill": v })));
13667
13667
  }
13668
13668
  if (paths.has("maxCurvature")) {
13669
13669
  const cur = drawing.maxCurvature ?? 24;
@@ -14680,8 +14680,8 @@ var UserDrawingController = class {
14680
14680
  this.toolbar.setFavorites(types);
14681
14681
  }
14682
14682
  /** Core push: per-tool shortcut hints (display strings) shown in the toolbar flyouts. */
14683
- setToolShortcuts(map) {
14684
- this.toolbar.setShortcuts(map);
14683
+ setToolShortcuts(map2) {
14684
+ this.toolbar.setShortcuts(map2);
14685
14685
  }
14686
14686
  /** Core push: set the sticky magnet mode. Applies to the renderer + reflects on the
14687
14687
  * in-chart toolbar WITHOUT notifying back (the caller already holds the value). */
@@ -14799,7 +14799,7 @@ var UserDrawingController = class {
14799
14799
  this.render();
14800
14800
  }
14801
14801
  }
14802
- pointerDown(x, y, snap = "off", shift = false) {
14802
+ pointerDown(x, y, snap = "off", shift2 = false) {
14803
14803
  if (this.eraserMode) {
14804
14804
  this.erasing = true;
14805
14805
  this.deleteAt(x, y);
@@ -14819,9 +14819,9 @@ var UserDrawingController = class {
14819
14819
  return;
14820
14820
  }
14821
14821
  }
14822
- this.interaction.down(x, y, snap, shift);
14822
+ this.interaction.down(x, y, snap, shift2);
14823
14823
  }
14824
- pointerMove(x, y, snap = "off", shift = false) {
14824
+ pointerMove(x, y, snap = "off", shift2 = false) {
14825
14825
  if (this.eraserMode) {
14826
14826
  if (this.erasing) this.deleteAt(x, y);
14827
14827
  return;
@@ -14832,7 +14832,7 @@ var UserDrawingController = class {
14832
14832
  this.render();
14833
14833
  return;
14834
14834
  }
14835
- this.interaction.move(x, y, snap, shift);
14835
+ this.interaction.move(x, y, snap, shift2);
14836
14836
  this.updateHover(x, y);
14837
14837
  }
14838
14838
  /** Track which drawing is hovered so its handles appear on hover (and only then). */
@@ -15509,14 +15509,14 @@ function layoutTable(ctx, t, args) {
15509
15509
  }
15510
15510
  for (const { box, w: w2, h: h2 } of spanning) {
15511
15511
  if (box.cs > 1) {
15512
- let sum = 0;
15513
- for (let c = box.c; c < box.c + box.cs; c += 1) sum += colW[c];
15514
- if (w2 > sum) for (let c = box.c; c < box.c + box.cs; c += 1) colW[c] += (w2 - sum) / box.cs;
15512
+ let sum2 = 0;
15513
+ for (let c = box.c; c < box.c + box.cs; c += 1) sum2 += colW[c];
15514
+ if (w2 > sum2) for (let c = box.c; c < box.c + box.cs; c += 1) colW[c] += (w2 - sum2) / box.cs;
15515
15515
  }
15516
15516
  if (box.rs > 1) {
15517
- let sum = 0;
15518
- for (let r = box.r; r < box.r + box.rs; r += 1) sum += rowH[r];
15519
- if (h2 > sum) for (let r = box.r; r < box.r + box.rs; r += 1) rowH[r] += (h2 - sum) / box.rs;
15517
+ let sum2 = 0;
15518
+ for (let r = box.r; r < box.r + box.rs; r += 1) sum2 += rowH[r];
15519
+ if (h2 > sum2) for (let r = box.r; r < box.r + box.rs; r += 1) rowH[r] += (h2 - sum2) / box.rs;
15520
15520
  }
15521
15521
  }
15522
15522
  let w = 0;
@@ -16213,24 +16213,24 @@ function rasterizeOverlay(ctx, root, frame) {
16213
16213
  drawSvgShapes(el, alpha);
16214
16214
  continue;
16215
16215
  }
16216
- const num3 = (name) => parseFloat(el.getAttribute(name) ?? "0") || 0;
16216
+ const num4 = (name) => parseFloat(el.getAttribute(name) ?? "0") || 0;
16217
16217
  let path = null;
16218
16218
  if (tag === "path") {
16219
16219
  const d = el.getAttribute("d");
16220
16220
  if (d) path = new Path2D(d);
16221
16221
  } else if (tag === "circle") {
16222
16222
  path = new Path2D();
16223
- path.arc(num3("cx"), num3("cy"), num3("r"), 0, Math.PI * 2);
16223
+ path.arc(num4("cx"), num4("cy"), num4("r"), 0, Math.PI * 2);
16224
16224
  } else if (tag === "ellipse") {
16225
16225
  path = new Path2D();
16226
- path.ellipse(num3("cx"), num3("cy"), num3("rx"), num3("ry"), 0, 0, Math.PI * 2);
16226
+ path.ellipse(num4("cx"), num4("cy"), num4("rx"), num4("ry"), 0, 0, Math.PI * 2);
16227
16227
  } else if (tag === "rect") {
16228
16228
  path = new Path2D();
16229
- path.rect(num3("x"), num3("y"), num3("width"), num3("height"));
16229
+ path.rect(num4("x"), num4("y"), num4("width"), num4("height"));
16230
16230
  } else if (tag === "line") {
16231
16231
  path = new Path2D();
16232
- path.moveTo(num3("x1"), num3("y1"));
16233
- path.lineTo(num3("x2"), num3("y2"));
16232
+ path.moveTo(num4("x1"), num4("y1"));
16233
+ path.lineTo(num4("x2"), num4("y2"));
16234
16234
  } else if (tag === "polyline" || tag === "polygon") {
16235
16235
  const pts = (el.getAttribute("points") ?? "").trim().split(/[\s,]+/).map(Number);
16236
16236
  if (pts.length < 4 || pts.some(Number.isNaN)) continue;
@@ -17647,8 +17647,8 @@ var NativeRenderer = class {
17647
17647
  drawingsDeleteAt: (x, y) => this.userDrawings?.deleteAt(x, y) ?? false,
17648
17648
  drawingsCancelPlacement: () => this.userDrawings?.cancelPlacement() ?? false,
17649
17649
  drawingsSnapMode: () => this.snapMode,
17650
- drawingsPointerDown: (x, y, snap, shift) => this.userDrawings?.pointerDown(x, y, snap, shift),
17651
- drawingsPointerMove: (x, y, snap, shift) => this.userDrawings?.pointerMove(x, y, snap, shift),
17650
+ drawingsPointerDown: (x, y, snap, shift2) => this.userDrawings?.pointerDown(x, y, snap, shift2),
17651
+ drawingsPointerMove: (x, y, snap, shift2) => this.userDrawings?.pointerMove(x, y, snap, shift2),
17652
17652
  drawingsPointerUp: (x, y, snap) => this.userDrawings?.pointerUp(x, y, snap),
17653
17653
  drawingsCursor: (x, y) => this.userDrawings?.cursorAt(x, y) ?? null,
17654
17654
  drawingsDblClick: (x, y) => this.userDrawings?.dblClick(x, y) ?? false,
@@ -17965,18 +17965,18 @@ var NativeRenderer = class {
17965
17965
  for (const model of this.scene.indicators.values()) this.refreshAnchorOffset(model);
17966
17966
  if (opts?.preserveView === true && prevHeadTime != null && this.bars.length > 0) {
17967
17967
  const newHeadTime = this.bars[0].time;
17968
- let shift = 0;
17968
+ let shift2 = 0;
17969
17969
  if (newHeadTime !== prevHeadTime) {
17970
17970
  const prepended = this.bars.findIndex((b) => b.time === prevHeadTime);
17971
- if (prepended > 0) shift = prepended;
17971
+ if (prepended > 0) shift2 = prepended;
17972
17972
  else {
17973
17973
  const trimmed = prevBars.findIndex((b) => b.time === newHeadTime);
17974
- if (trimmed > 0) shift = -trimmed;
17974
+ if (trimmed > 0) shift2 = -trimmed;
17975
17975
  }
17976
17976
  }
17977
- if (shift !== 0) {
17978
- this.zoomAnchorLogical += shift;
17979
- if (this.hoverLogical != null) this.hoverLogical += shift;
17977
+ if (shift2 !== 0) {
17978
+ this.zoomAnchorLogical += shift2;
17979
+ if (this.hoverLogical != null) this.hoverLogical += shift2;
17980
17980
  }
17981
17981
  }
17982
17982
  const skipFit = opts?.preserveView === true && this.didInitialFit;
@@ -19582,15 +19582,15 @@ var NativeRenderer = class {
19582
19582
  /** For each collapsed pane, the master (master-scale) indicator id whose legend row stays in the
19583
19583
  * strip — merged own-scale indicators are hidden while collapsed. */
19584
19584
  collapsedMasterMap() {
19585
- const map = /* @__PURE__ */ new Map();
19585
+ const map2 = /* @__PURE__ */ new Map();
19586
19586
  for (const pane of this.scene.panes.values()) {
19587
19587
  if (!pane.collapsed) continue;
19588
19588
  const models = this.scene.orderedIndicatorsForPane(pane.id);
19589
19589
  const merged = new Set(this.scene.ownScaleIndicatorsForPane(pane.id).map((m) => m.id));
19590
19590
  const master = models.find((m) => !merged.has(m.id)) ?? models[0];
19591
- map.set(pane.id, master?.id ?? null);
19591
+ map2.set(pane.id, master?.id ?? null);
19592
19592
  }
19593
- return map;
19593
+ return map2;
19594
19594
  }
19595
19595
  /** Reserve `px` of left gutter for the docked drawings toolbar (0 releases it) + re-lay-out the plot. */
19596
19596
  setToolbarGutter(px) {
@@ -19862,7 +19862,7 @@ var LiveSession = class {
19862
19862
 
19863
19863
  // src/core/engine/IndicatorHandleImpl.ts
19864
19864
  var IndicatorHandleImpl = class {
19865
- constructor(id, title, controller, source) {
19865
+ constructor(id, title, controller, source, nativeType) {
19866
19866
  this.controller = controller;
19867
19867
  this.schema = [];
19868
19868
  this.propsSchema = [];
@@ -19871,6 +19871,7 @@ var IndicatorHandleImpl = class {
19871
19871
  this.id = id;
19872
19872
  this.title = title;
19873
19873
  if (source !== void 0) this.source = source;
19874
+ if (nativeType !== void 0) this.nativeType = nativeType;
19874
19875
  }
19875
19876
  get inputs() {
19876
19877
  return this.schema;
@@ -19961,24 +19962,24 @@ function summarizeModel(model) {
19961
19962
  }
19962
19963
  function inspectModels(models) {
19963
19964
  const indicators = models.map(summarizeModel);
19964
- const sum = (pick) => indicators.reduce((n, s) => n + pick(s), 0);
19965
+ const sum2 = (pick) => indicators.reduce((n, s) => n + pick(s), 0);
19965
19966
  return {
19966
19967
  indicators,
19967
19968
  totals: {
19968
19969
  panes: new Set(indicators.map((s) => s.paneId ?? "price")).size,
19969
- series: sum((s) => Object.values(s.series).reduce((a, b) => a + b, 0)),
19970
- fills: sum((s) => s.fills),
19971
- backgrounds: sum((s) => s.backgrounds),
19972
- priceLines: sum((s) => s.priceLines),
19973
- lines: sum((s) => s.lines),
19974
- boxes: sum((s) => s.boxes),
19975
- labels: sum((s) => s.labels),
19976
- polylines: sum((s) => s.polylines),
19977
- linefills: sum((s) => s.linefills),
19978
- tables: sum((s) => s.tables),
19979
- barColors: sum((s) => s.barColors),
19980
- trades: sum((s) => s.trades),
19981
- forcedOverlay: sum((s) => s.forcedOverlay)
19970
+ series: sum2((s) => Object.values(s.series).reduce((a, b) => a + b, 0)),
19971
+ fills: sum2((s) => s.fills),
19972
+ backgrounds: sum2((s) => s.backgrounds),
19973
+ priceLines: sum2((s) => s.priceLines),
19974
+ lines: sum2((s) => s.lines),
19975
+ boxes: sum2((s) => s.boxes),
19976
+ labels: sum2((s) => s.labels),
19977
+ polylines: sum2((s) => s.polylines),
19978
+ linefills: sum2((s) => s.linefills),
19979
+ tables: sum2((s) => s.tables),
19980
+ barColors: sum2((s) => s.barColors),
19981
+ trades: sum2((s) => s.trades),
19982
+ forcedOverlay: sum2((s) => s.forcedOverlay)
19982
19983
  }
19983
19984
  };
19984
19985
  }
@@ -21035,16 +21036,20 @@ var EngineOrchestrator = class _EngineOrchestrator {
21035
21036
  /**
21036
21037
  * Add a NATIVE (core-computed, no Pine engine) indicator by registered type — a first-class
21037
21038
  * indicator that shares the registry, handle, legend, settings, and lifecycle events. SINGLE
21038
- * INSTANCE per type: a second add returns the existing handle. Unknown type ⇒ a fail-soft handle
21039
- * that never mounts (mirrors addIndicator). Built-in types: `'volume'`, `'vpvr'`.
21039
+ * INSTANCE per type unless the descriptor opts into `multiInstance`: a second add of a
21040
+ * single-instance type returns the existing handle; a multi-instance type gets a fresh
21041
+ * instance every time. Unknown type ⇒ a fail-soft handle that never mounts (mirrors
21042
+ * addIndicator). Built-in single-instance types: `'volume'`, `'vpvr'`.
21040
21043
  */
21041
21044
  addNativeIndicator(type, options = {}) {
21042
- const existing = this.registry.all().find((r) => r.native?.type === type);
21043
- if (existing) return this.handles.get(existing.id) ?? new IndicatorHandleImpl(existing.id, existing.title, this);
21044
21045
  const descriptor = getNativeIndicator(type);
21046
+ if (!descriptor?.multiInstance) {
21047
+ const existing = this.registry.all().find((r) => r.native?.type === type);
21048
+ if (existing) return this.handles.get(existing.id) ?? new IndicatorHandleImpl(existing.id, existing.title, this, void 0, type);
21049
+ }
21045
21050
  const id = this.registry.nextId("native");
21046
21051
  const title = descriptor?.title ?? type;
21047
- const handle = new IndicatorHandleImpl(id, title, this);
21052
+ const handle = new IndicatorHandleImpl(id, title, this, void 0, type);
21048
21053
  this.handles.set(id, handle);
21049
21054
  if (!descriptor) {
21050
21055
  console.warn(`[vela] addNativeIndicator("${type}") \u2014 no native indicator registered for this type.`);
@@ -21056,16 +21061,18 @@ var EngineOrchestrator = class _EngineOrchestrator {
21056
21061
  void this.startNativeIndicator(id, handle);
21057
21062
  return handle;
21058
21063
  }
21059
- /**
21060
- * The catalog of registered native-indicator types with their live state on THIS chart: each
21061
- * entry's `supported` (applies to the current symbol) and `present` (already added — a second
21062
- * add is a no-op). Powers a host "add native indicator" menu that can gate + de-duplicate. Async
21063
- * because a descriptor's `isSupported` may probe the provider for a required capability.
21064
- */
21065
- /** The native types present on the chart — SYNC (registry state; no support probe). */
21064
+ /** The native types present on the chart, one entry per INSTANCE in registry order (a
21065
+ * multi-instance type repeats) — SYNC (registry state; no support probe). */
21066
21066
  presentNativeIndicators() {
21067
21067
  return this.registry.all().map((r) => r.native?.type).filter((t) => !!t);
21068
21068
  }
21069
+ /**
21070
+ * The catalog of registered native-indicator types with their live state on THIS chart: each
21071
+ * entry's `supported` (applies to the current symbol) and `present` (at least one instance
21072
+ * added — a second add of a single-instance type is a no-op). Powers a host "add native
21073
+ * indicator" menu that can gate + de-duplicate. Async because a descriptor's `isSupported` may
21074
+ * probe the provider for a required capability.
21075
+ */
21069
21076
  async availableNativeIndicators() {
21070
21077
  const symbol = this.qualifiedSymbol();
21071
21078
  const present = new Set(this.presentNativeIndicators());
@@ -21075,7 +21082,8 @@ var EngineOrchestrator = class _EngineOrchestrator {
21075
21082
  title: d.title,
21076
21083
  supported: await this.isNativeSupported(d, symbol),
21077
21084
  present: present.has(d.type),
21078
- beta: d.beta
21085
+ beta: d.beta,
21086
+ ...d.multiInstance ? { multiInstance: true } : {}
21079
21087
  }))
21080
21088
  );
21081
21089
  }
@@ -22015,12 +22023,2322 @@ function registerVpvr() {
22015
22023
  registerNativeIndicator(vpvrDescriptor);
22016
22024
  }
22017
22025
 
22026
+ // src/core/native-indicators/classics/define.ts
22027
+ function num3(inputs, key, fallback) {
22028
+ const v = inputs[key];
22029
+ return typeof v === "number" && Number.isFinite(v) ? v : fallback;
22030
+ }
22031
+ function str3(inputs, key, fallback) {
22032
+ const v = inputs[key];
22033
+ return typeof v === "string" && v !== "" ? v : fallback;
22034
+ }
22035
+ function toPoints(bars, values, colors) {
22036
+ const pts = new Array(bars.length);
22037
+ for (let i = 0; i < bars.length; i++) {
22038
+ const v = values[i];
22039
+ const p = { time: bars[i].time, value: v != null && Number.isFinite(v) ? v : null };
22040
+ const c = colors?.[i];
22041
+ if (c != null) p.color = c;
22042
+ pts[i] = p;
22043
+ }
22044
+ return pts;
22045
+ }
22046
+ var ClassicIndicator = class {
22047
+ constructor(spec) {
22048
+ this.spec = spec;
22049
+ this.inputs = {};
22050
+ }
22051
+ start(ctx, inputs) {
22052
+ this.ctx = ctx;
22053
+ this.inputs = inputs;
22054
+ this.recompute();
22055
+ }
22056
+ onBars() {
22057
+ this.recompute();
22058
+ }
22059
+ onViewport() {
22060
+ }
22061
+ setInputs(inputs) {
22062
+ this.inputs = inputs;
22063
+ this.recompute();
22064
+ }
22065
+ /** Nothing to free — the compute reads the live bar accessor on demand. */
22066
+ suspend() {
22067
+ }
22068
+ resume() {
22069
+ this.recompute();
22070
+ }
22071
+ stop() {
22072
+ }
22073
+ recompute() {
22074
+ const bars = this.ctx.bars();
22075
+ const out = this.spec.compute(bars, this.inputs);
22076
+ const instanceId = this.spec.type;
22077
+ const series = [];
22078
+ const idsByKey = /* @__PURE__ */ new Map();
22079
+ out.plots.forEach((plot, ordinal) => {
22080
+ const kind = plot.kind ?? "line";
22081
+ const id = stableSeriesId({ instanceId, kind, title: plot.title, ordinal });
22082
+ idsByKey.set(plot.key, id);
22083
+ series.push({
22084
+ id,
22085
+ title: plot.title,
22086
+ paneId: "",
22087
+ // stamped by the orchestrator
22088
+ kind,
22089
+ points: toPoints(bars, plot.values, plot.colors),
22090
+ style: {
22091
+ color: plot.color,
22092
+ width: plot.width ?? 1,
22093
+ lineStyle: plot.lineStyle ?? "solid",
22094
+ ...plot.base != null ? { base: plot.base } : {}
22095
+ },
22096
+ ...plot.overlay ? { overlay: true } : {}
22097
+ });
22098
+ });
22099
+ if (out.markers && out.markers.length > 0) {
22100
+ series.push({
22101
+ id: stableSeriesId({ instanceId, kind: "markers", title: "markers", ordinal: 0 }),
22102
+ title: this.spec.shortTitle ?? this.spec.title,
22103
+ paneId: "",
22104
+ kind: "markers",
22105
+ markers: out.markers
22106
+ });
22107
+ }
22108
+ const fills = (out.bands ?? []).flatMap((band, ordinal) => {
22109
+ const from = idsByKey.get(band.from);
22110
+ const to = idsByKey.get(band.to);
22111
+ if (!from || !to) return [];
22112
+ return [
22113
+ {
22114
+ id: stableSeriesId({ instanceId, kind: "fill", title: band.key, ordinal }),
22115
+ paneId: "",
22116
+ fromSeriesId: from,
22117
+ toSeriesId: to,
22118
+ color: band.color
22119
+ }
22120
+ ];
22121
+ });
22122
+ const priceLines = (out.levels ?? []).map((level, ordinal) => ({
22123
+ id: stableSeriesId({ instanceId, kind: "hline", title: level.key, ordinal }),
22124
+ paneId: "",
22125
+ price: level.price,
22126
+ ...level.color != null ? { color: level.color } : {},
22127
+ lineStyle: level.lineStyle ?? "dashed",
22128
+ width: 1,
22129
+ ...level.title != null ? { title: level.title } : {}
22130
+ }));
22131
+ const polylines = (out.polylines ?? []).map((poly, ordinal) => {
22132
+ const { key, ...rest } = poly;
22133
+ return {
22134
+ id: stableSeriesId({ instanceId, kind: "polyline", title: key, ordinal }),
22135
+ paneId: "",
22136
+ ...rest
22137
+ };
22138
+ });
22139
+ this.ctx.emit({ series, fills, priceLines, polylines });
22140
+ this.ctx.setStatus("idle");
22141
+ }
22142
+ };
22143
+ function classicDescriptor(spec) {
22144
+ return {
22145
+ type: spec.type,
22146
+ title: spec.title,
22147
+ ...spec.shortTitle != null ? { shortTitle: spec.shortTitle } : {},
22148
+ paneHint: spec.overlay ? "price" : "new",
22149
+ overlay: spec.overlay,
22150
+ // Studies stack (a 20 and a 200 moving average side by side) — unlike the layer-backed natives.
22151
+ multiInstance: true,
22152
+ inputsSchema: () => spec.inputs,
22153
+ defaultInputs: () => Object.fromEntries(spec.inputs.map((i) => [i.key, i.defval])),
22154
+ create: () => new ClassicIndicator(spec)
22155
+ };
22156
+ }
22157
+
22158
+ // src/core/native-indicators/classics/math.ts
22159
+ var SOURCES2 = ["Close", "Open", "High", "Low", "HL2", "HLC3", "OHLC4", "HLCC4"];
22160
+ function sourceValues(bars, source) {
22161
+ const out = new Array(bars.length);
22162
+ for (let i = 0; i < bars.length; i++) {
22163
+ const b = bars[i];
22164
+ switch (source) {
22165
+ case "Open":
22166
+ out[i] = b.open;
22167
+ break;
22168
+ case "High":
22169
+ out[i] = b.high;
22170
+ break;
22171
+ case "Low":
22172
+ out[i] = b.low;
22173
+ break;
22174
+ case "HL2":
22175
+ out[i] = (b.high + b.low) / 2;
22176
+ break;
22177
+ case "HLC3":
22178
+ out[i] = (b.high + b.low + b.close) / 3;
22179
+ break;
22180
+ case "OHLC4":
22181
+ out[i] = (b.open + b.high + b.low + b.close) / 4;
22182
+ break;
22183
+ case "HLCC4":
22184
+ out[i] = (b.high + b.low + b.close + b.close) / 4;
22185
+ break;
22186
+ default:
22187
+ out[i] = b.close;
22188
+ }
22189
+ }
22190
+ return out;
22191
+ }
22192
+ function volumes(bars) {
22193
+ return bars.map((b) => b.volume != null && Number.isFinite(b.volume) ? b.volume : Number.NaN);
22194
+ }
22195
+ function highs(bars) {
22196
+ return bars.map((b) => b.high);
22197
+ }
22198
+ function lows(bars) {
22199
+ return bars.map((b) => b.low);
22200
+ }
22201
+ function closes(bars) {
22202
+ return bars.map((b) => b.close);
22203
+ }
22204
+ function opens(bars) {
22205
+ return bars.map((b) => b.open);
22206
+ }
22207
+ function zip(a, b, f) {
22208
+ const out = new Array(a.length);
22209
+ for (let i = 0; i < a.length; i++) {
22210
+ const x = a[i];
22211
+ const y = b[i];
22212
+ out[i] = Number.isFinite(x) && Number.isFinite(y) ? f(x, y) : Number.NaN;
22213
+ }
22214
+ return out;
22215
+ }
22216
+ function map(a, f) {
22217
+ const out = new Array(a.length);
22218
+ for (let i = 0; i < a.length; i++) {
22219
+ const x = a[i];
22220
+ out[i] = Number.isFinite(x) ? f(x) : Number.NaN;
22221
+ }
22222
+ return out;
22223
+ }
22224
+ function sub(a, b) {
22225
+ return zip(a, b, (x, y) => x - y);
22226
+ }
22227
+ function windowed(v, len, fold) {
22228
+ const out = new Array(v.length).fill(Number.NaN);
22229
+ if (len <= 0) return out;
22230
+ let run = 0;
22231
+ for (let i = 0; i < v.length; i++) {
22232
+ run = Number.isFinite(v[i]) ? run + 1 : 0;
22233
+ if (run >= len) out[i] = fold(v, i - len + 1, i);
22234
+ }
22235
+ return out;
22236
+ }
22237
+ function sum(v, len) {
22238
+ return windowed(v, len, (w, from, to) => {
22239
+ let s = 0;
22240
+ for (let k = from; k <= to; k++) s += w[k];
22241
+ return s;
22242
+ });
22243
+ }
22244
+ function sma(v, len) {
22245
+ return windowed(v, len, (w, from, to) => {
22246
+ let s = 0;
22247
+ for (let k = from; k <= to; k++) s += w[k];
22248
+ return s / len;
22249
+ });
22250
+ }
22251
+ function wma(v, len) {
22252
+ const norm = len * (len + 1) / 2;
22253
+ return windowed(v, len, (w, from, to) => {
22254
+ let s = 0;
22255
+ for (let k = from; k <= to; k++) s += w[k] * (k - from + 1);
22256
+ return s / norm;
22257
+ });
22258
+ }
22259
+ function vwma(src, vol, len) {
22260
+ const pv = zip(src, vol, (p, q) => p * q);
22261
+ return zip(sum(pv, len), sum(vol, len), (a, b) => b > 0 ? a / b : Number.NaN);
22262
+ }
22263
+ function stdev(v, len) {
22264
+ return windowed(v, len, (w, from, to) => {
22265
+ let s = 0;
22266
+ for (let k = from; k <= to; k++) s += w[k];
22267
+ const mean = s / len;
22268
+ let sq = 0;
22269
+ for (let k = from; k <= to; k++) {
22270
+ const d = w[k] - mean;
22271
+ sq += d * d;
22272
+ }
22273
+ return Math.sqrt(sq / len);
22274
+ });
22275
+ }
22276
+ function highest(v, len) {
22277
+ return windowed(v, len, (w, from, to) => {
22278
+ let m = -Infinity;
22279
+ for (let k = from; k <= to; k++) if (w[k] > m) m = w[k];
22280
+ return m;
22281
+ });
22282
+ }
22283
+ function lowest(v, len) {
22284
+ return windowed(v, len, (w, from, to) => {
22285
+ let m = Infinity;
22286
+ for (let k = from; k <= to; k++) if (w[k] < m) m = w[k];
22287
+ return m;
22288
+ });
22289
+ }
22290
+ function barsSinceHighest(v, len) {
22291
+ return windowed(v, len, (w, from, to) => {
22292
+ let m = -Infinity;
22293
+ let at = to;
22294
+ for (let k = from; k <= to; k++) {
22295
+ if (w[k] >= m) {
22296
+ m = w[k];
22297
+ at = k;
22298
+ }
22299
+ }
22300
+ return to - at;
22301
+ });
22302
+ }
22303
+ function barsSinceLowest(v, len) {
22304
+ return windowed(v, len, (w, from, to) => {
22305
+ let m = Infinity;
22306
+ let at = to;
22307
+ for (let k = from; k <= to; k++) {
22308
+ if (w[k] <= m) {
22309
+ m = w[k];
22310
+ at = k;
22311
+ }
22312
+ }
22313
+ return to - at;
22314
+ });
22315
+ }
22316
+ function recursive(v, len, alpha) {
22317
+ const out = new Array(v.length).fill(Number.NaN);
22318
+ let run = 0;
22319
+ let prev2 = Number.NaN;
22320
+ for (let i = 0; i < v.length; i++) {
22321
+ const x = v[i];
22322
+ if (!Number.isFinite(x)) {
22323
+ run = 0;
22324
+ prev2 = Number.NaN;
22325
+ continue;
22326
+ }
22327
+ run += 1;
22328
+ if (Number.isFinite(prev2)) {
22329
+ prev2 = alpha * x + (1 - alpha) * prev2;
22330
+ out[i] = prev2;
22331
+ } else if (run >= len) {
22332
+ let s = 0;
22333
+ for (let k = i - len + 1; k <= i; k++) s += v[k];
22334
+ prev2 = s / len;
22335
+ out[i] = prev2;
22336
+ }
22337
+ }
22338
+ return out;
22339
+ }
22340
+ function ema(v, len) {
22341
+ return recursive(v, len, 2 / (len + 1));
22342
+ }
22343
+ function rma(v, len) {
22344
+ return recursive(v, len, 1 / len);
22345
+ }
22346
+ function change(v, n = 1) {
22347
+ const out = new Array(v.length).fill(Number.NaN);
22348
+ for (let i = n; i < v.length; i++) {
22349
+ const a = v[i];
22350
+ const b = v[i - n];
22351
+ if (Number.isFinite(a) && Number.isFinite(b)) out[i] = a - b;
22352
+ }
22353
+ return out;
22354
+ }
22355
+ function roc(v, len) {
22356
+ const out = new Array(v.length).fill(Number.NaN);
22357
+ for (let i = len; i < v.length; i++) {
22358
+ const a = v[i];
22359
+ const b = v[i - len];
22360
+ if (Number.isFinite(a) && Number.isFinite(b) && b !== 0) out[i] = 100 * (a - b) / b;
22361
+ }
22362
+ return out;
22363
+ }
22364
+ function shift(v, n) {
22365
+ const out = new Array(v.length).fill(Number.NaN);
22366
+ for (let i = 0; i < v.length; i++) {
22367
+ const j = i - n;
22368
+ if (j >= 0 && j < v.length) out[i] = v[j];
22369
+ }
22370
+ return out;
22371
+ }
22372
+ function trueRange(bars) {
22373
+ const out = new Array(bars.length).fill(Number.NaN);
22374
+ for (let i = 0; i < bars.length; i++) {
22375
+ const b = bars[i];
22376
+ if (i === 0) {
22377
+ out[i] = b.high - b.low;
22378
+ continue;
22379
+ }
22380
+ const pc = bars[i - 1].close;
22381
+ out[i] = Math.max(b.high - b.low, Math.abs(b.high - pc), Math.abs(b.low - pc));
22382
+ }
22383
+ return out;
22384
+ }
22385
+ function atr(bars, len) {
22386
+ return rma(trueRange(bars), len);
22387
+ }
22388
+ function rsi(v, len) {
22389
+ const d = change(v);
22390
+ const up = map(d, (x) => Math.max(x, 0));
22391
+ const dn = map(d, (x) => Math.max(-x, 0));
22392
+ return zip(rma(up, len), rma(dn, len), (u, w) => w === 0 ? 100 : 100 - 100 / (1 + u / w));
22393
+ }
22394
+ function stoch(v, hi, lo, len) {
22395
+ const hh = highest(hi, len);
22396
+ const ll = lowest(lo, len);
22397
+ const out = new Array(v.length).fill(Number.NaN);
22398
+ for (let i = 0; i < v.length; i++) {
22399
+ const x = v[i];
22400
+ const h = hh[i];
22401
+ const l = ll[i];
22402
+ if (!Number.isFinite(x) || !Number.isFinite(h) || !Number.isFinite(l)) continue;
22403
+ out[i] = h === l ? 50 : 100 * (x - l) / (h - l);
22404
+ }
22405
+ return out;
22406
+ }
22407
+ function linreg(v, len) {
22408
+ const sx = (len - 1) * len / 2;
22409
+ const sxx = (len - 1) * len * (2 * len - 1) / 6;
22410
+ const denom = len * sxx - sx * sx;
22411
+ return windowed(v, len, (w, from, to) => {
22412
+ let sy = 0;
22413
+ let sxy = 0;
22414
+ for (let k = from; k <= to; k++) {
22415
+ const x = k - from;
22416
+ sy += w[k];
22417
+ sxy += x * w[k];
22418
+ }
22419
+ const slope = denom === 0 ? 0 : (len * sxy - sx * sy) / denom;
22420
+ const intercept = (sy - slope * sx) / len;
22421
+ return intercept + slope * (len - 1);
22422
+ });
22423
+ }
22424
+ function swma(v) {
22425
+ const out = new Array(v.length).fill(Number.NaN);
22426
+ for (let i = 3; i < v.length; i++) {
22427
+ const a = v[i - 3];
22428
+ const b = v[i - 2];
22429
+ const c = v[i - 1];
22430
+ const d = v[i];
22431
+ if (Number.isFinite(a) && Number.isFinite(b) && Number.isFinite(c) && Number.isFinite(d)) {
22432
+ out[i] = (a + 2 * b + 2 * c + d) / 6;
22433
+ }
22434
+ }
22435
+ return out;
22436
+ }
22437
+ function percentRank(v, len) {
22438
+ const out = new Array(v.length).fill(Number.NaN);
22439
+ for (let i = len; i < v.length; i++) {
22440
+ const x = v[i];
22441
+ if (!Number.isFinite(x)) continue;
22442
+ let below = 0;
22443
+ let count = 0;
22444
+ for (let k = i - len; k < i; k++) {
22445
+ const y = v[k];
22446
+ if (!Number.isFinite(y)) {
22447
+ count = -1;
22448
+ break;
22449
+ }
22450
+ count += 1;
22451
+ if (y <= x) below += 1;
22452
+ }
22453
+ if (count === len) out[i] = 100 * below / len;
22454
+ }
22455
+ return out;
22456
+ }
22457
+ function cumSum(v) {
22458
+ const out = new Array(v.length).fill(Number.NaN);
22459
+ let acc = 0;
22460
+ let seen = false;
22461
+ for (let i = 0; i < v.length; i++) {
22462
+ const x = v[i];
22463
+ if (Number.isFinite(x)) {
22464
+ acc += x;
22465
+ seen = true;
22466
+ }
22467
+ if (seen) out[i] = acc;
22468
+ }
22469
+ return out;
22470
+ }
22471
+
22472
+ // src/core/native-indicators/classics/shared.ts
22473
+ function lengthInput(defval, key = "length", title = "Length", max = 5e3) {
22474
+ return { key, title, type: "int", defval, min: 1, max, step: 1 };
22475
+ }
22476
+ function sourceInput(defval = "Close") {
22477
+ return { key: "source", title: "Source", type: "string", defval, options: SOURCES2 };
22478
+ }
22479
+ function colorInput(defval = SERIES_LINE, title = "Color", key = "color") {
22480
+ return { key, title, type: "color", defval };
22481
+ }
22482
+ function withAlpha2(hex, alpha = 0.08) {
22483
+ const m = /^#([0-9a-f]{6})$/i.exec(hex.trim());
22484
+ if (!m) return hex;
22485
+ const a = Math.round(alpha * 255).toString(16).padStart(2, "0");
22486
+ return `#${m[1]}${a}`;
22487
+ }
22488
+
22489
+ // src/core/native-indicators/classics/averages.ts
22490
+ var MA_KINDS = ["SMA", "EMA", "WMA", "RMA", "VWMA"];
22491
+ var offsetInput = { key: "offset", title: "Offset", type: "int", defval: 0, min: -500, max: 500, step: 1 };
22492
+ function fixedAverage(type, title, shortTitle, smooth) {
22493
+ return {
22494
+ type,
22495
+ title,
22496
+ shortTitle,
22497
+ overlay: true,
22498
+ inputs: [lengthInput(20), sourceInput(), offsetInput, colorInput()],
22499
+ compute: (bars, inputs) => {
22500
+ const len = num3(inputs, "length", 20);
22501
+ let values = smooth(sourceValues(bars, str3(inputs, "source", "Close")), len);
22502
+ const offset = Math.trunc(num3(inputs, "offset", 0));
22503
+ if (offset !== 0) values = shift(values, offset);
22504
+ return { plots: [{ key: shortTitle.toLowerCase(), title: shortTitle, values, color: str3(inputs, "color", SERIES_LINE), width: 2 }] };
22505
+ }
22506
+ };
22507
+ }
22508
+ var simpleMa = fixedAverage("sma", "Simple Moving Average", "SMA", sma);
22509
+ var exponentialMa = fixedAverage("ema", "Exponential Moving Average", "EMA", ema);
22510
+ var movingAverage = {
22511
+ type: "moving-average",
22512
+ title: "Moving Average",
22513
+ shortTitle: "MA",
22514
+ overlay: true,
22515
+ inputs: [
22516
+ { key: "maType", title: "Type", type: "string", defval: "SMA", options: MA_KINDS },
22517
+ lengthInput(20),
22518
+ sourceInput(),
22519
+ offsetInput,
22520
+ colorInput()
22521
+ ],
22522
+ compute: (bars, inputs) => {
22523
+ const len = num3(inputs, "length", 20);
22524
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
22525
+ const kind = str3(inputs, "maType", "SMA");
22526
+ let values;
22527
+ switch (kind) {
22528
+ case "EMA":
22529
+ values = ema(src, len);
22530
+ break;
22531
+ case "WMA":
22532
+ values = wma(src, len);
22533
+ break;
22534
+ case "RMA":
22535
+ values = rma(src, len);
22536
+ break;
22537
+ case "VWMA":
22538
+ values = vwma(src, volumes(bars), len);
22539
+ break;
22540
+ default:
22541
+ values = sma(src, len);
22542
+ }
22543
+ const offset = Math.trunc(num3(inputs, "offset", 0));
22544
+ if (offset !== 0) values = shift(values, offset);
22545
+ return { plots: [{ key: "ma", title: kind, values, color: str3(inputs, "color", SERIES_LINE), width: 2 }] };
22546
+ }
22547
+ };
22548
+ var smoothedMa = {
22549
+ type: "rma",
22550
+ title: "Smoothed Moving Average",
22551
+ shortTitle: "RMA",
22552
+ overlay: true,
22553
+ inputs: [lengthInput(14), sourceInput(), colorInput()],
22554
+ compute: (bars, inputs) => ({
22555
+ plots: [
22556
+ {
22557
+ key: "rma",
22558
+ title: "RMA",
22559
+ values: rma(sourceValues(bars, str3(inputs, "source", "Close")), num3(inputs, "length", 14)),
22560
+ color: str3(inputs, "color", SERIES_LINE),
22561
+ width: 2
22562
+ }
22563
+ ]
22564
+ })
22565
+ };
22566
+ var zlema = {
22567
+ type: "zlema",
22568
+ title: "Zero-Lag Exponential Moving Average",
22569
+ shortTitle: "ZLEMA",
22570
+ overlay: true,
22571
+ inputs: [lengthInput(14), sourceInput(), colorInput()],
22572
+ compute: (bars, inputs) => {
22573
+ const len = num3(inputs, "length", 14);
22574
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
22575
+ const lag = Math.floor((len - 1) / 2);
22576
+ const lagged = shift(src, lag);
22577
+ const delagged = zip(src, lagged, (a, b) => 2 * a - b);
22578
+ return { plots: [{ key: "zlema", title: "ZLEMA", values: ema(delagged, len), color: str3(inputs, "color", SERIES_LINE), width: 2 }] };
22579
+ }
22580
+ };
22581
+ var vidya = {
22582
+ type: "vidya",
22583
+ title: "Variable Index Dynamic Average",
22584
+ shortTitle: "VIDYA",
22585
+ overlay: true,
22586
+ inputs: [lengthInput(14), { key: "cmoLength", title: "Momentum length", type: "int", defval: 9, min: 1, max: 500, step: 1 }, sourceInput(), colorInput()],
22587
+ compute: (bars, inputs) => {
22588
+ const len = num3(inputs, "length", 14);
22589
+ const cmoLen = num3(inputs, "cmoLength", 9);
22590
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
22591
+ const d = change(src);
22592
+ const su = new Array(src.length).fill(Number.NaN);
22593
+ const sd = new Array(src.length).fill(Number.NaN);
22594
+ for (let i = cmoLen; i < src.length; i++) {
22595
+ let u = 0;
22596
+ let w = 0;
22597
+ let ok = true;
22598
+ for (let k = i - cmoLen + 1; k <= i; k++) {
22599
+ const x = d[k];
22600
+ if (!Number.isFinite(x)) {
22601
+ ok = false;
22602
+ break;
22603
+ }
22604
+ if (x > 0) u += x;
22605
+ else w -= x;
22606
+ }
22607
+ if (ok) {
22608
+ su[i] = u;
22609
+ sd[i] = w;
22610
+ }
22611
+ }
22612
+ const alpha = 2 / (len + 1);
22613
+ const out = new Array(src.length).fill(Number.NaN);
22614
+ let prev2 = Number.NaN;
22615
+ for (let i = 0; i < src.length; i++) {
22616
+ const x = src[i];
22617
+ const u = su[i];
22618
+ const w = sd[i];
22619
+ if (!Number.isFinite(x) || !Number.isFinite(u) || !Number.isFinite(w)) continue;
22620
+ const total = u + w;
22621
+ const k = total === 0 ? 0 : Math.abs((u - w) / total);
22622
+ prev2 = Number.isFinite(prev2) ? alpha * k * x + (1 - alpha * k) * prev2 : x;
22623
+ out[i] = prev2;
22624
+ }
22625
+ return { plots: [{ key: "vidya", title: "VIDYA", values: out, color: str3(inputs, "color", SERIES_LINE), width: 2 }] };
22626
+ }
22627
+ };
22628
+ var maEnvelope = {
22629
+ type: "ma-envelope",
22630
+ title: "Moving Average Envelope",
22631
+ shortTitle: "MA Env",
22632
+ overlay: true,
22633
+ inputs: [
22634
+ { key: "maType", title: "Type", type: "string", defval: "SMA", options: ["SMA", "EMA"] },
22635
+ lengthInput(20),
22636
+ sourceInput(),
22637
+ { key: "percent", title: "Percent", type: "float", defval: 2.5, min: 0.01, max: 50, step: 0.1 },
22638
+ colorInput()
22639
+ ],
22640
+ compute: (bars, inputs) => {
22641
+ const len = num3(inputs, "length", 20);
22642
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
22643
+ const basis = str3(inputs, "maType", "SMA") === "EMA" ? ema(src, len) : sma(src, len);
22644
+ const k = num3(inputs, "percent", 2.5) / 100;
22645
+ const ink = str3(inputs, "color", SERIES_LINE);
22646
+ return {
22647
+ plots: [
22648
+ { key: "basis", title: "Basis", values: basis, color: ink, width: 2 },
22649
+ { key: "upper", title: "Upper", values: map(basis, (x) => x * (1 + k)), color: ink },
22650
+ { key: "lower", title: "Lower", values: map(basis, (x) => x * (1 - k)), color: ink }
22651
+ ],
22652
+ bands: [{ key: "envelope", from: "upper", to: "lower", color: withAlpha2(ink) }]
22653
+ };
22654
+ }
22655
+ };
22656
+ var linearRegression = {
22657
+ type: "linear-regression",
22658
+ title: "Linear Regression Curve",
22659
+ shortTitle: "LinReg",
22660
+ overlay: true,
22661
+ inputs: [lengthInput(14), sourceInput(), colorInput()],
22662
+ compute: (bars, inputs) => ({
22663
+ plots: [
22664
+ {
22665
+ key: "linreg",
22666
+ title: "LinReg",
22667
+ values: linreg(sourceValues(bars, str3(inputs, "source", "Close")), num3(inputs, "length", 14)),
22668
+ color: str3(inputs, "color", SERIES_LINE),
22669
+ width: 2
22670
+ }
22671
+ ]
22672
+ })
22673
+ };
22674
+ var averageSpecs = [simpleMa, exponentialMa, movingAverage, smoothedMa, zlema, vidya, maEnvelope, linearRegression];
22675
+
22676
+ // src/core/native-indicators/classics/bands.ts
22677
+ function bollinger(bars, inputs) {
22678
+ const len = num3(inputs, "length", 20);
22679
+ const mult = num3(inputs, "mult", 2);
22680
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
22681
+ const basis = sma(src, len);
22682
+ const dev = map(stdev(src, len), (x) => x * mult);
22683
+ return { basis, upper: zip(basis, dev, (b, d) => b + d), lower: zip(basis, dev, (b, d) => b - d) };
22684
+ }
22685
+ var BB_INPUTS = [lengthInput(20), sourceInput(), { key: "mult", title: "StdDev", type: "float", defval: 2, min: 0.1, max: 50, step: 0.1 }];
22686
+ var bollingerBands = {
22687
+ type: "bollinger-bands",
22688
+ title: "Bollinger Bands",
22689
+ shortTitle: "BB",
22690
+ overlay: true,
22691
+ inputs: [...BB_INPUTS, colorInput()],
22692
+ compute: (bars, inputs) => {
22693
+ const { basis, upper, lower } = bollinger(bars, inputs);
22694
+ const ink = str3(inputs, "color", SERIES_LINE);
22695
+ return {
22696
+ plots: [
22697
+ { key: "basis", title: "Basis", values: basis, color: WARNING },
22698
+ { key: "upper", title: "Upper", values: upper, color: ink },
22699
+ { key: "lower", title: "Lower", values: lower, color: ink }
22700
+ ],
22701
+ bands: [{ key: "bb", from: "upper", to: "lower", color: withAlpha2(ink) }]
22702
+ };
22703
+ }
22704
+ };
22705
+ var percentB = {
22706
+ type: "percent-b",
22707
+ title: "Bollinger Bands %B",
22708
+ shortTitle: "%B",
22709
+ overlay: false,
22710
+ inputs: [...BB_INPUTS, colorInput()],
22711
+ compute: (bars, inputs) => {
22712
+ const { upper, lower } = bollinger(bars, inputs);
22713
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
22714
+ const values = src.map((x, i) => {
22715
+ const u = upper[i];
22716
+ const l = lower[i];
22717
+ return Number.isFinite(u) && Number.isFinite(l) && u !== l ? (x - l) / (u - l) : Number.NaN;
22718
+ });
22719
+ return {
22720
+ plots: [{ key: "pb", title: "%B", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
22721
+ levels: [
22722
+ { key: "upper", price: 1, color: NEUTRAL },
22723
+ { key: "middle", price: 0.5, color: NEUTRAL, lineStyle: "dotted" },
22724
+ { key: "lower", price: 0, color: NEUTRAL }
22725
+ ]
22726
+ };
22727
+ }
22728
+ };
22729
+ var bandwidth = {
22730
+ type: "bandwidth",
22731
+ title: "Bollinger Bands Width",
22732
+ shortTitle: "BandWidth",
22733
+ overlay: false,
22734
+ inputs: [...BB_INPUTS, colorInput()],
22735
+ compute: (bars, inputs) => {
22736
+ const { basis, upper, lower } = bollinger(bars, inputs);
22737
+ const values = basis.map((b, i) => {
22738
+ const u = upper[i];
22739
+ const l = lower[i];
22740
+ return Number.isFinite(b) && b !== 0 && Number.isFinite(u) && Number.isFinite(l) ? (u - l) / b * 100 : Number.NaN;
22741
+ });
22742
+ return { plots: [{ key: "bbw", title: "BBW", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }] };
22743
+ }
22744
+ };
22745
+ var keltner = {
22746
+ type: "keltner-channels",
22747
+ title: "Keltner Channels",
22748
+ shortTitle: "KC",
22749
+ overlay: true,
22750
+ inputs: [
22751
+ lengthInput(20),
22752
+ { key: "mult", title: "Multiplier", type: "float", defval: 2, min: 0.1, max: 50, step: 0.1 },
22753
+ { key: "atrLength", title: "ATR length", type: "int", defval: 10, min: 1, max: 500, step: 1 },
22754
+ sourceInput(),
22755
+ colorInput()
22756
+ ],
22757
+ compute: (bars, inputs) => {
22758
+ const len = num3(inputs, "length", 20);
22759
+ const mult = num3(inputs, "mult", 2);
22760
+ const basis = ema(sourceValues(bars, str3(inputs, "source", "Close")), len);
22761
+ const range = map(atr(bars, num3(inputs, "atrLength", 10)), (x) => x * mult);
22762
+ const ink = str3(inputs, "color", SERIES_LINE);
22763
+ return {
22764
+ plots: [
22765
+ { key: "basis", title: "Basis", values: basis, color: WARNING },
22766
+ { key: "upper", title: "Upper", values: zip(basis, range, (b, r) => b + r), color: ink },
22767
+ { key: "lower", title: "Lower", values: zip(basis, range, (b, r) => b - r), color: ink }
22768
+ ],
22769
+ bands: [{ key: "kc", from: "upper", to: "lower", color: withAlpha2(ink) }]
22770
+ };
22771
+ }
22772
+ };
22773
+ var donchian = {
22774
+ type: "donchian-channels",
22775
+ title: "Donchian Channels",
22776
+ shortTitle: "DC",
22777
+ overlay: true,
22778
+ inputs: [lengthInput(20), colorInput()],
22779
+ compute: (bars, inputs) => {
22780
+ const len = num3(inputs, "length", 20);
22781
+ const upper = highest(highs(bars), len);
22782
+ const lower = lowest(lows(bars), len);
22783
+ const ink = str3(inputs, "color", SERIES_LINE);
22784
+ return {
22785
+ plots: [
22786
+ { key: "basis", title: "Basis", values: zip(upper, lower, (u, l) => (u + l) / 2), color: WARNING },
22787
+ { key: "upper", title: "Upper", values: upper, color: ink },
22788
+ { key: "lower", title: "Lower", values: lower, color: ink }
22789
+ ],
22790
+ bands: [{ key: "dc", from: "upper", to: "lower", color: withAlpha2(ink) }]
22791
+ };
22792
+ }
22793
+ };
22794
+ var chandelier = {
22795
+ type: "chandelier-exit",
22796
+ title: "Chandelier Exit",
22797
+ shortTitle: "CE",
22798
+ overlay: true,
22799
+ inputs: [lengthInput(22), { key: "mult", title: "ATR multiplier", type: "float", defval: 3, min: 0.1, max: 50, step: 0.1 }],
22800
+ compute: (bars, inputs) => {
22801
+ const len = num3(inputs, "length", 22);
22802
+ const range = map(atr(bars, len), (x) => x * num3(inputs, "mult", 3));
22803
+ const long = zip(highest(highs(bars), len), range, (h, r) => h - r);
22804
+ const short = zip(lowest(lows(bars), len), range, (l, r) => l + r);
22805
+ return {
22806
+ plots: [
22807
+ { key: "long", title: "Long stop", values: long, color: BULLISH },
22808
+ { key: "short", title: "Short stop", values: short, color: BEARISH }
22809
+ ]
22810
+ };
22811
+ }
22812
+ };
22813
+ var chandeKroll = {
22814
+ type: "chande-kroll-stop",
22815
+ title: "Chande Kroll Stop",
22816
+ shortTitle: "CKS",
22817
+ overlay: true,
22818
+ inputs: [
22819
+ { key: "p", title: "ATR length", type: "int", defval: 10, min: 1, max: 500, step: 1 },
22820
+ { key: "x", title: "ATR multiplier", type: "float", defval: 1, min: 0.1, max: 50, step: 0.1 },
22821
+ { key: "q", title: "Stop length", type: "int", defval: 9, min: 1, max: 500, step: 1 }
22822
+ ],
22823
+ compute: (bars, inputs) => {
22824
+ const p = num3(inputs, "p", 10);
22825
+ const x = num3(inputs, "x", 1);
22826
+ const q = num3(inputs, "q", 9);
22827
+ const range = map(atr(bars, p), (v) => v * x);
22828
+ const firstHigh = zip(highest(highs(bars), p), range, (h, r) => h - r);
22829
+ const firstLow = zip(lowest(lows(bars), p), range, (l, r) => l + r);
22830
+ return {
22831
+ plots: [
22832
+ { key: "stopShort", title: "Stop short", values: highest(firstHigh, q), color: BEARISH },
22833
+ { key: "stopLong", title: "Stop long", values: lowest(firstLow, q), color: BULLISH }
22834
+ ]
22835
+ };
22836
+ }
22837
+ };
22838
+ var supertrend = {
22839
+ type: "supertrend",
22840
+ title: "SuperTrend",
22841
+ shortTitle: "SuperTrend",
22842
+ overlay: true,
22843
+ inputs: [
22844
+ { key: "atrLength", title: "ATR length", type: "int", defval: 10, min: 1, max: 500, step: 1 },
22845
+ { key: "mult", title: "Factor", type: "float", defval: 3, min: 0.1, max: 50, step: 0.1 }
22846
+ ],
22847
+ compute: (bars, inputs) => {
22848
+ const len = num3(inputs, "atrLength", 10);
22849
+ const mult = num3(inputs, "mult", 3);
22850
+ const range = atr(bars, len);
22851
+ const n = bars.length;
22852
+ const st = new Array(n).fill(Number.NaN);
22853
+ const dir = new Array(n).fill(1);
22854
+ let up = Number.NaN;
22855
+ let dn = Number.NaN;
22856
+ let trend = 1;
22857
+ for (let i = 0; i < n; i++) {
22858
+ const r = range[i];
22859
+ if (!Number.isFinite(r)) continue;
22860
+ const b = bars[i];
22861
+ const mid = (b.high + b.low) / 2;
22862
+ const basicUp = mid - mult * r;
22863
+ const basicDn = mid + mult * r;
22864
+ const prevClose = i > 0 ? bars[i - 1].close : b.close;
22865
+ up = Number.isFinite(up) && prevClose > up ? Math.max(basicUp, up) : basicUp;
22866
+ dn = Number.isFinite(dn) && prevClose < dn ? Math.min(basicDn, dn) : basicDn;
22867
+ if (trend === 1 && b.close < up) trend = -1;
22868
+ else if (trend === -1 && b.close > dn) trend = 1;
22869
+ dir[i] = trend;
22870
+ st[i] = trend === 1 ? up : dn;
22871
+ }
22872
+ const colors = dir.map((d, i) => Number.isFinite(st[i]) ? d === 1 ? BULLISH : BEARISH : null);
22873
+ return { plots: [{ key: "st", title: "SuperTrend", values: st, color: BULLISH, width: 2, colors }] };
22874
+ }
22875
+ };
22876
+ var alligator = {
22877
+ type: "williams-alligator",
22878
+ title: "Williams Alligator",
22879
+ shortTitle: "Alligator",
22880
+ overlay: true,
22881
+ inputs: [
22882
+ { key: "jawLength", title: "Jaw length", type: "int", defval: 13, min: 1, max: 500, step: 1 },
22883
+ { key: "teethLength", title: "Teeth length", type: "int", defval: 8, min: 1, max: 500, step: 1 },
22884
+ { key: "lipsLength", title: "Lips length", type: "int", defval: 5, min: 1, max: 500, step: 1 },
22885
+ { key: "jawOffset", title: "Jaw offset", type: "int", defval: 8, min: 0, max: 100, step: 1 },
22886
+ { key: "teethOffset", title: "Teeth offset", type: "int", defval: 5, min: 0, max: 100, step: 1 },
22887
+ { key: "lipsOffset", title: "Lips offset", type: "int", defval: 3, min: 0, max: 100, step: 1 }
22888
+ ],
22889
+ compute: (bars, inputs) => {
22890
+ const hl2 = sourceValues(bars, "HL2");
22891
+ const jaw = shift(rma(hl2, num3(inputs, "jawLength", 13)), num3(inputs, "jawOffset", 8));
22892
+ const teeth = shift(rma(hl2, num3(inputs, "teethLength", 8)), num3(inputs, "teethOffset", 5));
22893
+ const lips = shift(rma(hl2, num3(inputs, "lipsLength", 5)), num3(inputs, "lipsOffset", 3));
22894
+ return {
22895
+ plots: [
22896
+ { key: "jaw", title: "Jaw", values: jaw, color: SERIES_LINE },
22897
+ { key: "teeth", title: "Teeth", values: teeth, color: BEARISH },
22898
+ { key: "lips", title: "Lips", values: lips, color: BULLISH }
22899
+ ]
22900
+ };
22901
+ }
22902
+ };
22903
+ var gator = {
22904
+ type: "gator-oscillator",
22905
+ title: "Gator Oscillator",
22906
+ shortTitle: "Gator Oscillator",
22907
+ overlay: false,
22908
+ inputs: [],
22909
+ compute: (bars) => {
22910
+ const hl2 = sourceValues(bars, "HL2");
22911
+ const jaw = shift(rma(hl2, 13), 8);
22912
+ const teeth = shift(rma(hl2, 8), 5);
22913
+ const lips = shift(rma(hl2, 5), 3);
22914
+ const upper = map(sub(jaw, teeth), Math.abs);
22915
+ const lower = map(sub(teeth, lips), (x) => -Math.abs(x));
22916
+ const colorByExpansion = (v) => v.map((x, i) => {
22917
+ if (!Number.isFinite(x)) return null;
22918
+ const prev2 = i > 0 ? v[i - 1] : Number.NaN;
22919
+ return Number.isFinite(prev2) && Math.abs(x) > Math.abs(prev2) ? BULLISH : BEARISH;
22920
+ });
22921
+ return {
22922
+ plots: [
22923
+ { key: "upper", title: "Upper", values: upper, kind: "histogram", color: BULLISH, colors: colorByExpansion(upper), base: 0 },
22924
+ { key: "lower", title: "Lower", values: lower, kind: "histogram", color: BEARISH, colors: colorByExpansion(lower), base: 0 }
22925
+ ]
22926
+ };
22927
+ }
22928
+ };
22929
+ var bandSpecs = [bollingerBands, percentB, bandwidth, keltner, donchian, chandelier, chandeKroll, supertrend, alligator, gator];
22930
+
22931
+ // src/core/native-indicators/classics/oscillators.ts
22932
+ function guideLevels(low, high, mid) {
22933
+ const levels = [
22934
+ { key: "upper", price: high, color: NEUTRAL },
22935
+ { key: "lower", price: low, color: NEUTRAL }
22936
+ ];
22937
+ if (mid != null) levels.splice(1, 0, { key: "middle", price: mid, color: NEUTRAL, lineStyle: "dotted" });
22938
+ return levels;
22939
+ }
22940
+ var ZERO_LEVEL = [{ key: "zero", price: 0, color: NEUTRAL }];
22941
+ function histColors(values) {
22942
+ return values.map((x, i) => {
22943
+ if (!Number.isFinite(x)) return null;
22944
+ const prev2 = i > 0 ? values[i - 1] : Number.NaN;
22945
+ const rising = Number.isFinite(prev2) ? x >= prev2 : true;
22946
+ if (x >= 0) return rising ? BULLISH : `${BULLISH}80`;
22947
+ return rising ? `${BEARISH}80` : BEARISH;
22948
+ });
22949
+ }
22950
+ var rsiSpec = {
22951
+ type: "rsi",
22952
+ title: "Relative Strength Index",
22953
+ shortTitle: "RSI",
22954
+ overlay: false,
22955
+ inputs: [lengthInput(14), sourceInput(), colorInput()],
22956
+ compute: (bars, inputs) => ({
22957
+ plots: [
22958
+ {
22959
+ key: "rsi",
22960
+ title: "RSI",
22961
+ values: rsi(sourceValues(bars, str3(inputs, "source", "Close")), num3(inputs, "length", 14)),
22962
+ color: str3(inputs, "color", SERIES_LINE),
22963
+ width: 2
22964
+ }
22965
+ ],
22966
+ levels: guideLevels(30, 70, 50)
22967
+ })
22968
+ };
22969
+ var stochastic = {
22970
+ type: "stochastic",
22971
+ title: "Stochastic",
22972
+ shortTitle: "Stoch",
22973
+ overlay: false,
22974
+ inputs: [
22975
+ lengthInput(14, "kLength", "%K length"),
22976
+ lengthInput(1, "kSmoothing", "%K smoothing", 500),
22977
+ lengthInput(3, "dLength", "%D smoothing", 500)
22978
+ ],
22979
+ compute: (bars, inputs) => {
22980
+ const k = sma(stoch(closes(bars), highs(bars), lows(bars), num3(inputs, "kLength", 14)), num3(inputs, "kSmoothing", 1));
22981
+ const d = sma(k, num3(inputs, "dLength", 3));
22982
+ return {
22983
+ plots: [
22984
+ { key: "k", title: "%K", values: k, color: SERIES_LINE, width: 2 },
22985
+ { key: "d", title: "%D", values: d, color: WARNING }
22986
+ ],
22987
+ levels: guideLevels(20, 80)
22988
+ };
22989
+ }
22990
+ };
22991
+ var stochasticRsi = {
22992
+ type: "stochastic-rsi",
22993
+ title: "Stochastic RSI",
22994
+ shortTitle: "Stoch RSI",
22995
+ overlay: false,
22996
+ inputs: [
22997
+ lengthInput(14, "rsiLength", "RSI length"),
22998
+ lengthInput(14, "stochLength", "Stochastic length"),
22999
+ lengthInput(3, "kSmoothing", "%K smoothing", 500),
23000
+ lengthInput(3, "dLength", "%D smoothing", 500),
23001
+ sourceInput()
23002
+ ],
23003
+ compute: (bars, inputs) => {
23004
+ const r = rsi(sourceValues(bars, str3(inputs, "source", "Close")), num3(inputs, "rsiLength", 14));
23005
+ const k = sma(stoch(r, r, r, num3(inputs, "stochLength", 14)), num3(inputs, "kSmoothing", 3));
23006
+ const d = sma(k, num3(inputs, "dLength", 3));
23007
+ return {
23008
+ plots: [
23009
+ { key: "k", title: "%K", values: k, color: SERIES_LINE, width: 2 },
23010
+ { key: "d", title: "%D", values: d, color: WARNING }
23011
+ ],
23012
+ levels: guideLevels(20, 80)
23013
+ };
23014
+ }
23015
+ };
23016
+ var macd = {
23017
+ type: "macd",
23018
+ title: "MACD",
23019
+ overlay: false,
23020
+ inputs: [
23021
+ lengthInput(12, "fastLength", "Fast length"),
23022
+ lengthInput(26, "slowLength", "Slow length"),
23023
+ lengthInput(9, "signalLength", "Signal smoothing", 500),
23024
+ sourceInput()
23025
+ ],
23026
+ compute: (bars, inputs) => {
23027
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
23028
+ const line = sub(ema(src, num3(inputs, "fastLength", 12)), ema(src, num3(inputs, "slowLength", 26)));
23029
+ const signal = ema(line, num3(inputs, "signalLength", 9));
23030
+ const hist = sub(line, signal);
23031
+ return {
23032
+ plots: [
23033
+ { key: "hist", title: "Histogram", values: hist, kind: "histogram", color: BULLISH, colors: histColors(hist), base: 0 },
23034
+ { key: "macd", title: "MACD", values: line, color: SERIES_LINE, width: 2 },
23035
+ { key: "signal", title: "Signal", values: signal, color: WARNING }
23036
+ ],
23037
+ levels: ZERO_LEVEL
23038
+ };
23039
+ }
23040
+ };
23041
+ var ppo = {
23042
+ type: "ppo",
23043
+ title: "Percentage Price Oscillator",
23044
+ shortTitle: "PPO",
23045
+ overlay: false,
23046
+ inputs: [
23047
+ lengthInput(12, "fastLength", "Fast length"),
23048
+ lengthInput(26, "slowLength", "Slow length"),
23049
+ lengthInput(9, "signalLength", "Signal smoothing", 500),
23050
+ sourceInput()
23051
+ ],
23052
+ compute: (bars, inputs) => {
23053
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
23054
+ const fast = ema(src, num3(inputs, "fastLength", 12));
23055
+ const slow = ema(src, num3(inputs, "slowLength", 26));
23056
+ const line = zip(fast, slow, (f, s) => s === 0 ? Number.NaN : (f - s) / s * 100);
23057
+ const signal = ema(line, num3(inputs, "signalLength", 9));
23058
+ const hist = sub(line, signal);
23059
+ return {
23060
+ plots: [
23061
+ { key: "hist", title: "Histogram", values: hist, kind: "histogram", color: BULLISH, colors: histColors(hist), base: 0 },
23062
+ { key: "ppo", title: "PPO", values: line, color: SERIES_LINE, width: 2 },
23063
+ { key: "signal", title: "Signal", values: signal, color: WARNING }
23064
+ ],
23065
+ levels: ZERO_LEVEL
23066
+ };
23067
+ }
23068
+ };
23069
+ var awesome = {
23070
+ type: "awesome-oscillator",
23071
+ title: "Awesome Oscillator",
23072
+ shortTitle: "AO",
23073
+ overlay: false,
23074
+ inputs: [],
23075
+ compute: (bars) => {
23076
+ const hl2 = sourceValues(bars, "HL2");
23077
+ const ao = sub(sma(hl2, 5), sma(hl2, 34));
23078
+ const colors = ao.map((x, i) => {
23079
+ if (!Number.isFinite(x)) return null;
23080
+ const prev2 = i > 0 ? ao[i - 1] : Number.NaN;
23081
+ return Number.isFinite(prev2) && x < prev2 ? BEARISH : BULLISH;
23082
+ });
23083
+ return {
23084
+ plots: [{ key: "ao", title: "AO", values: ao, kind: "histogram", color: BULLISH, colors, base: 0 }],
23085
+ levels: ZERO_LEVEL
23086
+ };
23087
+ }
23088
+ };
23089
+ var cci = {
23090
+ type: "commodity-channel-index",
23091
+ title: "Commodity Channel Index",
23092
+ shortTitle: "CCI",
23093
+ overlay: false,
23094
+ inputs: [lengthInput(20), sourceInput("HLC3"), colorInput()],
23095
+ compute: (bars, inputs) => {
23096
+ const len = num3(inputs, "length", 20);
23097
+ const src = sourceValues(bars, str3(inputs, "source", "HLC3"));
23098
+ const basis = sma(src, len);
23099
+ const dev = new Array(src.length).fill(Number.NaN);
23100
+ for (let i = len - 1; i < src.length; i++) {
23101
+ const mean = basis[i];
23102
+ if (!Number.isFinite(mean)) continue;
23103
+ let d = 0;
23104
+ for (let k = i - len + 1; k <= i; k++) d += Math.abs(src[k] - mean);
23105
+ dev[i] = d / len;
23106
+ }
23107
+ const values = src.map((x, i) => {
23108
+ const b = basis[i];
23109
+ const d = dev[i];
23110
+ return Number.isFinite(b) && Number.isFinite(d) && d !== 0 ? (x - b) / (0.015 * d) : Number.NaN;
23111
+ });
23112
+ return {
23113
+ plots: [{ key: "cci", title: "CCI", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23114
+ levels: guideLevels(-100, 100, 0)
23115
+ };
23116
+ }
23117
+ };
23118
+ var williamsR = {
23119
+ type: "williams-percent-r",
23120
+ title: "Williams %R",
23121
+ shortTitle: "%R",
23122
+ overlay: false,
23123
+ inputs: [lengthInput(14), colorInput()],
23124
+ compute: (bars, inputs) => {
23125
+ const values = map(stoch(closes(bars), highs(bars), lows(bars), num3(inputs, "length", 14)), (x) => x - 100);
23126
+ return {
23127
+ plots: [{ key: "wr", title: "%R", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23128
+ levels: guideLevels(-80, -20)
23129
+ };
23130
+ }
23131
+ };
23132
+ var cmo = {
23133
+ type: "chande-momentum-oscillator",
23134
+ title: "Chande Momentum Oscillator",
23135
+ shortTitle: "CMO",
23136
+ overlay: false,
23137
+ inputs: [lengthInput(9), sourceInput(), colorInput()],
23138
+ compute: (bars, inputs) => {
23139
+ const len = num3(inputs, "length", 9);
23140
+ const d = change(sourceValues(bars, str3(inputs, "source", "Close")));
23141
+ const su = sum(map(d, (x) => Math.max(x, 0)), len);
23142
+ const sd = sum(map(d, (x) => Math.max(-x, 0)), len);
23143
+ const values = zip(su, sd, (u, w) => u + w === 0 ? 0 : 100 * (u - w) / (u + w));
23144
+ return {
23145
+ plots: [{ key: "cmo", title: "CMO", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23146
+ levels: guideLevels(-50, 50, 0)
23147
+ };
23148
+ }
23149
+ };
23150
+ var connorsRsi = {
23151
+ type: "connors-rsi",
23152
+ title: "Connors RSI",
23153
+ shortTitle: "CRSI",
23154
+ overlay: false,
23155
+ inputs: [
23156
+ lengthInput(3, "rsiLength", "RSI length"),
23157
+ lengthInput(2, "streakLength", "Streak RSI length"),
23158
+ lengthInput(100, "rankLength", "Percent-rank length"),
23159
+ colorInput()
23160
+ ],
23161
+ compute: (bars, inputs) => {
23162
+ const c = closes(bars);
23163
+ const streak = new Array(c.length).fill(0);
23164
+ for (let i = 1; i < c.length; i++) {
23165
+ const d = c[i] - c[i - 1];
23166
+ streak[i] = d > 0 ? Math.max(streak[i - 1], 0) + 1 : d < 0 ? Math.min(streak[i - 1], 0) - 1 : 0;
23167
+ }
23168
+ const a = rsi(c, num3(inputs, "rsiLength", 3));
23169
+ const b = rsi(streak, num3(inputs, "streakLength", 2));
23170
+ const r = percentRank(roc(c, 1), num3(inputs, "rankLength", 100));
23171
+ const values = c.map((_, i) => {
23172
+ const x = a[i];
23173
+ const y = b[i];
23174
+ const z = r[i];
23175
+ return Number.isFinite(x) && Number.isFinite(y) && Number.isFinite(z) ? (x + y + z) / 3 : Number.NaN;
23176
+ });
23177
+ return {
23178
+ plots: [{ key: "crsi", title: "CRSI", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23179
+ levels: guideLevels(20, 80, 50)
23180
+ };
23181
+ }
23182
+ };
23183
+ var fisher = {
23184
+ type: "fisher-transform",
23185
+ title: "Fisher Transform",
23186
+ shortTitle: "Fisher",
23187
+ overlay: false,
23188
+ inputs: [lengthInput(9)],
23189
+ compute: (bars, inputs) => {
23190
+ const len = num3(inputs, "length", 9);
23191
+ const hl2 = sourceValues(bars, "HL2");
23192
+ const hi = highest(hl2, len);
23193
+ const lo = lowest(hl2, len);
23194
+ const n = bars.length;
23195
+ const out = new Array(n).fill(Number.NaN);
23196
+ let value = 0;
23197
+ let fish = 0;
23198
+ for (let i = 0; i < n; i++) {
23199
+ const h = hi[i];
23200
+ const l = lo[i];
23201
+ if (!Number.isFinite(h) || !Number.isFinite(l)) continue;
23202
+ const norm = h === l ? 0 : (hl2[i] - l) / (h - l) - 0.5;
23203
+ value = Math.max(-0.999, Math.min(0.999, 0.66 * norm + 0.67 * value));
23204
+ fish = 0.5 * Math.log((1 + value) / (1 - value)) + 0.5 * fish;
23205
+ out[i] = fish;
23206
+ }
23207
+ return {
23208
+ plots: [
23209
+ { key: "fisher", title: "Fisher", values: out, color: SERIES_LINE, width: 2 },
23210
+ { key: "trigger", title: "Trigger", values: shift(out, 1), color: WARNING }
23211
+ ],
23212
+ levels: ZERO_LEVEL
23213
+ };
23214
+ }
23215
+ };
23216
+ var trix = {
23217
+ type: "trix",
23218
+ title: "TRIX",
23219
+ overlay: false,
23220
+ inputs: [lengthInput(18), colorInput()],
23221
+ compute: (bars, inputs) => {
23222
+ const len = num3(inputs, "length", 18);
23223
+ const logClose = map(closes(bars), Math.log);
23224
+ const smooth = ema(ema(ema(logClose, len), len), len);
23225
+ const values = map(change(smooth), (x) => x * 1e4);
23226
+ return {
23227
+ plots: [{ key: "trix", title: "TRIX", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23228
+ levels: ZERO_LEVEL
23229
+ };
23230
+ }
23231
+ };
23232
+ function tsiLine(src, long, short) {
23233
+ const mom = change(src);
23234
+ const numer = ema(ema(mom, long), short);
23235
+ const denom = ema(ema(map(mom, Math.abs), long), short);
23236
+ return zip(numer, denom, (a, b) => b === 0 ? 0 : 100 * a / b);
23237
+ }
23238
+ var tsi = {
23239
+ type: "true-strength-index",
23240
+ title: "True Strength Index",
23241
+ shortTitle: "TSI",
23242
+ overlay: false,
23243
+ inputs: [lengthInput(25, "longLength", "Long length"), lengthInput(13, "shortLength", "Short length"), lengthInput(13, "signalLength", "Signal length")],
23244
+ compute: (bars, inputs) => {
23245
+ const line = tsiLine(closes(bars), num3(inputs, "longLength", 25), num3(inputs, "shortLength", 13));
23246
+ return {
23247
+ plots: [
23248
+ { key: "tsi", title: "TSI", values: line, color: SERIES_LINE, width: 2 },
23249
+ { key: "signal", title: "Signal", values: ema(line, num3(inputs, "signalLength", 13)), color: WARNING }
23250
+ ],
23251
+ levels: ZERO_LEVEL
23252
+ };
23253
+ }
23254
+ };
23255
+ var smiErgodic = {
23256
+ type: "smi-ergodic",
23257
+ title: "SMI Ergodic Oscillator",
23258
+ shortTitle: "SMIE",
23259
+ overlay: false,
23260
+ inputs: [lengthInput(5, "shortLength", "Short length"), lengthInput(20, "longLength", "Long length"), lengthInput(5, "signalLength", "Signal length")],
23261
+ compute: (bars, inputs) => {
23262
+ const line = tsiLine(closes(bars), num3(inputs, "longLength", 20), num3(inputs, "shortLength", 5));
23263
+ const signal = ema(line, num3(inputs, "signalLength", 5));
23264
+ const osc = sub(line, signal);
23265
+ return {
23266
+ plots: [
23267
+ { key: "osc", title: "Oscillator", values: osc, kind: "histogram", color: BULLISH, colors: histColors(osc), base: 0 },
23268
+ { key: "smi", title: "SMI", values: line, color: SERIES_LINE, width: 2 },
23269
+ { key: "signal", title: "Signal", values: signal, color: WARNING }
23270
+ ],
23271
+ levels: ZERO_LEVEL
23272
+ };
23273
+ }
23274
+ };
23275
+ var stc = {
23276
+ type: "schaff-trend-cycle",
23277
+ title: "Schaff Trend Cycle",
23278
+ shortTitle: "Schaff Trend Cycle",
23279
+ overlay: false,
23280
+ inputs: [lengthInput(10, "cycleLength", "Cycle length"), lengthInput(23, "fastLength", "Fast length"), lengthInput(50, "slowLength", "Slow length")],
23281
+ compute: (bars, inputs) => {
23282
+ const len = num3(inputs, "cycleLength", 10);
23283
+ const src = closes(bars);
23284
+ const macdLine = sub(ema(src, num3(inputs, "fastLength", 23)), ema(src, num3(inputs, "slowLength", 50)));
23285
+ const stochOf = (v) => {
23286
+ const lo = lowest(v, len);
23287
+ const hi = highest(v, len);
23288
+ const out = new Array(v.length).fill(Number.NaN);
23289
+ let prevRaw = Number.NaN;
23290
+ let smoothed = Number.NaN;
23291
+ for (let i = 0; i < v.length; i++) {
23292
+ const x = v[i];
23293
+ const l = lo[i];
23294
+ const h = hi[i];
23295
+ if (!Number.isFinite(x) || !Number.isFinite(l) || !Number.isFinite(h)) continue;
23296
+ const raw = h > l ? (x - l) / (h - l) * 100 : Number.isFinite(prevRaw) ? prevRaw : 50;
23297
+ prevRaw = raw;
23298
+ smoothed = Number.isFinite(smoothed) ? smoothed + 0.5 * (raw - smoothed) : raw;
23299
+ out[i] = smoothed;
23300
+ }
23301
+ return out;
23302
+ };
23303
+ const values = stochOf(stochOf(macdLine));
23304
+ return {
23305
+ plots: [{ key: "stc", title: "STC", values, color: SERIES_LINE, width: 2 }],
23306
+ levels: guideLevels(25, 75)
23307
+ };
23308
+ }
23309
+ };
23310
+ var kst = {
23311
+ type: "know-sure-thing",
23312
+ title: "Know Sure Thing",
23313
+ shortTitle: "KST",
23314
+ overlay: false,
23315
+ inputs: [sourceInput(), lengthInput(9, "signalLength", "Signal length")],
23316
+ compute: (bars, inputs) => {
23317
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
23318
+ const line = [
23319
+ sma(roc(src, 10), 10),
23320
+ sma(roc(src, 15), 10),
23321
+ sma(roc(src, 20), 10),
23322
+ sma(roc(src, 30), 15)
23323
+ ].reduce((acc, r, i) => zip(acc, r, (a, b) => a + b * (i + 1)), new Array(src.length).fill(0));
23324
+ return {
23325
+ plots: [
23326
+ { key: "kst", title: "KST", values: line, color: SERIES_LINE, width: 2 },
23327
+ { key: "signal", title: "Signal", values: sma(line, num3(inputs, "signalLength", 9)), color: WARNING }
23328
+ ],
23329
+ levels: ZERO_LEVEL
23330
+ };
23331
+ }
23332
+ };
23333
+ var coppock = {
23334
+ type: "coppock-curve",
23335
+ title: "Coppock Curve",
23336
+ shortTitle: "Coppock",
23337
+ overlay: false,
23338
+ inputs: [
23339
+ lengthInput(10, "wmaLength", "WMA length"),
23340
+ lengthInput(14, "longRoc", "Long RoC length"),
23341
+ lengthInput(11, "shortRoc", "Short RoC length"),
23342
+ sourceInput(),
23343
+ colorInput()
23344
+ ],
23345
+ compute: (bars, inputs) => {
23346
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
23347
+ const values = wma(zip(roc(src, num3(inputs, "longRoc", 14)), roc(src, num3(inputs, "shortRoc", 11)), (a, b) => a + b), num3(inputs, "wmaLength", 10));
23348
+ return {
23349
+ plots: [{ key: "coppock", title: "Coppock", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23350
+ levels: ZERO_LEVEL
23351
+ };
23352
+ }
23353
+ };
23354
+ var dpo = {
23355
+ type: "detrended-price-oscillator",
23356
+ title: "Detrended Price Oscillator",
23357
+ shortTitle: "DPO",
23358
+ overlay: false,
23359
+ inputs: [lengthInput(21), colorInput()],
23360
+ compute: (bars, inputs) => {
23361
+ const len = num3(inputs, "length", 21);
23362
+ const barsback = Math.floor(len / 2) + 1;
23363
+ const values = sub(closes(bars), shift(sma(closes(bars), len), barsback));
23364
+ return {
23365
+ plots: [{ key: "dpo", title: "DPO", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23366
+ levels: ZERO_LEVEL
23367
+ };
23368
+ }
23369
+ };
23370
+ var rocSpec = {
23371
+ type: "rate-of-change",
23372
+ title: "Rate of Change",
23373
+ shortTitle: "ROC",
23374
+ overlay: false,
23375
+ inputs: [lengthInput(9), sourceInput(), colorInput()],
23376
+ compute: (bars, inputs) => ({
23377
+ plots: [
23378
+ {
23379
+ key: "roc",
23380
+ title: "RoC",
23381
+ values: roc(sourceValues(bars, str3(inputs, "source", "Close")), num3(inputs, "length", 9)),
23382
+ color: str3(inputs, "color", SERIES_LINE),
23383
+ width: 2
23384
+ }
23385
+ ],
23386
+ levels: ZERO_LEVEL
23387
+ })
23388
+ };
23389
+ var ultimate = {
23390
+ type: "ultimate-oscillator",
23391
+ title: "Ultimate Oscillator",
23392
+ shortTitle: "UO",
23393
+ overlay: false,
23394
+ inputs: [lengthInput(7, "fastLength", "Fast length"), lengthInput(14, "middleLength", "Middle length"), lengthInput(28, "slowLength", "Slow length"), colorInput()],
23395
+ compute: (bars, inputs) => {
23396
+ const n = bars.length;
23397
+ const bp = new Array(n).fill(Number.NaN);
23398
+ const tr = new Array(n).fill(Number.NaN);
23399
+ for (let i = 0; i < n; i++) {
23400
+ const b = bars[i];
23401
+ const pc = i > 0 ? bars[i - 1].close : b.close;
23402
+ const lo = Math.min(b.low, pc);
23403
+ const hi = Math.max(b.high, pc);
23404
+ bp[i] = b.close - lo;
23405
+ tr[i] = hi - lo;
23406
+ }
23407
+ const avg = (len) => zip(sum(bp, len), sum(tr, len), (a, b) => b === 0 ? Number.NaN : a / b);
23408
+ const a7 = avg(num3(inputs, "fastLength", 7));
23409
+ const a14 = avg(num3(inputs, "middleLength", 14));
23410
+ const a28 = avg(num3(inputs, "slowLength", 28));
23411
+ const values = a7.map((x, i) => {
23412
+ const y = a14[i];
23413
+ const z = a28[i];
23414
+ return Number.isFinite(x) && Number.isFinite(y) && Number.isFinite(z) ? 100 * (4 * x + 2 * y + z) / 7 : Number.NaN;
23415
+ });
23416
+ return {
23417
+ plots: [{ key: "uo", title: "UO", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23418
+ levels: guideLevels(30, 70, 50)
23419
+ };
23420
+ }
23421
+ };
23422
+ var rvgi = {
23423
+ type: "relative-vigor-index",
23424
+ title: "Relative Vigor Index",
23425
+ shortTitle: "RVGI",
23426
+ overlay: false,
23427
+ inputs: [lengthInput(10)],
23428
+ compute: (bars, inputs) => {
23429
+ const len = num3(inputs, "length", 10);
23430
+ const co = swma(sub(closes(bars), opens(bars)));
23431
+ const hl = swma(sub(highs(bars), lows(bars)));
23432
+ const line = zip(sum(co, len), sum(hl, len), (a, b) => b === 0 ? 0 : a / b);
23433
+ return {
23434
+ plots: [
23435
+ { key: "rvgi", title: "RVGI", values: line, color: SERIES_LINE, width: 2 },
23436
+ { key: "signal", title: "Signal", values: swma(line), color: WARNING }
23437
+ ],
23438
+ levels: ZERO_LEVEL
23439
+ };
23440
+ }
23441
+ };
23442
+ var relVolatility = {
23443
+ type: "relative-volatility-index",
23444
+ title: "Relative Volatility Index",
23445
+ shortTitle: "RVI",
23446
+ overlay: false,
23447
+ inputs: [lengthInput(10, "stdevLength", "StdDev length"), lengthInput(14, "smoothLength", "Smoothing length"), sourceInput(), colorInput()],
23448
+ compute: (bars, inputs) => {
23449
+ const smoothLen = num3(inputs, "smoothLength", 14);
23450
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
23451
+ const dev = stdev(src, num3(inputs, "stdevLength", 10));
23452
+ const d = change(src);
23453
+ const up = rma(zip(dev, d, (s, x) => x > 0 ? s : 0), smoothLen);
23454
+ const dn = rma(zip(dev, d, (s, x) => x <= 0 ? s : 0), smoothLen);
23455
+ const values = zip(up, dn, (u, w) => u + w === 0 ? 50 : 100 * u / (u + w));
23456
+ return {
23457
+ plots: [{ key: "rvi", title: "RVI", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23458
+ levels: guideLevels(20, 80, 50)
23459
+ };
23460
+ }
23461
+ };
23462
+ var bop = {
23463
+ type: "balance-of-power",
23464
+ title: "Balance of Power",
23465
+ shortTitle: "BOP",
23466
+ overlay: false,
23467
+ inputs: [colorInput()],
23468
+ compute: (bars, inputs) => {
23469
+ const values = bars.map((b) => b.high === b.low ? 0 : (b.close - b.open) / (b.high - b.low));
23470
+ return {
23471
+ plots: [{ key: "bop", title: "BOP", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23472
+ levels: ZERO_LEVEL
23473
+ };
23474
+ }
23475
+ };
23476
+ var elderRay = {
23477
+ type: "elder-ray",
23478
+ title: "Elder Ray",
23479
+ overlay: false,
23480
+ inputs: [lengthInput(13)],
23481
+ compute: (bars, inputs) => {
23482
+ const basis = ema(closes(bars), num3(inputs, "length", 13));
23483
+ return {
23484
+ plots: [
23485
+ { key: "bull", title: "Bull power", values: sub(highs(bars), basis), kind: "histogram", color: BULLISH, base: 0 },
23486
+ { key: "bear", title: "Bear power", values: sub(lows(bars), basis), kind: "histogram", color: BEARISH, base: 0 }
23487
+ ],
23488
+ levels: ZERO_LEVEL
23489
+ };
23490
+ }
23491
+ };
23492
+ var ttmSqueeze = {
23493
+ type: "ttm-squeeze",
23494
+ title: "TTM Squeeze",
23495
+ overlay: false,
23496
+ inputs: [
23497
+ lengthInput(20),
23498
+ { key: "bbMult", title: "Bollinger multiplier", type: "float", defval: 2, min: 0.1, max: 50, step: 0.1 },
23499
+ { key: "kcMult", title: "Keltner multiplier", type: "float", defval: 1.5, min: 0.1, max: 50, step: 0.1 }
23500
+ ],
23501
+ compute: (bars, inputs) => {
23502
+ const len = num3(inputs, "length", 20);
23503
+ const c = closes(bars);
23504
+ const basis = sma(c, len);
23505
+ const dev = map(stdev(c, len), (x) => x * num3(inputs, "bbMult", 2));
23506
+ const kcRange = map(rma(trueRange(bars), len), (x) => x * num3(inputs, "kcMult", 1.5));
23507
+ const donchianMid = zip(highest(highs(bars), len), lowest(lows(bars), len), (h, l) => (h + l) / 2);
23508
+ const anchor = zip(donchianMid, basis, (d, b) => (d + b) / 2);
23509
+ const mom = linreg(sub(c, anchor), len);
23510
+ const squeezed = c.map((_, i) => {
23511
+ const d = dev[i];
23512
+ const r = kcRange[i];
23513
+ return Number.isFinite(d) && Number.isFinite(r) ? d < r : false;
23514
+ });
23515
+ const stateColors = squeezed.map((s, i) => Number.isFinite(mom[i]) ? s ? BEARISH : BULLISH : null);
23516
+ return {
23517
+ plots: [
23518
+ { key: "mom", title: "Momentum", values: mom, kind: "histogram", color: BULLISH, colors: histColors(mom), base: 0 },
23519
+ { key: "squeeze", title: "Squeeze", values: mom.map((x) => Number.isFinite(x) ? 0 : Number.NaN), kind: "cross", color: INFO, colors: stateColors }
23520
+ ]
23521
+ };
23522
+ }
23523
+ };
23524
+ var oscillatorSpecs = [
23525
+ rsiSpec,
23526
+ stochastic,
23527
+ stochasticRsi,
23528
+ macd,
23529
+ ppo,
23530
+ awesome,
23531
+ cci,
23532
+ williamsR,
23533
+ cmo,
23534
+ connorsRsi,
23535
+ fisher,
23536
+ trix,
23537
+ tsi,
23538
+ smiErgodic,
23539
+ stc,
23540
+ kst,
23541
+ coppock,
23542
+ dpo,
23543
+ rocSpec,
23544
+ ultimate,
23545
+ rvgi,
23546
+ relVolatility,
23547
+ bop,
23548
+ elderRay,
23549
+ ttmSqueeze
23550
+ ];
23551
+
23552
+ // src/core/native-indicators/classics/trend.ts
23553
+ var aroon = {
23554
+ type: "aroon",
23555
+ title: "Aroon",
23556
+ overlay: false,
23557
+ inputs: [lengthInput(14)],
23558
+ compute: (bars, inputs) => {
23559
+ const len = num3(inputs, "length", 14);
23560
+ const up = map(barsSinceHighest(highs(bars), len + 1), (x) => 100 * (len - x) / len);
23561
+ const down = map(barsSinceLowest(lows(bars), len + 1), (x) => 100 * (len - x) / len);
23562
+ return {
23563
+ plots: [
23564
+ { key: "up", title: "Aroon up", values: up, color: BULLISH, width: 2 },
23565
+ { key: "down", title: "Aroon down", values: down, color: BEARISH, width: 2 }
23566
+ ],
23567
+ levels: [
23568
+ { key: "upper", price: 100, color: NEUTRAL, lineStyle: "dotted" },
23569
+ { key: "lower", price: 0, color: NEUTRAL, lineStyle: "dotted" }
23570
+ ]
23571
+ };
23572
+ }
23573
+ };
23574
+ var adx = {
23575
+ type: "average-directional-index",
23576
+ title: "Average Directional Index",
23577
+ shortTitle: "ADX",
23578
+ overlay: false,
23579
+ inputs: [lengthInput(14, "diLength", "DI length"), lengthInput(14, "adxLength", "ADX smoothing")],
23580
+ compute: (bars, inputs) => {
23581
+ const diLen = num3(inputs, "diLength", 14);
23582
+ const adxLen = num3(inputs, "adxLength", 14);
23583
+ const n = bars.length;
23584
+ const plusDm = new Array(n).fill(Number.NaN);
23585
+ const minusDm = new Array(n).fill(Number.NaN);
23586
+ for (let i = 1; i < n; i++) {
23587
+ const up = bars[i].high - bars[i - 1].high;
23588
+ const dn = bars[i - 1].low - bars[i].low;
23589
+ plusDm[i] = up > dn && up > 0 ? up : 0;
23590
+ minusDm[i] = dn > up && dn > 0 ? dn : 0;
23591
+ }
23592
+ const atrLine = rma(trueRange(bars), diLen);
23593
+ const plusDi = zip(rma(plusDm, diLen), atrLine, (d, a) => a === 0 ? 0 : 100 * d / a);
23594
+ const minusDi = zip(rma(minusDm, diLen), atrLine, (d, a) => a === 0 ? 0 : 100 * d / a);
23595
+ const dx = zip(plusDi, minusDi, (p, m) => p + m === 0 ? 0 : 100 * Math.abs(p - m) / (p + m));
23596
+ return {
23597
+ plots: [
23598
+ { key: "adx", title: "ADX", values: rma(dx, adxLen), color: SERIES_LINE, width: 2 },
23599
+ { key: "plusDi", title: "+DI", values: plusDi, color: BULLISH },
23600
+ { key: "minusDi", title: "-DI", values: minusDi, color: BEARISH }
23601
+ ],
23602
+ levels: [{ key: "threshold", price: 25, color: NEUTRAL, lineStyle: "dotted" }]
23603
+ };
23604
+ }
23605
+ };
23606
+ var vortex = {
23607
+ type: "vortex-indicator",
23608
+ title: "Vortex Indicator",
23609
+ shortTitle: "VI",
23610
+ overlay: false,
23611
+ inputs: [lengthInput(14)],
23612
+ compute: (bars, inputs) => {
23613
+ const len = num3(inputs, "length", 14);
23614
+ const n = bars.length;
23615
+ const vmPlus = new Array(n).fill(Number.NaN);
23616
+ const vmMinus = new Array(n).fill(Number.NaN);
23617
+ for (let i = 1; i < n; i++) {
23618
+ vmPlus[i] = Math.abs(bars[i].high - bars[i - 1].low);
23619
+ vmMinus[i] = Math.abs(bars[i].low - bars[i - 1].high);
23620
+ }
23621
+ const trSum = sum(trueRange(bars), len);
23622
+ return {
23623
+ plots: [
23624
+ { key: "viPlus", title: "VI+", values: zip(sum(vmPlus, len), trSum, (v, t) => t === 0 ? Number.NaN : v / t), color: BULLISH, width: 2 },
23625
+ { key: "viMinus", title: "VI-", values: zip(sum(vmMinus, len), trSum, (v, t) => t === 0 ? Number.NaN : v / t), color: BEARISH, width: 2 }
23626
+ ],
23627
+ levels: [{ key: "one", price: 1, color: NEUTRAL, lineStyle: "dotted" }]
23628
+ };
23629
+ }
23630
+ };
23631
+ var trendSpecs = [aroon, adx, vortex];
23632
+
23633
+ // src/core/native-indicators/classics/volatility.ts
23634
+ var atrSpec = {
23635
+ type: "average-true-range",
23636
+ title: "Average True Range",
23637
+ shortTitle: "ATR",
23638
+ overlay: false,
23639
+ inputs: [lengthInput(14), colorInput()],
23640
+ compute: (bars, inputs) => ({
23641
+ plots: [{ key: "atr", title: "ATR", values: atr(bars, num3(inputs, "length", 14)), color: str3(inputs, "color", SERIES_LINE), width: 2 }]
23642
+ })
23643
+ };
23644
+ var historicalVolatility = {
23645
+ type: "historical-volatility",
23646
+ title: "Historical Volatility",
23647
+ shortTitle: "HV",
23648
+ overlay: false,
23649
+ inputs: [
23650
+ lengthInput(10),
23651
+ { 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)" },
23652
+ colorInput()
23653
+ ],
23654
+ compute: (bars, inputs) => {
23655
+ const logReturns = map(change(map(closes(bars), Math.log)), (x) => x);
23656
+ const values = map(stdev(logReturns, num3(inputs, "length", 10)), (x) => x * Math.sqrt(num3(inputs, "annual", 365)) * 100);
23657
+ return { plots: [{ key: "hv", title: "HV", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }] };
23658
+ }
23659
+ };
23660
+ var chaikinVolatility = {
23661
+ type: "chaikin-volatility",
23662
+ title: "Chaikin Volatility",
23663
+ shortTitle: "CHV",
23664
+ overlay: false,
23665
+ inputs: [lengthInput(10, "emaLength", "EMA length"), lengthInput(10, "rocLength", "RoC length"), colorInput()],
23666
+ compute: (bars, inputs) => {
23667
+ const range = ema(sub(highs(bars), lows(bars)), num3(inputs, "emaLength", 10));
23668
+ return {
23669
+ plots: [{ key: "cv", title: "Chaikin Vol", values: roc(range, num3(inputs, "rocLength", 10)), color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23670
+ levels: [{ key: "zero", price: 0, color: NEUTRAL }]
23671
+ };
23672
+ }
23673
+ };
23674
+ var massIndex = {
23675
+ type: "mass-index",
23676
+ title: "Mass Index",
23677
+ shortTitle: "MI",
23678
+ overlay: false,
23679
+ inputs: [lengthInput(25), colorInput()],
23680
+ compute: (bars, inputs) => {
23681
+ const range = sub(highs(bars), lows(bars));
23682
+ const single = ema(range, 9);
23683
+ const ratio = zip(single, ema(single, 9), (a, b) => b === 0 ? Number.NaN : a / b);
23684
+ return {
23685
+ plots: [{ key: "mi", title: "Mass Index", values: sum(ratio, num3(inputs, "length", 25)), color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23686
+ levels: [{ key: "reversal", price: 27, color: NEUTRAL, lineStyle: "dotted" }]
23687
+ };
23688
+ }
23689
+ };
23690
+ var standardDeviation = {
23691
+ type: "standard-deviation",
23692
+ title: "Standard Deviation",
23693
+ shortTitle: "StdDev",
23694
+ overlay: false,
23695
+ inputs: [lengthInput(20), sourceInput(), colorInput()],
23696
+ compute: (bars, inputs) => ({
23697
+ plots: [
23698
+ {
23699
+ key: "stdev",
23700
+ title: "StdDev",
23701
+ values: stdev(sourceValues(bars, str3(inputs, "source", "Close")), num3(inputs, "length", 20)),
23702
+ color: str3(inputs, "color", SERIES_LINE),
23703
+ width: 2
23704
+ }
23705
+ ]
23706
+ })
23707
+ };
23708
+ var ulcerIndex = {
23709
+ type: "ulcer-index",
23710
+ title: "Ulcer Index",
23711
+ overlay: false,
23712
+ inputs: [lengthInput(14), colorInput()],
23713
+ compute: (bars, inputs) => {
23714
+ const len = num3(inputs, "length", 14);
23715
+ const c = closes(bars);
23716
+ const drawdown = zip(c, highest(c, len), (x, h) => h === 0 ? 0 : 100 * (x - h) / h);
23717
+ const values = map(sma(map(drawdown, (d) => d * d), len), Math.sqrt);
23718
+ return { plots: [{ key: "ui", title: "Ulcer", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }] };
23719
+ }
23720
+ };
23721
+ var choppiness = {
23722
+ type: "choppiness-index",
23723
+ title: "Choppiness Index",
23724
+ shortTitle: "CHOP",
23725
+ overlay: false,
23726
+ inputs: [lengthInput(14), colorInput()],
23727
+ compute: (bars, inputs) => {
23728
+ const len = num3(inputs, "length", 14);
23729
+ const trSum = sum(trueRange(bars), len);
23730
+ const span = zip(highest(highs(bars), len), lowest(lows(bars), len), (h, l) => h - l);
23731
+ const values = zip(trSum, span, (t, s) => s > 0 && t > 0 ? 100 * Math.log10(t / s) / Math.log10(len) : Number.NaN);
23732
+ return {
23733
+ plots: [{ key: "chop", title: "CHOP", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23734
+ levels: [
23735
+ { key: "upper", price: 61.8, color: NEUTRAL },
23736
+ { key: "lower", price: 38.2, color: NEUTRAL }
23737
+ ]
23738
+ };
23739
+ }
23740
+ };
23741
+ var volatilitySpecs = [atrSpec, historicalVolatility, chaikinVolatility, massIndex, standardDeviation, ulcerIndex, choppiness];
23742
+
23743
+ // src/core/native-indicators/classics/volume.ts
23744
+ function moneyFlowVolume(bars) {
23745
+ return bars.map((b) => {
23746
+ if (b.volume == null || !Number.isFinite(b.volume)) return Number.NaN;
23747
+ if (b.high === b.low) return 0;
23748
+ return (2 * b.close - b.high - b.low) / (b.high - b.low) * b.volume;
23749
+ });
23750
+ }
23751
+ var obv = {
23752
+ type: "on-balance-volume",
23753
+ title: "On Balance Volume",
23754
+ shortTitle: "OBV",
23755
+ overlay: false,
23756
+ inputs: [colorInput()],
23757
+ compute: (bars, inputs) => {
23758
+ const vol = volumes(bars);
23759
+ const d = change(closes(bars));
23760
+ const signed2 = zip(vol, d, (v, x) => x > 0 ? v : x < 0 ? -v : 0);
23761
+ return { plots: [{ key: "obv", title: "OBV", values: cumSum(signed2), color: str3(inputs, "color", SERIES_LINE), width: 2 }] };
23762
+ }
23763
+ };
23764
+ var accDist = {
23765
+ type: "accumulation-distribution",
23766
+ title: "Accumulation / Distribution",
23767
+ shortTitle: "A/D",
23768
+ overlay: false,
23769
+ inputs: [colorInput()],
23770
+ compute: (bars, inputs) => ({
23771
+ plots: [{ key: "ad", title: "A/D", values: cumSum(moneyFlowVolume(bars)), color: str3(inputs, "color", SERIES_LINE), width: 2 }]
23772
+ })
23773
+ };
23774
+ var cmf = {
23775
+ type: "chaikin-money-flow",
23776
+ title: "Chaikin Money Flow",
23777
+ shortTitle: "CMF",
23778
+ overlay: false,
23779
+ inputs: [lengthInput(20), colorInput()],
23780
+ compute: (bars, inputs) => {
23781
+ const len = num3(inputs, "length", 20);
23782
+ const values = zip(sum(moneyFlowVolume(bars), len), sum(volumes(bars), len), (m, v) => v === 0 ? 0 : m / v);
23783
+ return {
23784
+ plots: [{ key: "cmf", title: "CMF", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23785
+ levels: [{ key: "zero", price: 0, color: NEUTRAL }]
23786
+ };
23787
+ }
23788
+ };
23789
+ var chaikinOscillator = {
23790
+ type: "chaikin-oscillator",
23791
+ title: "Chaikin Oscillator",
23792
+ shortTitle: "Chaikin Osc",
23793
+ overlay: false,
23794
+ inputs: [lengthInput(3, "fastLength", "Fast length"), lengthInput(10, "slowLength", "Slow length"), colorInput()],
23795
+ compute: (bars, inputs) => {
23796
+ const ad = cumSum(moneyFlowVolume(bars));
23797
+ const values = sub(ema(ad, num3(inputs, "fastLength", 3)), ema(ad, num3(inputs, "slowLength", 10)));
23798
+ return {
23799
+ plots: [{ key: "co", title: "Chaikin Osc", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23800
+ levels: [{ key: "zero", price: 0, color: NEUTRAL }]
23801
+ };
23802
+ }
23803
+ };
23804
+ var eom = {
23805
+ type: "ease-of-movement",
23806
+ title: "Ease of Movement",
23807
+ shortTitle: "EOM",
23808
+ overlay: false,
23809
+ inputs: [lengthInput(14), { key: "divisor", title: "Divisor", type: "int", defval: 1e4, min: 1, max: 1e9, step: 1 }, colorInput()],
23810
+ compute: (bars, inputs) => {
23811
+ const div = num3(inputs, "divisor", 1e4);
23812
+ const hl2 = sourceValues(bars, "HL2");
23813
+ const move = change(hl2);
23814
+ const raw = bars.map((b, i) => {
23815
+ const m = move[i];
23816
+ if (b.volume == null || !Number.isFinite(b.volume) || b.volume === 0 || !Number.isFinite(m)) return Number.NaN;
23817
+ return div * m * (b.high - b.low) / b.volume;
23818
+ });
23819
+ return {
23820
+ plots: [{ key: "eom", title: "EOM", values: sma(raw, num3(inputs, "length", 14)), color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23821
+ levels: [{ key: "zero", price: 0, color: NEUTRAL }]
23822
+ };
23823
+ }
23824
+ };
23825
+ var forceIndex = {
23826
+ type: "force-index",
23827
+ title: "Force Index",
23828
+ shortTitle: "FI",
23829
+ overlay: false,
23830
+ inputs: [lengthInput(13), colorInput()],
23831
+ compute: (bars, inputs) => {
23832
+ const raw = zip(change(closes(bars)), volumes(bars), (d, v) => d * v);
23833
+ return {
23834
+ plots: [{ key: "force", title: "Force", values: ema(raw, num3(inputs, "length", 13)), color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23835
+ levels: [{ key: "zero", price: 0, color: NEUTRAL }]
23836
+ };
23837
+ }
23838
+ };
23839
+ var klinger = {
23840
+ type: "klinger-oscillator",
23841
+ title: "Klinger Oscillator",
23842
+ shortTitle: "KVO",
23843
+ overlay: false,
23844
+ inputs: [lengthInput(34, "fastLength", "Fast length"), lengthInput(55, "slowLength", "Slow length"), lengthInput(13, "signalLength", "Signal length")],
23845
+ compute: (bars, inputs) => {
23846
+ const hlc3 = sourceValues(bars, "HLC3");
23847
+ const trend = change(hlc3);
23848
+ const signedVolume = zip(volumes(bars), trend, (v, t) => t >= 0 ? v : -v);
23849
+ const line = sub(ema(signedVolume, num3(inputs, "fastLength", 34)), ema(signedVolume, num3(inputs, "slowLength", 55)));
23850
+ return {
23851
+ plots: [
23852
+ { key: "kvo", title: "KVO", values: line, color: SERIES_LINE, width: 2 },
23853
+ { key: "signal", title: "Signal", values: ema(line, num3(inputs, "signalLength", 13)), color: WARNING }
23854
+ ],
23855
+ levels: [{ key: "zero", price: 0, color: NEUTRAL }]
23856
+ };
23857
+ }
23858
+ };
23859
+ var mfi = {
23860
+ type: "money-flow-index",
23861
+ title: "Money Flow Index",
23862
+ shortTitle: "MFI",
23863
+ overlay: false,
23864
+ inputs: [lengthInput(14), colorInput()],
23865
+ compute: (bars, inputs) => {
23866
+ const len = num3(inputs, "length", 14);
23867
+ const tp = sourceValues(bars, "HLC3");
23868
+ const rawFlow = zip(tp, volumes(bars), (p, v) => p * v);
23869
+ const d = change(tp);
23870
+ const pos = sum(zip(rawFlow, d, (f, x) => x > 0 ? f : 0), len);
23871
+ const neg = sum(zip(rawFlow, d, (f, x) => x < 0 ? f : 0), len);
23872
+ const values = zip(pos, neg, (p, n) => n === 0 ? 100 : 100 - 100 / (1 + p / n));
23873
+ return {
23874
+ plots: [{ key: "mfi", title: "MFI", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23875
+ levels: [
23876
+ { key: "upper", price: 80, color: NEUTRAL },
23877
+ { key: "lower", price: 20, color: NEUTRAL }
23878
+ ]
23879
+ };
23880
+ }
23881
+ };
23882
+ function volumeIndex(bars, when) {
23883
+ const out = new Array(bars.length).fill(Number.NaN);
23884
+ let index = 1e3;
23885
+ let started = false;
23886
+ for (let i = 1; i < bars.length; i++) {
23887
+ const v = bars[i].volume;
23888
+ const pv = bars[i - 1].volume;
23889
+ const pc = bars[i - 1].close;
23890
+ if (v == null || pv == null || !Number.isFinite(v) || !Number.isFinite(pv) || pc === 0) {
23891
+ if (started) out[i] = index;
23892
+ continue;
23893
+ }
23894
+ started = true;
23895
+ if (when(v, pv)) index += (bars[i].close - pc) / pc * index;
23896
+ out[i] = index;
23897
+ }
23898
+ return out;
23899
+ }
23900
+ var nvi = {
23901
+ type: "negative-volume-index",
23902
+ title: "Negative Volume Index",
23903
+ shortTitle: "Negative Volume Index",
23904
+ overlay: false,
23905
+ inputs: [lengthInput(255, "signalLength", "Signal length"), colorInput()],
23906
+ compute: (bars, inputs) => {
23907
+ const line = volumeIndex(bars, (v, pv) => v < pv);
23908
+ return {
23909
+ plots: [
23910
+ { key: "nvi", title: "NVI", values: line, color: str3(inputs, "color", SERIES_LINE), width: 2 },
23911
+ { key: "signal", title: "Signal", values: ema(line, num3(inputs, "signalLength", 255)), color: WARNING }
23912
+ ]
23913
+ };
23914
+ }
23915
+ };
23916
+ var pvi = {
23917
+ type: "positive-volume-index",
23918
+ title: "Positive Volume Index",
23919
+ shortTitle: "Positive Volume Index",
23920
+ overlay: false,
23921
+ inputs: [lengthInput(255, "signalLength", "Signal length"), colorInput()],
23922
+ compute: (bars, inputs) => {
23923
+ const line = volumeIndex(bars, (v, pv) => v > pv);
23924
+ return {
23925
+ plots: [
23926
+ { key: "pvi", title: "PVI", values: line, color: str3(inputs, "color", SERIES_LINE), width: 2 },
23927
+ { key: "signal", title: "Signal", values: ema(line, num3(inputs, "signalLength", 255)), color: WARNING }
23928
+ ]
23929
+ };
23930
+ }
23931
+ };
23932
+ var pvt = {
23933
+ type: "price-volume-trend",
23934
+ title: "Price Volume Trend",
23935
+ shortTitle: "PVT",
23936
+ overlay: false,
23937
+ inputs: [colorInput()],
23938
+ compute: (bars, inputs) => {
23939
+ const c = closes(bars);
23940
+ const raw = bars.map((b, i) => {
23941
+ if (i === 0 || b.volume == null || !Number.isFinite(b.volume)) return Number.NaN;
23942
+ const pc = c[i - 1];
23943
+ return pc === 0 ? 0 : (c[i] - pc) / pc * b.volume;
23944
+ });
23945
+ return { plots: [{ key: "pvt", title: "PVT", values: cumSum(raw), color: str3(inputs, "color", SERIES_LINE), width: 2 }] };
23946
+ }
23947
+ };
23948
+ var volumeOscillator = {
23949
+ type: "volume-oscillator",
23950
+ title: "Volume Oscillator",
23951
+ shortTitle: "Vol Osc",
23952
+ overlay: false,
23953
+ inputs: [lengthInput(5, "fastLength", "Fast length"), lengthInput(10, "slowLength", "Slow length"), colorInput()],
23954
+ compute: (bars, inputs) => {
23955
+ const vol = volumes(bars);
23956
+ const fast = ema(vol, num3(inputs, "fastLength", 5));
23957
+ const slow = ema(vol, num3(inputs, "slowLength", 10));
23958
+ const values = zip(fast, slow, (f, s) => s === 0 ? Number.NaN : 100 * (f - s) / s);
23959
+ return {
23960
+ plots: [{ key: "vo", title: "Volume Osc", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23961
+ levels: [{ key: "zero", price: 0, color: NEUTRAL }]
23962
+ };
23963
+ }
23964
+ };
23965
+ var pvo = {
23966
+ type: "pvo",
23967
+ title: "Percentage Volume Oscillator",
23968
+ shortTitle: "PVO",
23969
+ overlay: false,
23970
+ inputs: [lengthInput(12, "fastLength", "Fast length"), lengthInput(26, "slowLength", "Slow length"), lengthInput(9, "signalLength", "Signal length")],
23971
+ compute: (bars, inputs) => {
23972
+ const vol = volumes(bars);
23973
+ const fast = ema(vol, num3(inputs, "fastLength", 12));
23974
+ const slow = ema(vol, num3(inputs, "slowLength", 26));
23975
+ const line = zip(fast, slow, (f, s) => s === 0 ? Number.NaN : 100 * (f - s) / s);
23976
+ const signal = ema(line, num3(inputs, "signalLength", 9));
23977
+ const hist = sub(line, signal);
23978
+ return {
23979
+ plots: [
23980
+ { key: "hist", title: "Histogram", values: hist, kind: "histogram", color: BULLISH, colors: hist.map((x) => Number.isFinite(x) ? x >= 0 ? BULLISH : BEARISH : null), base: 0 },
23981
+ { key: "pvo", title: "PVO", values: line, color: SERIES_LINE, width: 2 },
23982
+ { key: "signal", title: "Signal", values: signal, color: WARNING }
23983
+ ],
23984
+ levels: [{ key: "zero", price: 0, color: NEUTRAL }]
23985
+ };
23986
+ }
23987
+ };
23988
+ var vfi = {
23989
+ type: "volume-flow-indicator",
23990
+ title: "Volume Flow Indicator",
23991
+ shortTitle: "Volume Flow Indicator",
23992
+ overlay: false,
23993
+ inputs: [
23994
+ lengthInput(130),
23995
+ { key: "coef", title: "Cutoff coefficient", type: "float", defval: 0.2, min: 0.01, max: 10, step: 0.01 },
23996
+ { key: "volCoef", title: "Volume cap", type: "float", defval: 2.5, min: 0.1, max: 50, step: 0.1 },
23997
+ lengthInput(3, "smoothLength", "Smoothing length")
23998
+ ],
23999
+ compute: (bars, inputs) => {
24000
+ const len = num3(inputs, "length", 130);
24001
+ const coef = num3(inputs, "coef", 0.2);
24002
+ const volCoef = num3(inputs, "volCoef", 2.5);
24003
+ const typical = map(sourceValues(bars, "HLC3"), Math.log);
24004
+ const inter = change(typical);
24005
+ const vInter = stdev(inter, 30);
24006
+ const vol = volumes(bars);
24007
+ const vAve = sma(vol, len);
24008
+ const mf = change(sourceValues(bars, "HLC3"));
24009
+ const raw = bars.map((b, i) => {
24010
+ const cutoff = Number.isFinite(vInter[i]) ? coef * vInter[i] * b.close : Number.NaN;
24011
+ const v = vol[i];
24012
+ const ave = i > 0 ? vAve[i - 1] : Number.NaN;
24013
+ const m = mf[i];
24014
+ if (!Number.isFinite(cutoff) || !Number.isFinite(v) || !Number.isFinite(ave) || !Number.isFinite(m)) return Number.NaN;
24015
+ const capped = Math.min(v, ave * volCoef);
24016
+ return m > cutoff ? capped : m < -cutoff ? -capped : 0;
24017
+ });
24018
+ const values = bars.map((_, i) => {
24019
+ const ave = i > 0 ? vAve[i - 1] : Number.NaN;
24020
+ return Number.isFinite(ave) && ave !== 0 ? ave : Number.NaN;
24021
+ });
24022
+ const flowSum = sum(raw, len);
24023
+ const line = zip(flowSum, values, (f, a) => f / a);
24024
+ return {
24025
+ plots: [{ key: "vfi", title: "VFI", values: sma(line, num3(inputs, "smoothLength", 3)), color: SERIES_LINE, width: 2 }],
24026
+ levels: [{ key: "zero", price: 0, color: NEUTRAL }]
24027
+ };
24028
+ }
24029
+ };
24030
+ var intradayIntensity = {
24031
+ type: "intraday-intensity",
24032
+ title: "Intraday Intensity",
24033
+ shortTitle: "Intraday Intensity",
24034
+ overlay: false,
24035
+ inputs: [lengthInput(21), colorInput()],
24036
+ compute: (bars, inputs) => {
24037
+ const len = num3(inputs, "length", 21);
24038
+ const values = zip(sum(moneyFlowVolume(bars), len), sum(volumes(bars), len), (m, v) => v === 0 ? 0 : 100 * m / v);
24039
+ return {
24040
+ plots: [{ key: "ii", title: "II%", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
24041
+ levels: [{ key: "zero", price: 0, color: NEUTRAL }]
24042
+ };
24043
+ }
24044
+ };
24045
+ var volumeSpecs = [
24046
+ obv,
24047
+ accDist,
24048
+ cmf,
24049
+ chaikinOscillator,
24050
+ eom,
24051
+ forceIndex,
24052
+ klinger,
24053
+ mfi,
24054
+ nvi,
24055
+ pvi,
24056
+ pvt,
24057
+ volumeOscillator,
24058
+ pvo,
24059
+ vfi,
24060
+ intradayIntensity
24061
+ ];
24062
+
24063
+ // src/core/native-indicators/classics/overlays.ts
24064
+ var DAY_MS2 = 864e5;
24065
+ function periodKey(anchor, time) {
24066
+ if (anchor === "Week") return Math.floor((Math.floor(time / DAY_MS2) + 3) / 7);
24067
+ if (anchor === "Month") {
24068
+ const d = new Date(time);
24069
+ return d.getUTCFullYear() * 12 + d.getUTCMonth();
24070
+ }
24071
+ return Math.floor(time / DAY_MS2);
24072
+ }
24073
+ var parabolicSar = {
24074
+ type: "parabolic-sar",
24075
+ title: "Parabolic SAR",
24076
+ shortTitle: "SAR",
24077
+ overlay: true,
24078
+ inputs: [
24079
+ { key: "start", title: "Start", type: "float", defval: 0.02, min: 1e-3, max: 1, step: 1e-3 },
24080
+ { key: "increment", title: "Increment", type: "float", defval: 0.02, min: 1e-3, max: 1, step: 1e-3 },
24081
+ { key: "maximum", title: "Max value", type: "float", defval: 0.2, min: 0.01, max: 1, step: 0.01 }
24082
+ ],
24083
+ compute: (bars, inputs) => {
24084
+ const start = num3(inputs, "start", 0.02);
24085
+ const inc = num3(inputs, "increment", 0.02);
24086
+ const max = num3(inputs, "maximum", 0.2);
24087
+ const n = bars.length;
24088
+ const values = new Array(n).fill(Number.NaN);
24089
+ const colors = new Array(n).fill(null);
24090
+ if (n >= 2) {
24091
+ let long = bars[1].close >= bars[0].close;
24092
+ let sar = long ? bars[0].low : bars[0].high;
24093
+ let ep = long ? bars[0].high : bars[0].low;
24094
+ let af = start;
24095
+ for (let i = 1; i < n; i++) {
24096
+ const b = bars[i];
24097
+ sar += af * (ep - sar);
24098
+ if (long) sar = Math.min(sar, bars[i - 1].low, i >= 2 ? bars[i - 2].low : bars[i - 1].low);
24099
+ else sar = Math.max(sar, bars[i - 1].high, i >= 2 ? bars[i - 2].high : bars[i - 1].high);
24100
+ const reversed = long ? b.low < sar : b.high > sar;
24101
+ if (reversed) {
24102
+ long = !long;
24103
+ sar = ep;
24104
+ ep = long ? b.high : b.low;
24105
+ af = start;
24106
+ } else if (long && b.high > ep) {
24107
+ ep = b.high;
24108
+ af = Math.min(max, af + inc);
24109
+ } else if (!long && b.low < ep) {
24110
+ ep = b.low;
24111
+ af = Math.min(max, af + inc);
24112
+ }
24113
+ values[i] = sar;
24114
+ colors[i] = long ? BULLISH : BEARISH;
24115
+ }
24116
+ }
24117
+ return { plots: [{ key: "sar", title: "PSAR", values, kind: "circles", color: SERIES_LINE, colors }] };
24118
+ }
24119
+ };
24120
+ var vwap = {
24121
+ type: "vwap",
24122
+ title: "Volume Weighted Average Price",
24123
+ shortTitle: "VWAP",
24124
+ overlay: true,
24125
+ inputs: [
24126
+ { key: "anchor", title: "Anchor", type: "string", defval: "Day", options: ["Day", "Week", "Month"], tooltip: "Where the accumulation resets (UTC periods)" },
24127
+ { key: "source", title: "Source", type: "string", defval: "HLC3", options: ["HLC3", "OHLC4", "Close"] },
24128
+ colorInput(INFO)
24129
+ ],
24130
+ compute: (bars, inputs) => {
24131
+ const anchor = str3(inputs, "anchor", "Day");
24132
+ const source = str3(inputs, "source", "HLC3");
24133
+ const values = new Array(bars.length).fill(Number.NaN);
24134
+ let period = Number.NaN;
24135
+ let cumPV = 0;
24136
+ let cumV = 0;
24137
+ for (let i = 0; i < bars.length; i++) {
24138
+ const b = bars[i];
24139
+ const key = periodKey(anchor, b.time);
24140
+ if (key !== period) {
24141
+ period = key;
24142
+ cumPV = 0;
24143
+ cumV = 0;
24144
+ }
24145
+ const v = b.volume;
24146
+ if (v != null && Number.isFinite(v) && v > 0) {
24147
+ const tp = source === "Close" ? b.close : source === "OHLC4" ? (b.open + b.high + b.low + b.close) / 4 : (b.high + b.low + b.close) / 3;
24148
+ cumPV += tp * v;
24149
+ cumV += v;
24150
+ }
24151
+ if (cumV > 0) values[i] = cumPV / cumV;
24152
+ }
24153
+ return { plots: [{ key: "vwap", title: "VWAP", values, color: str3(inputs, "color", INFO), width: 2 }] };
24154
+ }
24155
+ };
24156
+ var pivotPoints = {
24157
+ type: "pivot-points",
24158
+ title: "Pivot Points",
24159
+ shortTitle: "Pivots",
24160
+ overlay: true,
24161
+ inputs: [{ key: "anchor", title: "Period", type: "string", defval: "Day", options: ["Day", "Week", "Month"] }],
24162
+ compute: (bars, inputs) => {
24163
+ const anchor = str3(inputs, "anchor", "Day");
24164
+ const aggs = /* @__PURE__ */ new Map();
24165
+ for (const b of bars) {
24166
+ const key = periodKey(anchor, b.time);
24167
+ const a = aggs.get(key);
24168
+ if (!a) aggs.set(key, { high: b.high, low: b.low, close: b.close });
24169
+ else {
24170
+ a.high = Math.max(a.high, b.high);
24171
+ a.low = Math.min(a.low, b.low);
24172
+ a.close = b.close;
24173
+ }
24174
+ }
24175
+ const n = bars.length;
24176
+ const mk = () => new Array(n).fill(Number.NaN);
24177
+ const levels = { p: mk(), r1: mk(), s1: mk(), r2: mk(), s2: mk(), r3: mk(), s3: mk() };
24178
+ for (let i = 0; i < n; i++) {
24179
+ const prev2 = aggs.get(periodKey(anchor, bars[i].time) - 1);
24180
+ if (!prev2) continue;
24181
+ const p = (prev2.high + prev2.low + prev2.close) / 3;
24182
+ levels.p[i] = p;
24183
+ levels.r1[i] = 2 * p - prev2.low;
24184
+ levels.s1[i] = 2 * p - prev2.high;
24185
+ levels.r2[i] = p + (prev2.high - prev2.low);
24186
+ levels.s2[i] = p - (prev2.high - prev2.low);
24187
+ levels.r3[i] = prev2.high + 2 * (p - prev2.low);
24188
+ levels.s3[i] = prev2.low - 2 * (prev2.high - p);
24189
+ }
24190
+ const plot = (key, title, color) => ({ key, title, kind: "step", values: levels[key], color });
24191
+ return {
24192
+ plots: [
24193
+ plot("p", "P", WARNING),
24194
+ plot("r1", "R1", BEARISH),
24195
+ plot("s1", "S1", BULLISH),
24196
+ plot("r2", "R2", BEARISH),
24197
+ plot("s2", "S2", BULLISH),
24198
+ plot("r3", "R3", BEARISH),
24199
+ plot("s3", "S3", BULLISH)
24200
+ ]
24201
+ };
24202
+ }
24203
+ };
24204
+ var fiftyTwoWeek = {
24205
+ type: "52-week-high-low",
24206
+ title: "52 Week High/Low",
24207
+ shortTitle: "52W H/L",
24208
+ overlay: true,
24209
+ inputs: [{ key: "weeks", title: "Weeks", type: "int", defval: 52, min: 1, max: 520, step: 1 }],
24210
+ compute: (bars, inputs) => {
24211
+ const span = num3(inputs, "weeks", 52) * 7 * DAY_MS2;
24212
+ const n = bars.length;
24213
+ const hi = new Array(n).fill(Number.NaN);
24214
+ const lo = new Array(n).fill(Number.NaN);
24215
+ const maxIdx = [];
24216
+ const minIdx = [];
24217
+ let from = 0;
24218
+ for (let i = 0; i < n; i++) {
24219
+ const b = bars[i];
24220
+ while (maxIdx.length > 0 && bars[maxIdx[maxIdx.length - 1]].high <= b.high) maxIdx.pop();
24221
+ maxIdx.push(i);
24222
+ while (minIdx.length > 0 && bars[minIdx[minIdx.length - 1]].low >= b.low) minIdx.pop();
24223
+ minIdx.push(i);
24224
+ while (bars[from].time < b.time - span) from += 1;
24225
+ while (maxIdx[0] < from) maxIdx.shift();
24226
+ while (minIdx[0] < from) minIdx.shift();
24227
+ hi[i] = bars[maxIdx[0]].high;
24228
+ lo[i] = bars[minIdx[0]].low;
24229
+ }
24230
+ return {
24231
+ plots: [
24232
+ { key: "high", title: "52W high", values: hi, kind: "step", color: BULLISH },
24233
+ { key: "low", title: "52W low", values: lo, kind: "step", color: BEARISH }
24234
+ ]
24235
+ };
24236
+ }
24237
+ };
24238
+ var zigzag = {
24239
+ type: "zigzag",
24240
+ title: "ZigZag",
24241
+ overlay: true,
24242
+ inputs: [
24243
+ { key: "deviation", title: "Deviation %", type: "float", defval: 5, min: 0.01, max: 100, step: 0.1 },
24244
+ { key: "depth", title: "Depth", type: "int", defval: 10, min: 1, max: 500, step: 1 },
24245
+ colorInput()
24246
+ ],
24247
+ compute: (bars, inputs) => {
24248
+ const dev = num3(inputs, "deviation", 5) / 100;
24249
+ const depth = num3(inputs, "depth", 10);
24250
+ const pivots = [];
24251
+ if (bars.length > 1) {
24252
+ let up = bars[1].close >= bars[0].close;
24253
+ let ext = up ? { i: 0, price: bars[0].high } : { i: 0, price: bars[0].low };
24254
+ for (let i = 1; i < bars.length; i++) {
24255
+ const b = bars[i];
24256
+ if (up) {
24257
+ if (b.high >= ext.price) ext = { i, price: b.high };
24258
+ else if (b.low <= ext.price * (1 - dev) && i - (pivots[pivots.length - 1]?.i ?? -depth) >= depth) {
24259
+ pivots.push(ext);
24260
+ up = false;
24261
+ ext = { i, price: b.low };
24262
+ }
24263
+ } else if (b.low <= ext.price) {
24264
+ ext = { i, price: b.low };
24265
+ } else if (b.high >= ext.price * (1 + dev) && i - (pivots[pivots.length - 1]?.i ?? -depth) >= depth) {
24266
+ pivots.push(ext);
24267
+ up = true;
24268
+ ext = { i, price: b.high };
24269
+ }
24270
+ }
24271
+ pivots.push(ext);
24272
+ }
24273
+ return {
24274
+ plots: [],
24275
+ polylines: [
24276
+ {
24277
+ key: "zigzag",
24278
+ points: pivots.map((p) => ({ xloc: "bar_time", x: bars[p.i].time, price: p.price })),
24279
+ curved: false,
24280
+ closed: false,
24281
+ lineColor: str3(inputs, "color", SERIES_LINE),
24282
+ lineWidth: 2,
24283
+ lineStyle: "solid",
24284
+ arrowLeft: false,
24285
+ arrowRight: false
24286
+ }
24287
+ ]
24288
+ };
24289
+ }
24290
+ };
24291
+ var williamsFractal = {
24292
+ type: "williams-fractal",
24293
+ title: "Williams Fractal",
24294
+ shortTitle: "Fractals",
24295
+ overlay: true,
24296
+ inputs: [{ key: "periods", title: "Periods", type: "int", defval: 2, min: 1, max: 50, step: 1 }],
24297
+ compute: (bars, inputs) => {
24298
+ const p = num3(inputs, "periods", 2);
24299
+ const markers = [];
24300
+ const isExtreme = (i, pick, better) => {
24301
+ const v = pick(bars[i]);
24302
+ for (let k = i - p; k <= i + p; k++) {
24303
+ if (k === i) continue;
24304
+ if (!better(v, pick(bars[k]))) return false;
24305
+ }
24306
+ return true;
24307
+ };
24308
+ for (let i = p; i < bars.length - p; i++) {
24309
+ if (isExtreme(i, (b) => b.high, (a, b) => a >= b)) {
24310
+ markers.push({ time: bars[i].time, position: "aboveBar", shape: "triangleup", color: BULLISH });
24311
+ }
24312
+ if (isExtreme(i, (b) => b.low, (a, b) => a <= b)) {
24313
+ markers.push({ time: bars[i].time, position: "belowBar", shape: "triangledown", color: BEARISH });
24314
+ }
24315
+ }
24316
+ return { plots: [], markers };
24317
+ }
24318
+ };
24319
+ var overlaySpecs = [parabolicSar, vwap, pivotPoints, fiftyTwoWeek, zigzag, williamsFractal];
24320
+
24321
+ // src/core/native-indicators/classics/index.ts
24322
+ var classicSpecs = [
24323
+ ...averageSpecs,
24324
+ ...bandSpecs,
24325
+ ...oscillatorSpecs,
24326
+ ...trendSpecs,
24327
+ ...volatilitySpecs,
24328
+ ...volumeSpecs,
24329
+ ...overlaySpecs
24330
+ ];
24331
+ function registerClassicIndicators() {
24332
+ for (const spec of classicSpecs) registerNativeIndicator(classicDescriptor(spec));
24333
+ }
24334
+
22018
24335
  // src/Vela.ts
22019
24336
  var Vela = class {
22020
24337
  constructor(container, options = {}, deps = {}) {
22021
24338
  registerBuiltinChartTypes();
22022
24339
  registerVolume();
22023
24340
  registerVpvr();
24341
+ registerClassicIndicators();
22024
24342
  const element = resolveElement(container);
22025
24343
  const theme = resolveTheme(options.theme);
22026
24344
  let animZoom = true;
@@ -22095,25 +24413,28 @@ var Vela = class {
22095
24413
  }
22096
24414
  /**
22097
24415
  * Add a built-in NATIVE indicator (core-computed, no scripting engine) by registered `type` —
22098
- * e.g. `'vpvr'`. It becomes a first-class indicator (legend row, settings, hide, remove,
22099
- * events) and is single-instance per type (a second call returns the existing handle). Returns
22100
- * a fail-soft handle for an unregistered type. Native renderer only.
24416
+ * e.g. `'vpvr'` or `'sma'`. It becomes a first-class indicator (legend row, settings, hide,
24417
+ * remove, events). Layer-backed types (`'volume'`, `'vpvr'`) are single-instance — a second
24418
+ * call returns the existing handle; the classic studies allow several instances, so each call
24419
+ * adds one more. Returns a fail-soft handle for an unregistered type. Native renderer only.
22101
24420
  */
22102
24421
  addNativeIndicator(type, options) {
22103
24422
  return this.orchestrator.addNativeIndicator(type, options);
22104
24423
  }
22105
24424
  /**
22106
24425
  * The catalog of built-in native indicators with their live state on this chart — each entry's
22107
- * `type`, `title`, whether it `supported`s the current symbol, and whether it's already `present`
22108
- * (native indicators are single-instance per type, so a second `addNativeIndicator` is a no-op).
22109
- * Lets a host "add indicator" UI list them, gate unsupported ones, and avoid duplicates. Async
22110
- * because support may probe the provider (a type may need data the symbol lacks).
24426
+ * `type`, `title`, whether it `supported`s the current symbol, whether it's already `present`,
24427
+ * and whether it allows several instances (`multiInstance`; otherwise a second
24428
+ * `addNativeIndicator` is a no-op). Lets a host "add indicator" UI list them, gate unsupported
24429
+ * ones, and de-duplicate the single-instance ones. Async because support may probe the
24430
+ * provider (a type may need data the symbol lacks).
22111
24431
  */
22112
24432
  availableNativeIndicators() {
22113
24433
  return this.orchestrator.availableNativeIndicators();
22114
24434
  }
22115
24435
  /**
22116
- * The native-indicator types PRESENT on the chart right now — the synchronous slice of
24436
+ * The native-indicator types PRESENT on the chart right now, one entry per instance (a
24437
+ * multi-instance type repeats) — the synchronous slice of
22117
24438
  * {@link availableNativeIndicators} (only support probing is async; presence never is).
22118
24439
  * Persistence snapshots read this: an unload-time flush must see an add/remove that
22119
24440
  * happened microseconds ago, which an async catalog mirror cannot guarantee.