@luxalgo/vela 0.6.15 → 0.6.17

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 (48) hide show
  1. package/README.md +12 -1
  2. package/dist/{DataProvider-XLdJB86x.d.ts → DataProvider-B-Jz59Rf.d.ts} +1 -1
  3. package/dist/{DataProvider-xH2VBcFT.d.cts → DataProvider-OfR3fXg9.d.cts} +1 -1
  4. package/dist/{chunk-6DPAYEGO.js → chunk-CAFCLMPF.js} +1 -0
  5. package/dist/chunk-FE6V2YAQ.js +1 -0
  6. package/dist/{chunk-32A7KHFM.js → chunk-IFJJPXSV.js} +8 -2
  7. package/dist/{chunk-7C7NWEXH.js → chunk-OSZDSH7F.js} +83 -59
  8. package/dist/{chunk-MWDNPGU3.js → chunk-T5Z5YUCF.js} +23 -16
  9. package/dist/{chunk-MWS4CUJL.js → chunk-XCBJU674.js} +2 -2
  10. package/dist/{chunk-J5QIR2Z3.js → chunk-YCD72KGK.js} +2543 -159
  11. package/dist/{contributions-x7iamlnd.d.cts → contributions-C3b0Sx5P.d.cts} +29 -13
  12. package/dist/{contributions-h5BzEiV2.d.ts → contributions-Swzz10fG.d.ts} +29 -13
  13. package/dist/index.cjs +2746 -355
  14. package/dist/index.d.cts +15 -6
  15. package/dist/index.d.ts +15 -6
  16. package/dist/index.js +5 -5
  17. package/dist/{options-bdSAtp7y.d.ts → options-CX1lSYWA.d.cts} +12 -2
  18. package/dist/{options-bdSAtp7y.d.cts → options-CX1lSYWA.d.ts} +12 -2
  19. package/dist/{plugin-2sJcf5mk.d.cts → plugin-BjnEE1zA.d.cts} +3 -3
  20. package/dist/{plugin-BLaNPh55.d.ts → plugin-mvddudqd.d.ts} +3 -3
  21. package/dist/plugin.cjs +1 -0
  22. package/dist/plugin.d.cts +4 -4
  23. package/dist/plugin.d.ts +4 -4
  24. package/dist/plugin.js +3 -3
  25. package/dist/providers/binance.d.cts +2 -2
  26. package/dist/providers/binance.d.ts +2 -2
  27. package/dist/providers/coinbase.d.cts +2 -2
  28. package/dist/providers/coinbase.d.ts +2 -2
  29. package/dist/providers/hyperliquid.d.cts +2 -2
  30. package/dist/providers/hyperliquid.d.ts +2 -2
  31. package/dist/{statusline-model--tH0VWCy.d.cts → statusline-model-D91cU2fF.d.cts} +3 -3
  32. package/dist/{statusline-model-CLpaO3Dl.d.ts → statusline-model-IURCya0H.d.ts} +3 -3
  33. package/dist/ui.cjs +23 -15
  34. package/dist/ui.d.cts +1 -1
  35. package/dist/ui.d.ts +1 -1
  36. package/dist/ui.js +3 -3
  37. package/dist/vela.global.js +2600 -209
  38. package/dist/vela.global.min.js +52 -55
  39. package/dist/widget.cjs +2663 -241
  40. package/dist/widget.d.cts +6 -6
  41. package/dist/widget.d.ts +6 -6
  42. package/dist/widget.js +7 -7
  43. package/dist/workspace.cjs +2663 -241
  44. package/dist/workspace.d.cts +25 -14
  45. package/dist/workspace.d.ts +25 -14
  46. package/dist/workspace.js +6 -6
  47. package/package.json +1 -1
  48. 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-IFJJPXSV.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-T5Z5YUCF.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-CAFCLMPF.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 = {}) {
@@ -2368,6 +2368,23 @@ function attachChromeTooltip(anchor, opts) {
2368
2368
  clear();
2369
2369
  };
2370
2370
  }
2371
+ function chromeHint(text, opts) {
2372
+ const px = opts.size ?? 16;
2373
+ const el = document.createElement("span");
2374
+ el.className = "vela-hint";
2375
+ el.setAttribute("role", "img");
2376
+ el.setAttribute("aria-label", text);
2377
+ el.innerHTML = iconAt("info", px);
2378
+ el.style.cssText = `flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:${px}px;height:${px}px;border-radius:50%;cursor:help;user-select:none;background:var(--vela-hover);color:var(--vela-fg-muted);transition:background var(--vela-dur-fast) ease,color var(--vela-dur-fast) ease;`;
2379
+ const hover = (on) => {
2380
+ el.style.background = on ? "var(--vela-active)" : "var(--vela-hover)";
2381
+ el.style.color = on ? "var(--vela-fg-bright)" : "var(--vela-fg-muted)";
2382
+ };
2383
+ el.addEventListener("pointerenter", () => hover(true));
2384
+ el.addEventListener("pointerleave", () => hover(false));
2385
+ const dispose = attachChromeTooltip(el, { host: opts.host, theme: opts.theme, text: () => text, wrap: true, delayMs: 250 });
2386
+ return { el, dispose };
2387
+ }
2371
2388
 
2372
2389
  // src/renderers/native/drawings/DrawingToolbar.ts
2373
2390
  var TOOLBAR_WIDTH = 44;
@@ -2475,8 +2492,8 @@ var DrawingToolbar = class {
2475
2492
  }
2476
2493
  /** Per-tool shortcut hints (pre-formatted display strings, e.g. `'Alt+T'`) shown at the
2477
2494
  * right edge of the flyout rows, beside the favorite star. */
2478
- setShortcuts(map) {
2479
- this.shortcuts = new Map(Object.entries(map));
2495
+ setShortcuts(map2) {
2496
+ this.shortcuts = new Map(Object.entries(map2));
2480
2497
  }
2481
2498
  /** Paint one star for the current favorite state (filled + gold, or outline). */
2482
2499
  paintStar(el, type) {
@@ -3001,6 +3018,24 @@ function arrowHitHtml(svg) {
3001
3018
  return `<span class="vela-dtb-hit vela-dtb-hit--arrow">${iconSpan(svg, 11)}</span>`;
3002
3019
  }
3003
3020
 
3021
+ // src/core/options.ts
3022
+ var normalizeSession = (v) => v === "regular" || v === "extended" ? v : void 0;
3023
+ var LIVE_BAR_EASE_DEFAULT_MS = 90;
3024
+ var LIVE_BAR_EASE_MAX_MS = 1e3;
3025
+ function resolveLiveBarEaseMs(value) {
3026
+ if (value === true) return LIVE_BAR_EASE_DEFAULT_MS;
3027
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) return 0;
3028
+ return Math.min(value, LIVE_BAR_EASE_MAX_MS);
3029
+ }
3030
+ function resolveAnimations(animations) {
3031
+ if (typeof animations === "boolean") return { animZoom: animations, animPan: animations, animLiveBar: 0 };
3032
+ return {
3033
+ animZoom: animations?.zoom ?? true,
3034
+ animPan: animations?.pan ?? true,
3035
+ animLiveBar: resolveLiveBarEaseMs(animations?.liveBar)
3036
+ };
3037
+ }
3038
+
3004
3039
  // src/core/model/series.ts
3005
3040
  function isLineLikeSeries(spec) {
3006
3041
  return spec.kind !== "candle" && spec.kind !== "bar" && spec.kind !== "markers";
@@ -3373,14 +3408,11 @@ var IndicatorInputsDialog = class {
3373
3408
  buildTextField(id, current, emit) {
3374
3409
  return buildFieldControl({ kind: "text", id, value: current, onChange: emit }).el;
3375
3410
  }
3376
- /** A hoverable ⓘ affordance carrying an input's `tooltip` (themed chrome tip; wraps). */
3411
+ /** The shared ⓘ hint carrying an input's `tooltip` (see {@link chromeHint}). */
3377
3412
  infoButton(tooltip) {
3378
- const b = document.createElement("span");
3379
- b.textContent = "i";
3380
- this.dialogTips.push(attachChromeTooltip(b, { host: this.host.container, theme: () => this.host.theme(), text: () => tooltip, wrap: true, delayMs: 250 }));
3381
- b.className = "vela-ind-hint";
3382
- b.style.cssText = "flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:50%;font-weight:600;font-size:11px;font-family:inherit;line-height:1;cursor:help;user-select:none;";
3383
- return b;
3413
+ const { el, dispose } = chromeHint(tooltip, { host: this.host.container, theme: () => this.host.theme(), size: 18 });
3414
+ this.dialogTips.push(dispose);
3415
+ return el;
3384
3416
  }
3385
3417
  /** A dropdown over plain string options (value === label) — a thin case of {@link selectPairs}. */
3386
3418
  select(id, options, current, emit) {
@@ -3817,8 +3849,6 @@ function ensureDialogStyles() {
3817
3849
  ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
3818
3850
  .vela-ind-tab{font-weight:600;font-size:13px;line-height:20px;transition:color var(--vela-dur-fast) ease,border-color var(--vela-dur-fast) ease;}
3819
3851
  .vela-ind-tab:not(.vela-ind-tab-active):hover{color:var(--vela-fg-bright);}
3820
- .vela-ind-hint{background:var(--vela-hover);color:var(--vela-fg-muted);transition:background var(--vela-dur-fast) ease,color var(--vela-dur-fast) ease;}
3821
- .vela-ind-hint:hover{background:var(--vela-active);color:var(--vela-fg-bright);}
3822
3852
  .vela-ind-ctl,.vela-ind-close{background-color:transparent;color:inherit;transition:color var(--vela-dur-fast) ease,background-color var(--vela-dur-fast) ease;}
3823
3853
  .vela-ind-ctl svg,.vela-ind-close svg{width:${LEGEND_ICON_PX}px;height:${LEGEND_ICON_PX}px;display:block;flex:none;stroke-width:1;}
3824
3854
  .vela-ind-ctl:hover{background-color:var(--vela-hover-strong);}
@@ -4327,8 +4357,8 @@ var InputsUI = class {
4327
4357
  lg.style.top = `${top}px`;
4328
4358
  }
4329
4359
  /** Set which panes are collapsed and, for each, the master indicator to keep in its strip. */
4330
- setCollapsedPanes(map) {
4331
- this.paneCollapse = map;
4360
+ setCollapsedPanes(map2) {
4361
+ this.paneCollapse = map2;
4332
4362
  }
4333
4363
  /**
4334
4364
  * Keep the fold toggle in step with the indicator count: with 2+ indicators anywhere
@@ -5291,9 +5321,9 @@ function factoryResetConfig(factory) {
5291
5321
  if (inst.enableKey) defaults[inst.enableKey] = false;
5292
5322
  addRows(inst.rows);
5293
5323
  }
5294
- for (const sub of section.subsections ?? []) {
5295
- if (sub.enableKey) defaults[sub.enableKey] = false;
5296
- addRows(sub.rows);
5324
+ for (const sub2 of section.subsections ?? []) {
5325
+ if (sub2.enableKey) defaults[sub2.enableKey] = false;
5326
+ addRows(sub2.rows);
5297
5327
  }
5298
5328
  if (!section.instances) addRows(section.rows);
5299
5329
  bag[t.id] = defaults;
@@ -5362,7 +5392,8 @@ function mergeConfig(base, patch) {
5362
5392
  labelsVisible: isBool(ps.labelsVisible) ? ps.labelsVisible : base.priceScale.labelsVisible,
5363
5393
  currentPriceLine: isBool(ps.currentPriceLine) ? ps.currentPriceLine : base.priceScale.currentPriceLine,
5364
5394
  priceLabel: isBool(ps.priceLabel) ? ps.priceLabel : base.priceScale.priceLabel,
5365
- countdown: isBool(ps.countdown) ? ps.countdown : base.priceScale.countdown
5395
+ countdown: isBool(ps.countdown) ? ps.countdown : base.priceScale.countdown,
5396
+ animateLastPrice: isBool(ps.animateLastPrice) ? ps.animateLastPrice : base.priceScale.animateLastPrice
5366
5397
  },
5367
5398
  panes: {
5368
5399
  separatorColor: isColor(panes.separatorColor) ? panes.separatorColor : base.panes.separatorColor
@@ -6622,11 +6653,11 @@ function mergeBarColors(indicators) {
6622
6653
  }
6623
6654
  }
6624
6655
  if (!any) return EMPTY_BARCOLORS;
6625
- const map = /* @__PURE__ */ new Map();
6656
+ const map2 = /* @__PURE__ */ new Map();
6626
6657
  for (const m of indicators.values()) {
6627
- if (m.barColors) for (const bc of m.barColors) map.set(bc.time, bc.color);
6658
+ if (m.barColors) for (const bc of m.barColors) map2.set(bc.time, bc.color);
6628
6659
  }
6629
- return map;
6660
+ return map2;
6630
6661
  }
6631
6662
 
6632
6663
  // src/renderers/native/core/ViewportState.ts
@@ -6923,9 +6954,9 @@ function isHorizontalWheel(deltaX, deltaY) {
6923
6954
  function wheelPanRightOffset(rightOffset, deltaX, barSpacing) {
6924
6955
  return rightOffset + deltaX / barSpacing;
6925
6956
  }
6926
- function wheelPanDelta(deltaX, deltaY, shift) {
6957
+ function wheelPanDelta(deltaX, deltaY, shift2) {
6927
6958
  if (isHorizontalWheel(deltaX, deltaY)) return deltaX;
6928
- if (shift) return deltaY;
6959
+ if (shift2) return deltaY;
6929
6960
  return null;
6930
6961
  }
6931
6962
  function effectiveSnapMode(momentaryOverride, sticky) {
@@ -8547,11 +8578,11 @@ function mergeBarColors2(indicators) {
8547
8578
  }
8548
8579
  }
8549
8580
  if (!any) return EMPTY_BARCOLORS2;
8550
- const map = /* @__PURE__ */ new Map();
8581
+ const map2 = /* @__PURE__ */ new Map();
8551
8582
  for (const m of indicators.values()) {
8552
- if (m.barColors) for (const bc of m.barColors) map.set(bc.time, bc.color);
8583
+ if (m.barColors) for (const bc of m.barColors) map2.set(bc.time, bc.color);
8553
8584
  }
8554
- return map;
8585
+ return map2;
8555
8586
  }
8556
8587
 
8557
8588
  // src/renderers/shared/drawing-geometry.ts
@@ -10149,6 +10180,8 @@ var BUILTIN_SETTINGS_IDS = [
10149
10180
  "symbol.style.baseline.fill-bottom",
10150
10181
  "symbol.style.baseline.base-level",
10151
10182
  "symbol.style.baseline.width",
10183
+ "symbol.animation",
10184
+ "symbol.animation.price-changes",
10152
10185
  "symbol.timezone",
10153
10186
  "scales",
10154
10187
  "scales.price-scale",
@@ -10190,9 +10223,9 @@ function settingsIdCatalog(hostSections) {
10190
10223
  };
10191
10224
  if (section.rows) addRows(section.rows);
10192
10225
  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);
10226
+ for (const sub2 of section.subsections ?? []) {
10227
+ ids.add(`${scope}.${settingsIdSlug(sub2.title)}`);
10228
+ addRows(sub2.rows);
10196
10229
  }
10197
10230
  }
10198
10231
  for (const hs of hostSections) {
@@ -10291,8 +10324,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
10291
10324
  .vela-sd-mobile .vela-select-trigger,.vela-sd-mobile .vela-num input,.vela-sd-mobile .vela-width-field{height:34px;}
10292
10325
  .vela-sd-mobile .vela-sd-close{width:40px;height:40px;}
10293
10326
  .vela-sd-mobile .vela-sd-btn{height:38px;}
10294
- .vela-sd-mobile .vela-sd-row span,.vela-sd-mobile .vela-sd-bool span,.vela-sd-mobile .vela-field-label{white-space:normal !important;}
10295
- `;
10327
+ .vela-sd-mobile .vela-sd-row span,.vela-sd-mobile .vela-sd-bool span,.vela-sd-mobile .vela-field-label{white-space:normal !important;}`;
10296
10328
  if (!existing) document.head.appendChild(st);
10297
10329
  }
10298
10330
  var SettingsDialog = class {
@@ -10313,6 +10345,8 @@ var SettingsDialog = class {
10313
10345
  this.themeControl = null;
10314
10346
  /** The built tabs, by title — how `showSection` reaches a pane while the dialog is open. */
10315
10347
  this.tabs = [];
10348
+ this.hintTips = [];
10349
+ // ⓘ tooltip disposers of the open build (see hint)
10316
10350
  /** Active instance-strip tab per chart type — remembered across dialog rebuilds. */
10317
10351
  this.typeActiveInstance = /* @__PURE__ */ new Map();
10318
10352
  /** Active TOC group per structured pane (`<typeId>/<pane>` → group label). */
@@ -10501,6 +10535,13 @@ var SettingsDialog = class {
10501
10535
  body.append(g);
10502
10536
  }
10503
10537
  showActive(config.series.style);
10538
+ body.append(sid(this.sectionTitle("Animation"), "symbol.animation"));
10539
+ body.append(sid(this.boolRow(
10540
+ "Animate price changes",
10541
+ config.priceScale.animateLastPrice,
10542
+ (v) => this.emit({ priceScale: { animateLastPrice: v } }),
10543
+ this.hint("Glide the live bar to each new price instead of snapping.")
10544
+ ), "symbol.animation.price-changes"));
10504
10545
  body.append(sid(this.sectionTitle("Time zone"), "symbol.timezone"));
10505
10546
  body.append(sid(this.selectRowLabeled("Time zone", normalizeTimezone(config.timeScale.timezone), timezoneOptions(config.timeScale.timezone), (v) => this.emit({ timeScale: { timezone: v } })), "symbol.timezone"));
10506
10547
  const renderHostSections = (placement) => {
@@ -10708,6 +10749,8 @@ var SettingsDialog = class {
10708
10749
  this.ui = null;
10709
10750
  this.root = null;
10710
10751
  this.tabs = [];
10752
+ for (const dispose of this.hintTips) dispose();
10753
+ this.hintTips = [];
10711
10754
  ui?.destroy();
10712
10755
  }
10713
10756
  destroy() {
@@ -10751,14 +10794,14 @@ var SettingsDialog = class {
10751
10794
  };
10752
10795
  if (section.rows) seed(section.rows);
10753
10796
  for (const inst of section.instances ?? []) seed(inst.rows);
10754
- for (const sub of section.subsections ?? []) seed(sub.rows);
10797
+ for (const sub2 of section.subsections ?? []) seed(sub2.rows);
10755
10798
  for (const inst of section.instances ?? []) {
10756
10799
  if (!inst.enableKey || inst.enableKey in bag) continue;
10757
10800
  bag[inst.enableKey] = values[inst.enableKey] === true;
10758
10801
  }
10759
- for (const sub of section.subsections ?? []) {
10760
- if (!sub.enableKey || sub.enableKey in bag) continue;
10761
- bag[sub.enableKey] = values[sub.enableKey] === true;
10802
+ for (const sub2 of section.subsections ?? []) {
10803
+ if (!sub2.enableKey || sub2.enableKey in bag) continue;
10804
+ bag[sub2.enableKey] = values[sub2.enableKey] === true;
10762
10805
  }
10763
10806
  const refreshers = [];
10764
10807
  const put = (key, v) => {
@@ -10775,16 +10818,16 @@ var SettingsDialog = class {
10775
10818
  if (section.layout === "grouped") body.append(this.groupedRows(`${typeId}/rows`, rows, bag, put, refreshers));
10776
10819
  else this.flatTypeRows(rows, bag, put, refreshers, body);
10777
10820
  }
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);
10821
+ for (const sub2 of section.subsections ?? []) {
10822
+ if (settingsIdHidden(`${scope}.${settingsIdSlug(sub2.title)}`, this.hiddenSettings)) continue;
10823
+ const rows = filterHiddenRows(sub2.rows, scope, this.hiddenSettings);
10781
10824
  if (rows.length === 0) continue;
10782
- const subMarker = this.section(sub.title);
10825
+ const subMarker = this.section(sub2.title);
10783
10826
  subMarker.dataset.sdStyle = typeId;
10784
10827
  subMarker.dataset.sdVisibility = section.visibility ?? "active";
10785
10828
  subMarker.dataset.sdSub = "1";
10786
10829
  body.append(subMarker);
10787
- body.append(this.groupedRows(`${typeId}/${sub.title}`, rows, bag, put, refreshers, sub.enableKey));
10830
+ body.append(this.groupedRows(`${typeId}/${sub2.title}`, rows, bag, put, refreshers, sub2.enableKey));
10788
10831
  }
10789
10832
  for (const r of refreshers) r();
10790
10833
  }
@@ -11121,23 +11164,31 @@ var SettingsDialog = class {
11121
11164
  }).el
11122
11165
  });
11123
11166
  }
11124
- /** A toggle row: the checkbox sits to the LEFT of its label (never in the control area). */
11125
- boolRow(label, value, onChange) {
11126
- return this.toggleRow(label, value, onChange, []);
11167
+ /** A toggle row: the checkbox sits to the LEFT of its label (never in the control area).
11168
+ * `info` (see {@link hint}) rides after the label. */
11169
+ boolRow(label, value, onChange, info) {
11170
+ return this.toggleRow(label, value, onChange, [], void 0, info);
11127
11171
  }
11128
11172
  /** An enable row: checkbox + label in the label column, dependent controls in the
11129
11173
  * shared control column; the control group dims and ignores input while the toggle
11130
11174
  * is off. With `get`, the row re-reads its state on a 'vela-sync' event. */
11131
- toggleRow(label, value, onToggle, controls, get) {
11175
+ toggleRow(label, value, onToggle, controls, get, info) {
11132
11176
  return fieldRow({
11133
11177
  label,
11134
11178
  labelSize: "sm",
11135
11179
  bool: controls.length === 0,
11136
11180
  toggle: { checked: value, onChange: onToggle, get },
11137
11181
  control: controls,
11182
+ info,
11138
11183
  className: controls.length === 0 ? "vela-sd-bool" : "vela-sd-row"
11139
11184
  });
11140
11185
  }
11186
+ /** The shared ⓘ hint after a row label (see {@link chromeHint}). Disposed with the dialog. */
11187
+ hint(text) {
11188
+ const { el, dispose } = chromeHint(text, { host: this.container, theme: () => this.theme });
11189
+ this.hintTips.push(dispose);
11190
+ return el;
11191
+ }
11141
11192
  numberRow(label, value, min, max, step, onChange) {
11142
11193
  return fieldRow({
11143
11194
  label,
@@ -12891,11 +12942,11 @@ var DrawingInteraction = class {
12891
12942
  /** A press: place the next anchor, or grab a drawing/handle (resolved as click or drag on release).
12892
12943
  * `shift` over a drawing toggles it in/out of the selection (multi-select) instead of dragging;
12893
12944
  * while placing a line tool it locks the segment angle to 45° steps. */
12894
- down(x, y, mode = "off", shift = false) {
12945
+ down(x, y, mode = "off", shift2 = false) {
12895
12946
  const proj = this.deps.projector();
12896
12947
  if (this.state.kind === "placing") {
12897
12948
  if (this.state.draft.placementMode() !== "click") return;
12898
- const pivot = shift ? this.placingPivot(this.state.draft) : null;
12949
+ const pivot = shift2 ? this.placingPivot(this.state.draft) : null;
12899
12950
  const point = pivot ? this.resolveAngleSnapped(pivot, this.state.draft.paneId, x, y) : this.resolve(x, y, this.state.draft.paneId, mode);
12900
12951
  this.state.draft.anchors.push(this.placementAnchor(this.state.draft, point));
12901
12952
  if (this.state.draft.anchors.length >= this.state.need) this.finalize();
@@ -12914,7 +12965,7 @@ var DrawingInteraction = class {
12914
12965
  }
12915
12966
  const hit = this.hitAt(x, y);
12916
12967
  if (hit) {
12917
- if (shift) {
12968
+ if (shift2) {
12918
12969
  this.deps.emit({ kind: "select", ids: [hit.id], additive: true });
12919
12970
  return;
12920
12971
  }
@@ -12924,7 +12975,7 @@ var DrawingInteraction = class {
12924
12975
  }
12925
12976
  /** Pointer move: advance the placing ghost, or live-preview a drag once past the slop.
12926
12977
  * `shift` on a line tool locks the segment angle to 45° steps (placing + handle drags). */
12927
- move(x, y, mode = "off", shift = false) {
12978
+ move(x, y, mode = "off", shift2 = false) {
12928
12979
  if (this.state.kind === "idle") {
12929
12980
  const tool = this.deps.activeTool();
12930
12981
  const had = this.snapAt != null;
@@ -12938,7 +12989,7 @@ var DrawingInteraction = class {
12938
12989
  this.captureFreehand(x, y);
12939
12990
  return;
12940
12991
  }
12941
- const pivot = shift ? this.placingPivot(this.state.draft) : null;
12992
+ const pivot = shift2 ? this.placingPivot(this.state.draft) : null;
12942
12993
  this.state.cursor = pivot ? this.resolveAngleSnapped(pivot, this.state.draft.paneId, x, y) : this.resolve(x, y, this.state.draft.paneId, mode);
12943
12994
  this.deps.changed();
12944
12995
  return;
@@ -12951,7 +13002,7 @@ var DrawingInteraction = class {
12951
13002
  if (this.state.handle >= 0) {
12952
13003
  const a = d.anchors[this.state.handle];
12953
13004
  if (a) {
12954
- const pivot = shift && ANGLE_SNAP_TYPES.has(d.type) && d.anchors.length === 2 ? d.anchors[1 - this.state.handle] : void 0;
13005
+ const pivot = shift2 && ANGLE_SNAP_TYPES.has(d.type) && d.anchors.length === 2 ? d.anchors[1 - this.state.handle] : void 0;
12955
13006
  const point = pivot ? this.resolveAngleSnapped(pivot, d.paneId, x, y) : this.resolve(x, y, d.paneId, mode);
12956
13007
  applyFree(a, point, d.anchorSchema().slots[this.state.handle]?.free ?? "both");
12957
13008
  }
@@ -13649,21 +13700,21 @@ var DrawingSettingsPopup = class {
13649
13700
  bar.appendChild(this.colorButton("Lower fill", BUCKET_ICON, reg.lowerFill, (v) => actions.patch({ "reg.lowerFill": v })));
13650
13701
  bar.appendChild(this.toggle("Show R\xB2", R2_ICON, reg.showR2, (v) => actions.patch({ "reg.showR2": v })));
13651
13702
  }
13652
- const vwap = drawing.vwap;
13653
- if (paths.has("vwap.midColor") && vwap) {
13703
+ const vwap2 = drawing.vwap;
13704
+ if (paths.has("vwap.midColor") && vwap2) {
13654
13705
  const styles = LINE_STYLE_OPTIONS.map((o) => o.value);
13655
13706
  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 }));
13707
+ bar.appendChild(this.colorButton("VWAP color", BRUSH_ICON, vwap2.midColor, (v) => actions.patch({ "vwap.midColor": v })));
13708
+ bar.appendChild(this.dropdown("VWAP style", styles, vwap2.midStyle, (s) => lineIcon(2, s), (v) => actions.patch({ "vwap.midStyle": v }), { label: styleLabel2 }));
13658
13709
  bar.appendChild(
13659
- this.dropdown("Band multiplier", MULTS, vwap.multiplier, () => BANDS_ICON, (v) => actions.patch({ "vwap.multiplier": v }), {
13710
+ this.dropdown("Band multiplier", MULTS, vwap2.multiplier, () => BANDS_ICON, (v) => actions.patch({ "vwap.multiplier": v }), {
13660
13711
  label: (v) => `${v}\u03C3`,
13661
13712
  labelInTrigger: true
13662
13713
  })
13663
13714
  );
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 })));
13715
+ bar.appendChild(this.colorButton("Upper band color", BRUSH_ICON, vwap2.upperColor, (v) => actions.patch({ "vwap.upperColor": v })));
13716
+ bar.appendChild(this.colorButton("Lower band color", BRUSH_ICON, vwap2.lowerColor, (v) => actions.patch({ "vwap.lowerColor": v })));
13717
+ bar.appendChild(this.colorButton("Band fill", BUCKET_ICON, vwap2.bandFill, (v) => actions.patch({ "vwap.bandFill": v })));
13667
13718
  }
13668
13719
  if (paths.has("maxCurvature")) {
13669
13720
  const cur = drawing.maxCurvature ?? 24;
@@ -14680,8 +14731,8 @@ var UserDrawingController = class {
14680
14731
  this.toolbar.setFavorites(types);
14681
14732
  }
14682
14733
  /** Core push: per-tool shortcut hints (display strings) shown in the toolbar flyouts. */
14683
- setToolShortcuts(map) {
14684
- this.toolbar.setShortcuts(map);
14734
+ setToolShortcuts(map2) {
14735
+ this.toolbar.setShortcuts(map2);
14685
14736
  }
14686
14737
  /** Core push: set the sticky magnet mode. Applies to the renderer + reflects on the
14687
14738
  * in-chart toolbar WITHOUT notifying back (the caller already holds the value). */
@@ -14799,7 +14850,7 @@ var UserDrawingController = class {
14799
14850
  this.render();
14800
14851
  }
14801
14852
  }
14802
- pointerDown(x, y, snap = "off", shift = false) {
14853
+ pointerDown(x, y, snap = "off", shift2 = false) {
14803
14854
  if (this.eraserMode) {
14804
14855
  this.erasing = true;
14805
14856
  this.deleteAt(x, y);
@@ -14819,9 +14870,9 @@ var UserDrawingController = class {
14819
14870
  return;
14820
14871
  }
14821
14872
  }
14822
- this.interaction.down(x, y, snap, shift);
14873
+ this.interaction.down(x, y, snap, shift2);
14823
14874
  }
14824
- pointerMove(x, y, snap = "off", shift = false) {
14875
+ pointerMove(x, y, snap = "off", shift2 = false) {
14825
14876
  if (this.eraserMode) {
14826
14877
  if (this.erasing) this.deleteAt(x, y);
14827
14878
  return;
@@ -14832,7 +14883,7 @@ var UserDrawingController = class {
14832
14883
  this.render();
14833
14884
  return;
14834
14885
  }
14835
- this.interaction.move(x, y, snap, shift);
14886
+ this.interaction.move(x, y, snap, shift2);
14836
14887
  this.updateHover(x, y);
14837
14888
  }
14838
14889
  /** Track which drawing is hovered so its handles appear on hover (and only then). */
@@ -15509,14 +15560,14 @@ function layoutTable(ctx, t, args) {
15509
15560
  }
15510
15561
  for (const { box, w: w2, h: h2 } of spanning) {
15511
15562
  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;
15563
+ let sum2 = 0;
15564
+ for (let c = box.c; c < box.c + box.cs; c += 1) sum2 += colW[c];
15565
+ if (w2 > sum2) for (let c = box.c; c < box.c + box.cs; c += 1) colW[c] += (w2 - sum2) / box.cs;
15515
15566
  }
15516
15567
  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;
15568
+ let sum2 = 0;
15569
+ for (let r = box.r; r < box.r + box.rs; r += 1) sum2 += rowH[r];
15570
+ if (h2 > sum2) for (let r = box.r; r < box.r + box.rs; r += 1) rowH[r] += (h2 - sum2) / box.rs;
15520
15571
  }
15521
15572
  }
15522
15573
  let w = 0;
@@ -16213,24 +16264,24 @@ function rasterizeOverlay(ctx, root, frame) {
16213
16264
  drawSvgShapes(el, alpha);
16214
16265
  continue;
16215
16266
  }
16216
- const num3 = (name) => parseFloat(el.getAttribute(name) ?? "0") || 0;
16267
+ const num4 = (name) => parseFloat(el.getAttribute(name) ?? "0") || 0;
16217
16268
  let path = null;
16218
16269
  if (tag === "path") {
16219
16270
  const d = el.getAttribute("d");
16220
16271
  if (d) path = new Path2D(d);
16221
16272
  } else if (tag === "circle") {
16222
16273
  path = new Path2D();
16223
- path.arc(num3("cx"), num3("cy"), num3("r"), 0, Math.PI * 2);
16274
+ path.arc(num4("cx"), num4("cy"), num4("r"), 0, Math.PI * 2);
16224
16275
  } else if (tag === "ellipse") {
16225
16276
  path = new Path2D();
16226
- path.ellipse(num3("cx"), num3("cy"), num3("rx"), num3("ry"), 0, 0, Math.PI * 2);
16277
+ path.ellipse(num4("cx"), num4("cy"), num4("rx"), num4("ry"), 0, 0, Math.PI * 2);
16227
16278
  } else if (tag === "rect") {
16228
16279
  path = new Path2D();
16229
- path.rect(num3("x"), num3("y"), num3("width"), num3("height"));
16280
+ path.rect(num4("x"), num4("y"), num4("width"), num4("height"));
16230
16281
  } else if (tag === "line") {
16231
16282
  path = new Path2D();
16232
- path.moveTo(num3("x1"), num3("y1"));
16233
- path.lineTo(num3("x2"), num3("y2"));
16283
+ path.moveTo(num4("x1"), num4("y1"));
16284
+ path.lineTo(num4("x2"), num4("y2"));
16234
16285
  } else if (tag === "polyline" || tag === "polygon") {
16235
16286
  const pts = (el.getAttribute("points") ?? "").trim().split(/[\s,]+/).map(Number);
16236
16287
  if (pts.length < 4 || pts.some(Number.isNaN)) continue;
@@ -16491,7 +16542,6 @@ var ZOOM_OUT_MARGIN_BARS = 6;
16491
16542
  var ZOOM_TAU_MS = 70;
16492
16543
  var SCALE_TAU_MS = 80;
16493
16544
  var FLING_TAU_MS = 110;
16494
- var LIVE_BAR_TAU_MS = 90;
16495
16545
  var SCROLL_TO_TAU_MS = 130;
16496
16546
  var FLING_STOP_PX = 0.02;
16497
16547
  var PRICE_SCALE_K = 4e-3;
@@ -16562,9 +16612,13 @@ var NativeRenderer = class {
16562
16612
  /** Drawings layer self-serves Ctrl+Z/Y (see the `historyChords` feature). */
16563
16613
  this.historyChordsEnabled = true;
16564
16614
  this.liveRegion = null;
16565
- // ── animation state (eased zoom + inertial pan) ──
16615
+ // ── animation state (eased zoom + inertial pan + live-bar glide) ──
16566
16616
  this.animZoom = true;
16567
16617
  this.animPan = true;
16618
+ this.animLiveBarMs = 0;
16619
+ // forming-bar OHLC glide time-constant; 0 = each tick snaps
16620
+ this.animLiveBarOnMs = LIVE_BAR_EASE_DEFAULT_MS;
16621
+ // the duration the settings-dialog on/off toggle restores (last non-zero value configured)
16568
16622
  // Brand default candles.
16569
16623
  this.candleUp = BULLISH;
16570
16624
  this.candleDown = BEARISH;
@@ -16689,7 +16743,7 @@ var NativeRenderer = class {
16689
16743
  this.moveIndicatorCbs = /* @__PURE__ */ new Set();
16690
16744
  this.priceStyleCbs = /* @__PURE__ */ new Set();
16691
16745
  this.name = "native";
16692
- this.features = ["logScale", "currentPriceLine", "priceLabel", "countdown", "upColor", "downColor", "glow", "animZoom", "animPan", "intro", "zoomAnchor", "axisDrag", "paneResize", "candleZOrder", "candleVisible", "seriesOrder", "highlights", "sessionZones", "gridlines", "axisLabels", "scaleMode", "invertScale", "paneScales", "autoScale", "timezone", "keyboard", "historyChords", "priceStyle", "priceBaseline", "baselinePrice", "settings", "attribution", "dialogHost", "tradeMarkers", "indicatorTitles", "indicatorValues"];
16746
+ this.features = ["logScale", "currentPriceLine", "priceLabel", "countdown", "upColor", "downColor", "glow", "animZoom", "animPan", "animLiveBar", "intro", "zoomAnchor", "axisDrag", "paneResize", "candleZOrder", "candleVisible", "seriesOrder", "highlights", "sessionZones", "gridlines", "axisLabels", "scaleMode", "invertScale", "paneScales", "autoScale", "timezone", "keyboard", "historyChords", "priceStyle", "priceBaseline", "baselinePrice", "settings", "attribution", "dialogHost", "tradeMarkers", "indicatorTitles", "indicatorValues"];
16693
16747
  /** Track cursor proximity to the scroll button on the plot (bubbles from the button too,
16694
16748
  * so moving onto the button doesn't count as leaving). */
16695
16749
  this.onScrollProximityMove = (e) => {
@@ -16723,6 +16777,7 @@ var NativeRenderer = class {
16723
16777
  this.backendMode = opts.nativeBackend;
16724
16778
  this.animZoom = opts.animZoom;
16725
16779
  this.animPan = opts.animPan;
16780
+ this.setLiveBarEase(resolveLiveBarEaseMs(opts.animLiveBar));
16726
16781
  this.glowAmount = opts.glow;
16727
16782
  this.candleUp = opts.upColor;
16728
16783
  this.candleDown = opts.downColor;
@@ -16772,6 +16827,10 @@ var NativeRenderer = class {
16772
16827
  case "animPan":
16773
16828
  this.animPan = Boolean(value);
16774
16829
  return;
16830
+ case "animLiveBar":
16831
+ this.setLiveBarEase(resolveLiveBarEaseMs(value));
16832
+ return;
16833
+ // affects the next tick only; a glide in flight finishes at the new rate (or snaps at 0)
16775
16834
  case "intro": {
16776
16835
  const s = value === false || value === "none" || value === "off" || value == null ? "" : String(value);
16777
16836
  this.introStyle = s;
@@ -16908,6 +16967,8 @@ var NativeRenderer = class {
16908
16967
  return this.animZoom;
16909
16968
  case "animPan":
16910
16969
  return this.animPan;
16970
+ case "animLiveBar":
16971
+ return this.animLiveBarMs;
16911
16972
  case "intro":
16912
16973
  return this.introStyle;
16913
16974
  case "zoomAnchor":
@@ -17076,7 +17137,8 @@ var NativeRenderer = class {
17076
17137
  labelsVisible: this.scene.showAxisLabels,
17077
17138
  currentPriceLine: this.scene.showPriceLine,
17078
17139
  priceLabel: this.scene.showPriceLabel,
17079
- countdown: this.scene.showCountdown
17140
+ countdown: this.scene.showCountdown,
17141
+ animateLastPrice: this.animLiveBarMs > 0
17080
17142
  },
17081
17143
  panes: { separatorColor: s.separatorColor ?? t.borderColor },
17082
17144
  trades: {
@@ -17183,6 +17245,7 @@ var NativeRenderer = class {
17183
17245
  this.scene.showPriceLabel = next.priceScale.priceLabel;
17184
17246
  this.scene.showCountdown = next.priceScale.countdown;
17185
17247
  this.syncCountdownTimer();
17248
+ this.animLiveBarMs = next.priceScale.animateLastPrice ? this.animLiveBarOnMs : 0;
17186
17249
  s.separatorColor = keepInherit(s.separatorColor, next.panes.separatorColor, prevTheme.borderColor);
17187
17250
  this.scene.tradeMarkers = {
17188
17251
  visible: next.trades.visible,
@@ -17647,8 +17710,8 @@ var NativeRenderer = class {
17647
17710
  drawingsDeleteAt: (x, y) => this.userDrawings?.deleteAt(x, y) ?? false,
17648
17711
  drawingsCancelPlacement: () => this.userDrawings?.cancelPlacement() ?? false,
17649
17712
  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),
17713
+ drawingsPointerDown: (x, y, snap, shift2) => this.userDrawings?.pointerDown(x, y, snap, shift2),
17714
+ drawingsPointerMove: (x, y, snap, shift2) => this.userDrawings?.pointerMove(x, y, snap, shift2),
17652
17715
  drawingsPointerUp: (x, y, snap) => this.userDrawings?.pointerUp(x, y, snap),
17653
17716
  drawingsCursor: (x, y) => this.userDrawings?.cursorAt(x, y) ?? null,
17654
17717
  drawingsDblClick: (x, y) => this.userDrawings?.dblClick(x, y) ?? false,
@@ -17965,18 +18028,18 @@ var NativeRenderer = class {
17965
18028
  for (const model of this.scene.indicators.values()) this.refreshAnchorOffset(model);
17966
18029
  if (opts?.preserveView === true && prevHeadTime != null && this.bars.length > 0) {
17967
18030
  const newHeadTime = this.bars[0].time;
17968
- let shift = 0;
18031
+ let shift2 = 0;
17969
18032
  if (newHeadTime !== prevHeadTime) {
17970
18033
  const prepended = this.bars.findIndex((b) => b.time === prevHeadTime);
17971
- if (prepended > 0) shift = prepended;
18034
+ if (prepended > 0) shift2 = prepended;
17972
18035
  else {
17973
18036
  const trimmed = prevBars.findIndex((b) => b.time === newHeadTime);
17974
- if (trimmed > 0) shift = -trimmed;
18037
+ if (trimmed > 0) shift2 = -trimmed;
17975
18038
  }
17976
18039
  }
17977
- if (shift !== 0) {
17978
- this.zoomAnchorLogical += shift;
17979
- if (this.hoverLogical != null) this.hoverLogical += shift;
18040
+ if (shift2 !== 0) {
18041
+ this.zoomAnchorLogical += shift2;
18042
+ if (this.hoverLogical != null) this.hoverLogical += shift2;
17980
18043
  }
17981
18044
  }
17982
18045
  const skipFit = opts?.preserveView === true && this.didInitialFit;
@@ -17999,8 +18062,11 @@ var NativeRenderer = class {
17999
18062
  const last = this.bars[n - 1];
18000
18063
  if (last && bar.time === last.time) {
18001
18064
  this.bars[n - 1] = bar;
18002
- if (this.liveEaseTime !== bar.time) this.syncLiveEase(bar);
18003
- this.animator.start();
18065
+ if (this.animLiveBarMs <= 0 || this.liveEaseTime !== bar.time) {
18066
+ this.syncLiveEase(bar);
18067
+ } else {
18068
+ this.animator.start();
18069
+ }
18004
18070
  } else if (!last || bar.time > last.time) {
18005
18071
  this.bars.push(bar);
18006
18072
  this.syncLiveEase(bar);
@@ -18012,6 +18078,12 @@ var NativeRenderer = class {
18012
18078
  this.scene.bars = this.bars;
18013
18079
  this.scheduler.invalidate(4 /* Full */);
18014
18080
  }
18081
+ /** Set the live-bar glide duration (0 = off). A non-zero value is also remembered as
18082
+ * what the config's on/off toggle (`priceScale.animateLastPrice`) switches back on to. */
18083
+ setLiveBarEase(ms) {
18084
+ this.animLiveBarMs = ms;
18085
+ if (ms > 0) this.animLiveBarOnMs = ms;
18086
+ }
18015
18087
  /** Snap the eased forming-bar state to `bar` — no glide (a fresh bar or the first tick of one). */
18016
18088
  syncLiveEase(bar) {
18017
18089
  this.liveEaseTime = bar.time;
@@ -18024,9 +18096,10 @@ var NativeRenderer = class {
18024
18096
  const target = this.bars[this.bars.length - 1];
18025
18097
  if (!target || this.liveEaseTime !== target.time) return false;
18026
18098
  const eps = Math.max(1e-9, Math.abs(target.close) * 1e-6);
18027
- const nh = easeToward(this.liveEaseHigh, target.high, dtMs, LIVE_BAR_TAU_MS);
18028
- const nl = easeToward(this.liveEaseLow, target.low, dtMs, LIVE_BAR_TAU_MS);
18029
- const nc = easeToward(this.liveEaseClose, target.close, dtMs, LIVE_BAR_TAU_MS);
18099
+ const tau = this.animLiveBarMs;
18100
+ const nh = easeToward(this.liveEaseHigh, target.high, dtMs, tau);
18101
+ const nl = easeToward(this.liveEaseLow, target.low, dtMs, tau);
18102
+ const nc = easeToward(this.liveEaseClose, target.close, dtMs, tau);
18030
18103
  const active = Math.abs(nh - target.high) > eps || Math.abs(nl - target.low) > eps || Math.abs(nc - target.close) > eps;
18031
18104
  this.liveEaseHigh = active ? nh : target.high;
18032
18105
  this.liveEaseLow = active ? nl : target.low;
@@ -19582,15 +19655,15 @@ var NativeRenderer = class {
19582
19655
  /** For each collapsed pane, the master (master-scale) indicator id whose legend row stays in the
19583
19656
  * strip — merged own-scale indicators are hidden while collapsed. */
19584
19657
  collapsedMasterMap() {
19585
- const map = /* @__PURE__ */ new Map();
19658
+ const map2 = /* @__PURE__ */ new Map();
19586
19659
  for (const pane of this.scene.panes.values()) {
19587
19660
  if (!pane.collapsed) continue;
19588
19661
  const models = this.scene.orderedIndicatorsForPane(pane.id);
19589
19662
  const merged = new Set(this.scene.ownScaleIndicatorsForPane(pane.id).map((m) => m.id));
19590
19663
  const master = models.find((m) => !merged.has(m.id)) ?? models[0];
19591
- map.set(pane.id, master?.id ?? null);
19664
+ map2.set(pane.id, master?.id ?? null);
19592
19665
  }
19593
- return map;
19666
+ return map2;
19594
19667
  }
19595
19668
  /** Reserve `px` of left gutter for the docked drawings toolbar (0 releases it) + re-lay-out the plot. */
19596
19669
  setToolbarGutter(px) {
@@ -19862,7 +19935,7 @@ var LiveSession = class {
19862
19935
 
19863
19936
  // src/core/engine/IndicatorHandleImpl.ts
19864
19937
  var IndicatorHandleImpl = class {
19865
- constructor(id, title, controller, source) {
19938
+ constructor(id, title, controller, source, nativeType) {
19866
19939
  this.controller = controller;
19867
19940
  this.schema = [];
19868
19941
  this.propsSchema = [];
@@ -19871,6 +19944,7 @@ var IndicatorHandleImpl = class {
19871
19944
  this.id = id;
19872
19945
  this.title = title;
19873
19946
  if (source !== void 0) this.source = source;
19947
+ if (nativeType !== void 0) this.nativeType = nativeType;
19874
19948
  }
19875
19949
  get inputs() {
19876
19950
  return this.schema;
@@ -19961,24 +20035,24 @@ function summarizeModel(model) {
19961
20035
  }
19962
20036
  function inspectModels(models) {
19963
20037
  const indicators = models.map(summarizeModel);
19964
- const sum = (pick) => indicators.reduce((n, s) => n + pick(s), 0);
20038
+ const sum2 = (pick) => indicators.reduce((n, s) => n + pick(s), 0);
19965
20039
  return {
19966
20040
  indicators,
19967
20041
  totals: {
19968
20042
  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)
20043
+ series: sum2((s) => Object.values(s.series).reduce((a, b) => a + b, 0)),
20044
+ fills: sum2((s) => s.fills),
20045
+ backgrounds: sum2((s) => s.backgrounds),
20046
+ priceLines: sum2((s) => s.priceLines),
20047
+ lines: sum2((s) => s.lines),
20048
+ boxes: sum2((s) => s.boxes),
20049
+ labels: sum2((s) => s.labels),
20050
+ polylines: sum2((s) => s.polylines),
20051
+ linefills: sum2((s) => s.linefills),
20052
+ tables: sum2((s) => s.tables),
20053
+ barColors: sum2((s) => s.barColors),
20054
+ trades: sum2((s) => s.trades),
20055
+ forcedOverlay: sum2((s) => s.forcedOverlay)
19982
20056
  }
19983
20057
  };
19984
20058
  }
@@ -21035,16 +21109,20 @@ var EngineOrchestrator = class _EngineOrchestrator {
21035
21109
  /**
21036
21110
  * Add a NATIVE (core-computed, no Pine engine) indicator by registered type — a first-class
21037
21111
  * 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'`.
21112
+ * INSTANCE per type unless the descriptor opts into `multiInstance`: a second add of a
21113
+ * single-instance type returns the existing handle; a multi-instance type gets a fresh
21114
+ * instance every time. Unknown type ⇒ a fail-soft handle that never mounts (mirrors
21115
+ * addIndicator). Built-in single-instance types: `'volume'`, `'vpvr'`.
21040
21116
  */
21041
21117
  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
21118
  const descriptor = getNativeIndicator(type);
21119
+ if (!descriptor?.multiInstance) {
21120
+ const existing = this.registry.all().find((r) => r.native?.type === type);
21121
+ if (existing) return this.handles.get(existing.id) ?? new IndicatorHandleImpl(existing.id, existing.title, this, void 0, type);
21122
+ }
21045
21123
  const id = this.registry.nextId("native");
21046
21124
  const title = descriptor?.title ?? type;
21047
- const handle = new IndicatorHandleImpl(id, title, this);
21125
+ const handle = new IndicatorHandleImpl(id, title, this, void 0, type);
21048
21126
  this.handles.set(id, handle);
21049
21127
  if (!descriptor) {
21050
21128
  console.warn(`[vela] addNativeIndicator("${type}") \u2014 no native indicator registered for this type.`);
@@ -21056,16 +21134,18 @@ var EngineOrchestrator = class _EngineOrchestrator {
21056
21134
  void this.startNativeIndicator(id, handle);
21057
21135
  return handle;
21058
21136
  }
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). */
21137
+ /** The native types present on the chart, one entry per INSTANCE in registry order (a
21138
+ * multi-instance type repeats) — SYNC (registry state; no support probe). */
21066
21139
  presentNativeIndicators() {
21067
21140
  return this.registry.all().map((r) => r.native?.type).filter((t) => !!t);
21068
21141
  }
21142
+ /**
21143
+ * The catalog of registered native-indicator types with their live state on THIS chart: each
21144
+ * entry's `supported` (applies to the current symbol) and `present` (at least one instance
21145
+ * added — a second add of a single-instance type is a no-op). Powers a host "add native
21146
+ * indicator" menu that can gate + de-duplicate. Async because a descriptor's `isSupported` may
21147
+ * probe the provider for a required capability.
21148
+ */
21069
21149
  async availableNativeIndicators() {
21070
21150
  const symbol = this.qualifiedSymbol();
21071
21151
  const present = new Set(this.presentNativeIndicators());
@@ -21075,7 +21155,8 @@ var EngineOrchestrator = class _EngineOrchestrator {
21075
21155
  title: d.title,
21076
21156
  supported: await this.isNativeSupported(d, symbol),
21077
21157
  present: present.has(d.type),
21078
- beta: d.beta
21158
+ beta: d.beta,
21159
+ ...d.multiInstance ? { multiInstance: true } : {}
21079
21160
  }))
21080
21161
  );
21081
21162
  }
@@ -22015,29 +22096,2329 @@ function registerVpvr() {
22015
22096
  registerNativeIndicator(vpvrDescriptor);
22016
22097
  }
22017
22098
 
22018
- // src/Vela.ts
22019
- var Vela = class {
22020
- constructor(container, options = {}, deps = {}) {
22021
- registerBuiltinChartTypes();
22022
- registerVolume();
22023
- registerVpvr();
22024
- const element = resolveElement(container);
22025
- const theme = resolveTheme(options.theme);
22026
- let animZoom = true;
22027
- let animPan = true;
22028
- if (typeof options.animations === "boolean") {
22029
- animZoom = options.animations;
22030
- animPan = options.animations;
22031
- } else if (options.animations) {
22032
- animZoom = options.animations.zoom ?? true;
22033
- animPan = options.animations.pan ?? true;
22099
+ // src/core/native-indicators/classics/define.ts
22100
+ function num3(inputs, key, fallback) {
22101
+ const v = inputs[key];
22102
+ return typeof v === "number" && Number.isFinite(v) ? v : fallback;
22103
+ }
22104
+ function str3(inputs, key, fallback) {
22105
+ const v = inputs[key];
22106
+ return typeof v === "string" && v !== "" ? v : fallback;
22107
+ }
22108
+ function toPoints(bars, values, colors) {
22109
+ const pts = new Array(bars.length);
22110
+ for (let i = 0; i < bars.length; i++) {
22111
+ const v = values[i];
22112
+ const p = { time: bars[i].time, value: v != null && Number.isFinite(v) ? v : null };
22113
+ const c = colors?.[i];
22114
+ if (c != null) p.color = c;
22115
+ pts[i] = p;
22116
+ }
22117
+ return pts;
22118
+ }
22119
+ var ClassicIndicator = class {
22120
+ constructor(spec) {
22121
+ this.spec = spec;
22122
+ this.inputs = {};
22123
+ }
22124
+ start(ctx, inputs) {
22125
+ this.ctx = ctx;
22126
+ this.inputs = inputs;
22127
+ this.recompute();
22128
+ }
22129
+ onBars() {
22130
+ this.recompute();
22131
+ }
22132
+ onViewport() {
22133
+ }
22134
+ setInputs(inputs) {
22135
+ this.inputs = inputs;
22136
+ this.recompute();
22137
+ }
22138
+ /** Nothing to free — the compute reads the live bar accessor on demand. */
22139
+ suspend() {
22140
+ }
22141
+ resume() {
22142
+ this.recompute();
22143
+ }
22144
+ stop() {
22145
+ }
22146
+ recompute() {
22147
+ const bars = this.ctx.bars();
22148
+ const out = this.spec.compute(bars, this.inputs);
22149
+ const instanceId = this.spec.type;
22150
+ const series = [];
22151
+ const idsByKey = /* @__PURE__ */ new Map();
22152
+ out.plots.forEach((plot, ordinal) => {
22153
+ const kind = plot.kind ?? "line";
22154
+ const id = stableSeriesId({ instanceId, kind, title: plot.title, ordinal });
22155
+ idsByKey.set(plot.key, id);
22156
+ series.push({
22157
+ id,
22158
+ title: plot.title,
22159
+ paneId: "",
22160
+ // stamped by the orchestrator
22161
+ kind,
22162
+ points: toPoints(bars, plot.values, plot.colors),
22163
+ style: {
22164
+ color: plot.color,
22165
+ width: plot.width ?? 1,
22166
+ lineStyle: plot.lineStyle ?? "solid",
22167
+ ...plot.base != null ? { base: plot.base } : {}
22168
+ },
22169
+ ...plot.overlay ? { overlay: true } : {}
22170
+ });
22171
+ });
22172
+ if (out.markers && out.markers.length > 0) {
22173
+ series.push({
22174
+ id: stableSeriesId({ instanceId, kind: "markers", title: "markers", ordinal: 0 }),
22175
+ title: this.spec.shortTitle ?? this.spec.title,
22176
+ paneId: "",
22177
+ kind: "markers",
22178
+ markers: out.markers
22179
+ });
22180
+ }
22181
+ const fills = (out.bands ?? []).flatMap((band, ordinal) => {
22182
+ const from = idsByKey.get(band.from);
22183
+ const to = idsByKey.get(band.to);
22184
+ if (!from || !to) return [];
22185
+ return [
22186
+ {
22187
+ id: stableSeriesId({ instanceId, kind: "fill", title: band.key, ordinal }),
22188
+ paneId: "",
22189
+ fromSeriesId: from,
22190
+ toSeriesId: to,
22191
+ color: band.color
22192
+ }
22193
+ ];
22194
+ });
22195
+ const priceLines = (out.levels ?? []).map((level, ordinal) => ({
22196
+ id: stableSeriesId({ instanceId, kind: "hline", title: level.key, ordinal }),
22197
+ paneId: "",
22198
+ price: level.price,
22199
+ ...level.color != null ? { color: level.color } : {},
22200
+ lineStyle: level.lineStyle ?? "dashed",
22201
+ width: 1,
22202
+ ...level.title != null ? { title: level.title } : {}
22203
+ }));
22204
+ const polylines = (out.polylines ?? []).map((poly, ordinal) => {
22205
+ const { key, ...rest } = poly;
22206
+ return {
22207
+ id: stableSeriesId({ instanceId, kind: "polyline", title: key, ordinal }),
22208
+ paneId: "",
22209
+ ...rest
22210
+ };
22211
+ });
22212
+ this.ctx.emit({ series, fills, priceLines, polylines });
22213
+ this.ctx.setStatus("idle");
22214
+ }
22215
+ };
22216
+ function classicDescriptor(spec) {
22217
+ return {
22218
+ type: spec.type,
22219
+ title: spec.title,
22220
+ ...spec.shortTitle != null ? { shortTitle: spec.shortTitle } : {},
22221
+ paneHint: spec.overlay ? "price" : "new",
22222
+ overlay: spec.overlay,
22223
+ // Studies stack (a 20 and a 200 moving average side by side) — unlike the layer-backed natives.
22224
+ multiInstance: true,
22225
+ inputsSchema: () => spec.inputs,
22226
+ defaultInputs: () => Object.fromEntries(spec.inputs.map((i) => [i.key, i.defval])),
22227
+ create: () => new ClassicIndicator(spec)
22228
+ };
22229
+ }
22230
+
22231
+ // src/core/native-indicators/classics/math.ts
22232
+ var SOURCES2 = ["Close", "Open", "High", "Low", "HL2", "HLC3", "OHLC4", "HLCC4"];
22233
+ function sourceValues(bars, source) {
22234
+ const out = new Array(bars.length);
22235
+ for (let i = 0; i < bars.length; i++) {
22236
+ const b = bars[i];
22237
+ switch (source) {
22238
+ case "Open":
22239
+ out[i] = b.open;
22240
+ break;
22241
+ case "High":
22242
+ out[i] = b.high;
22243
+ break;
22244
+ case "Low":
22245
+ out[i] = b.low;
22246
+ break;
22247
+ case "HL2":
22248
+ out[i] = (b.high + b.low) / 2;
22249
+ break;
22250
+ case "HLC3":
22251
+ out[i] = (b.high + b.low + b.close) / 3;
22252
+ break;
22253
+ case "OHLC4":
22254
+ out[i] = (b.open + b.high + b.low + b.close) / 4;
22255
+ break;
22256
+ case "HLCC4":
22257
+ out[i] = (b.high + b.low + b.close + b.close) / 4;
22258
+ break;
22259
+ default:
22260
+ out[i] = b.close;
22261
+ }
22262
+ }
22263
+ return out;
22264
+ }
22265
+ function volumes(bars) {
22266
+ return bars.map((b) => b.volume != null && Number.isFinite(b.volume) ? b.volume : Number.NaN);
22267
+ }
22268
+ function highs(bars) {
22269
+ return bars.map((b) => b.high);
22270
+ }
22271
+ function lows(bars) {
22272
+ return bars.map((b) => b.low);
22273
+ }
22274
+ function closes(bars) {
22275
+ return bars.map((b) => b.close);
22276
+ }
22277
+ function opens(bars) {
22278
+ return bars.map((b) => b.open);
22279
+ }
22280
+ function zip(a, b, f) {
22281
+ const out = new Array(a.length);
22282
+ for (let i = 0; i < a.length; i++) {
22283
+ const x = a[i];
22284
+ const y = b[i];
22285
+ out[i] = Number.isFinite(x) && Number.isFinite(y) ? f(x, y) : Number.NaN;
22286
+ }
22287
+ return out;
22288
+ }
22289
+ function map(a, f) {
22290
+ const out = new Array(a.length);
22291
+ for (let i = 0; i < a.length; i++) {
22292
+ const x = a[i];
22293
+ out[i] = Number.isFinite(x) ? f(x) : Number.NaN;
22294
+ }
22295
+ return out;
22296
+ }
22297
+ function sub(a, b) {
22298
+ return zip(a, b, (x, y) => x - y);
22299
+ }
22300
+ function windowed(v, len, fold) {
22301
+ const out = new Array(v.length).fill(Number.NaN);
22302
+ if (len <= 0) return out;
22303
+ let run = 0;
22304
+ for (let i = 0; i < v.length; i++) {
22305
+ run = Number.isFinite(v[i]) ? run + 1 : 0;
22306
+ if (run >= len) out[i] = fold(v, i - len + 1, i);
22307
+ }
22308
+ return out;
22309
+ }
22310
+ function sum(v, len) {
22311
+ return windowed(v, len, (w, from, to) => {
22312
+ let s = 0;
22313
+ for (let k = from; k <= to; k++) s += w[k];
22314
+ return s;
22315
+ });
22316
+ }
22317
+ function sma(v, len) {
22318
+ return windowed(v, len, (w, from, to) => {
22319
+ let s = 0;
22320
+ for (let k = from; k <= to; k++) s += w[k];
22321
+ return s / len;
22322
+ });
22323
+ }
22324
+ function wma(v, len) {
22325
+ const norm = len * (len + 1) / 2;
22326
+ return windowed(v, len, (w, from, to) => {
22327
+ let s = 0;
22328
+ for (let k = from; k <= to; k++) s += w[k] * (k - from + 1);
22329
+ return s / norm;
22330
+ });
22331
+ }
22332
+ function vwma(src, vol, len) {
22333
+ const pv = zip(src, vol, (p, q) => p * q);
22334
+ return zip(sum(pv, len), sum(vol, len), (a, b) => b > 0 ? a / b : Number.NaN);
22335
+ }
22336
+ function stdev(v, len) {
22337
+ return windowed(v, len, (w, from, to) => {
22338
+ let s = 0;
22339
+ for (let k = from; k <= to; k++) s += w[k];
22340
+ const mean = s / len;
22341
+ let sq = 0;
22342
+ for (let k = from; k <= to; k++) {
22343
+ const d = w[k] - mean;
22344
+ sq += d * d;
22345
+ }
22346
+ return Math.sqrt(sq / len);
22347
+ });
22348
+ }
22349
+ function highest(v, len) {
22350
+ return windowed(v, len, (w, from, to) => {
22351
+ let m = -Infinity;
22352
+ for (let k = from; k <= to; k++) if (w[k] > m) m = w[k];
22353
+ return m;
22354
+ });
22355
+ }
22356
+ function lowest(v, len) {
22357
+ return windowed(v, len, (w, from, to) => {
22358
+ let m = Infinity;
22359
+ for (let k = from; k <= to; k++) if (w[k] < m) m = w[k];
22360
+ return m;
22361
+ });
22362
+ }
22363
+ function barsSinceHighest(v, len) {
22364
+ return windowed(v, len, (w, from, to) => {
22365
+ let m = -Infinity;
22366
+ let at = to;
22367
+ for (let k = from; k <= to; k++) {
22368
+ if (w[k] >= m) {
22369
+ m = w[k];
22370
+ at = k;
22371
+ }
22372
+ }
22373
+ return to - at;
22374
+ });
22375
+ }
22376
+ function barsSinceLowest(v, len) {
22377
+ return windowed(v, len, (w, from, to) => {
22378
+ let m = Infinity;
22379
+ let at = to;
22380
+ for (let k = from; k <= to; k++) {
22381
+ if (w[k] <= m) {
22382
+ m = w[k];
22383
+ at = k;
22384
+ }
22385
+ }
22386
+ return to - at;
22387
+ });
22388
+ }
22389
+ function recursive(v, len, alpha) {
22390
+ const out = new Array(v.length).fill(Number.NaN);
22391
+ let run = 0;
22392
+ let prev2 = Number.NaN;
22393
+ for (let i = 0; i < v.length; i++) {
22394
+ const x = v[i];
22395
+ if (!Number.isFinite(x)) {
22396
+ run = 0;
22397
+ prev2 = Number.NaN;
22398
+ continue;
22399
+ }
22400
+ run += 1;
22401
+ if (Number.isFinite(prev2)) {
22402
+ prev2 = alpha * x + (1 - alpha) * prev2;
22403
+ out[i] = prev2;
22404
+ } else if (run >= len) {
22405
+ let s = 0;
22406
+ for (let k = i - len + 1; k <= i; k++) s += v[k];
22407
+ prev2 = s / len;
22408
+ out[i] = prev2;
22409
+ }
22410
+ }
22411
+ return out;
22412
+ }
22413
+ function ema(v, len) {
22414
+ return recursive(v, len, 2 / (len + 1));
22415
+ }
22416
+ function rma(v, len) {
22417
+ return recursive(v, len, 1 / len);
22418
+ }
22419
+ function change(v, n = 1) {
22420
+ const out = new Array(v.length).fill(Number.NaN);
22421
+ for (let i = n; i < v.length; i++) {
22422
+ const a = v[i];
22423
+ const b = v[i - n];
22424
+ if (Number.isFinite(a) && Number.isFinite(b)) out[i] = a - b;
22425
+ }
22426
+ return out;
22427
+ }
22428
+ function roc(v, len) {
22429
+ const out = new Array(v.length).fill(Number.NaN);
22430
+ for (let i = len; i < v.length; i++) {
22431
+ const a = v[i];
22432
+ const b = v[i - len];
22433
+ if (Number.isFinite(a) && Number.isFinite(b) && b !== 0) out[i] = 100 * (a - b) / b;
22434
+ }
22435
+ return out;
22436
+ }
22437
+ function shift(v, n) {
22438
+ const out = new Array(v.length).fill(Number.NaN);
22439
+ for (let i = 0; i < v.length; i++) {
22440
+ const j = i - n;
22441
+ if (j >= 0 && j < v.length) out[i] = v[j];
22442
+ }
22443
+ return out;
22444
+ }
22445
+ function trueRange(bars) {
22446
+ const out = new Array(bars.length).fill(Number.NaN);
22447
+ for (let i = 0; i < bars.length; i++) {
22448
+ const b = bars[i];
22449
+ if (i === 0) {
22450
+ out[i] = b.high - b.low;
22451
+ continue;
22452
+ }
22453
+ const pc = bars[i - 1].close;
22454
+ out[i] = Math.max(b.high - b.low, Math.abs(b.high - pc), Math.abs(b.low - pc));
22455
+ }
22456
+ return out;
22457
+ }
22458
+ function atr(bars, len) {
22459
+ return rma(trueRange(bars), len);
22460
+ }
22461
+ function rsi(v, len) {
22462
+ const d = change(v);
22463
+ const up = map(d, (x) => Math.max(x, 0));
22464
+ const dn = map(d, (x) => Math.max(-x, 0));
22465
+ return zip(rma(up, len), rma(dn, len), (u, w) => w === 0 ? 100 : 100 - 100 / (1 + u / w));
22466
+ }
22467
+ function stoch(v, hi, lo, len) {
22468
+ const hh = highest(hi, len);
22469
+ const ll = lowest(lo, len);
22470
+ const out = new Array(v.length).fill(Number.NaN);
22471
+ for (let i = 0; i < v.length; i++) {
22472
+ const x = v[i];
22473
+ const h = hh[i];
22474
+ const l = ll[i];
22475
+ if (!Number.isFinite(x) || !Number.isFinite(h) || !Number.isFinite(l)) continue;
22476
+ out[i] = h === l ? 50 : 100 * (x - l) / (h - l);
22477
+ }
22478
+ return out;
22479
+ }
22480
+ function linreg(v, len) {
22481
+ const sx = (len - 1) * len / 2;
22482
+ const sxx = (len - 1) * len * (2 * len - 1) / 6;
22483
+ const denom = len * sxx - sx * sx;
22484
+ return windowed(v, len, (w, from, to) => {
22485
+ let sy = 0;
22486
+ let sxy = 0;
22487
+ for (let k = from; k <= to; k++) {
22488
+ const x = k - from;
22489
+ sy += w[k];
22490
+ sxy += x * w[k];
22491
+ }
22492
+ const slope = denom === 0 ? 0 : (len * sxy - sx * sy) / denom;
22493
+ const intercept = (sy - slope * sx) / len;
22494
+ return intercept + slope * (len - 1);
22495
+ });
22496
+ }
22497
+ function swma(v) {
22498
+ const out = new Array(v.length).fill(Number.NaN);
22499
+ for (let i = 3; i < v.length; i++) {
22500
+ const a = v[i - 3];
22501
+ const b = v[i - 2];
22502
+ const c = v[i - 1];
22503
+ const d = v[i];
22504
+ if (Number.isFinite(a) && Number.isFinite(b) && Number.isFinite(c) && Number.isFinite(d)) {
22505
+ out[i] = (a + 2 * b + 2 * c + d) / 6;
22506
+ }
22507
+ }
22508
+ return out;
22509
+ }
22510
+ function percentRank(v, len) {
22511
+ const out = new Array(v.length).fill(Number.NaN);
22512
+ for (let i = len; i < v.length; i++) {
22513
+ const x = v[i];
22514
+ if (!Number.isFinite(x)) continue;
22515
+ let below = 0;
22516
+ let count = 0;
22517
+ for (let k = i - len; k < i; k++) {
22518
+ const y = v[k];
22519
+ if (!Number.isFinite(y)) {
22520
+ count = -1;
22521
+ break;
22522
+ }
22523
+ count += 1;
22524
+ if (y <= x) below += 1;
22525
+ }
22526
+ if (count === len) out[i] = 100 * below / len;
22527
+ }
22528
+ return out;
22529
+ }
22530
+ function cumSum(v) {
22531
+ const out = new Array(v.length).fill(Number.NaN);
22532
+ let acc = 0;
22533
+ let seen = false;
22534
+ for (let i = 0; i < v.length; i++) {
22535
+ const x = v[i];
22536
+ if (Number.isFinite(x)) {
22537
+ acc += x;
22538
+ seen = true;
22539
+ }
22540
+ if (seen) out[i] = acc;
22541
+ }
22542
+ return out;
22543
+ }
22544
+
22545
+ // src/core/native-indicators/classics/shared.ts
22546
+ function lengthInput(defval, key = "length", title = "Length", max = 5e3) {
22547
+ return { key, title, type: "int", defval, min: 1, max, step: 1 };
22548
+ }
22549
+ function sourceInput(defval = "Close") {
22550
+ return { key: "source", title: "Source", type: "string", defval, options: SOURCES2 };
22551
+ }
22552
+ function colorInput(defval = SERIES_LINE, title = "Color", key = "color") {
22553
+ return { key, title, type: "color", defval };
22554
+ }
22555
+ function withAlpha2(hex, alpha = 0.08) {
22556
+ const m = /^#([0-9a-f]{6})$/i.exec(hex.trim());
22557
+ if (!m) return hex;
22558
+ const a = Math.round(alpha * 255).toString(16).padStart(2, "0");
22559
+ return `#${m[1]}${a}`;
22560
+ }
22561
+
22562
+ // src/core/native-indicators/classics/averages.ts
22563
+ var MA_KINDS = ["SMA", "EMA", "WMA", "RMA", "VWMA"];
22564
+ var offsetInput = { key: "offset", title: "Offset", type: "int", defval: 0, min: -500, max: 500, step: 1 };
22565
+ function fixedAverage(type, title, shortTitle, smooth) {
22566
+ return {
22567
+ type,
22568
+ title,
22569
+ shortTitle,
22570
+ overlay: true,
22571
+ inputs: [lengthInput(20), sourceInput(), offsetInput, colorInput()],
22572
+ compute: (bars, inputs) => {
22573
+ const len = num3(inputs, "length", 20);
22574
+ let values = smooth(sourceValues(bars, str3(inputs, "source", "Close")), len);
22575
+ const offset = Math.trunc(num3(inputs, "offset", 0));
22576
+ if (offset !== 0) values = shift(values, offset);
22577
+ return { plots: [{ key: shortTitle.toLowerCase(), title: shortTitle, values, color: str3(inputs, "color", SERIES_LINE), width: 2 }] };
22578
+ }
22579
+ };
22580
+ }
22581
+ var simpleMa = fixedAverage("sma", "Simple Moving Average", "SMA", sma);
22582
+ var exponentialMa = fixedAverage("ema", "Exponential Moving Average", "EMA", ema);
22583
+ var movingAverage = {
22584
+ type: "moving-average",
22585
+ title: "Moving Average",
22586
+ shortTitle: "MA",
22587
+ overlay: true,
22588
+ inputs: [
22589
+ { key: "maType", title: "Type", type: "string", defval: "SMA", options: MA_KINDS },
22590
+ lengthInput(20),
22591
+ sourceInput(),
22592
+ offsetInput,
22593
+ colorInput()
22594
+ ],
22595
+ compute: (bars, inputs) => {
22596
+ const len = num3(inputs, "length", 20);
22597
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
22598
+ const kind = str3(inputs, "maType", "SMA");
22599
+ let values;
22600
+ switch (kind) {
22601
+ case "EMA":
22602
+ values = ema(src, len);
22603
+ break;
22604
+ case "WMA":
22605
+ values = wma(src, len);
22606
+ break;
22607
+ case "RMA":
22608
+ values = rma(src, len);
22609
+ break;
22610
+ case "VWMA":
22611
+ values = vwma(src, volumes(bars), len);
22612
+ break;
22613
+ default:
22614
+ values = sma(src, len);
22615
+ }
22616
+ const offset = Math.trunc(num3(inputs, "offset", 0));
22617
+ if (offset !== 0) values = shift(values, offset);
22618
+ return { plots: [{ key: "ma", title: kind, values, color: str3(inputs, "color", SERIES_LINE), width: 2 }] };
22619
+ }
22620
+ };
22621
+ var smoothedMa = {
22622
+ type: "rma",
22623
+ title: "Smoothed Moving Average",
22624
+ shortTitle: "RMA",
22625
+ overlay: true,
22626
+ inputs: [lengthInput(14), sourceInput(), colorInput()],
22627
+ compute: (bars, inputs) => ({
22628
+ plots: [
22629
+ {
22630
+ key: "rma",
22631
+ title: "RMA",
22632
+ values: rma(sourceValues(bars, str3(inputs, "source", "Close")), num3(inputs, "length", 14)),
22633
+ color: str3(inputs, "color", SERIES_LINE),
22634
+ width: 2
22635
+ }
22636
+ ]
22637
+ })
22638
+ };
22639
+ var zlema = {
22640
+ type: "zlema",
22641
+ title: "Zero-Lag Exponential Moving Average",
22642
+ shortTitle: "ZLEMA",
22643
+ overlay: true,
22644
+ inputs: [lengthInput(14), sourceInput(), colorInput()],
22645
+ compute: (bars, inputs) => {
22646
+ const len = num3(inputs, "length", 14);
22647
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
22648
+ const lag = Math.floor((len - 1) / 2);
22649
+ const lagged = shift(src, lag);
22650
+ const delagged = zip(src, lagged, (a, b) => 2 * a - b);
22651
+ return { plots: [{ key: "zlema", title: "ZLEMA", values: ema(delagged, len), color: str3(inputs, "color", SERIES_LINE), width: 2 }] };
22652
+ }
22653
+ };
22654
+ var vidya = {
22655
+ type: "vidya",
22656
+ title: "Variable Index Dynamic Average",
22657
+ shortTitle: "VIDYA",
22658
+ overlay: true,
22659
+ inputs: [lengthInput(14), { key: "cmoLength", title: "Momentum length", type: "int", defval: 9, min: 1, max: 500, step: 1 }, sourceInput(), colorInput()],
22660
+ compute: (bars, inputs) => {
22661
+ const len = num3(inputs, "length", 14);
22662
+ const cmoLen = num3(inputs, "cmoLength", 9);
22663
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
22664
+ const d = change(src);
22665
+ const su = new Array(src.length).fill(Number.NaN);
22666
+ const sd = new Array(src.length).fill(Number.NaN);
22667
+ for (let i = cmoLen; i < src.length; i++) {
22668
+ let u = 0;
22669
+ let w = 0;
22670
+ let ok = true;
22671
+ for (let k = i - cmoLen + 1; k <= i; k++) {
22672
+ const x = d[k];
22673
+ if (!Number.isFinite(x)) {
22674
+ ok = false;
22675
+ break;
22676
+ }
22677
+ if (x > 0) u += x;
22678
+ else w -= x;
22679
+ }
22680
+ if (ok) {
22681
+ su[i] = u;
22682
+ sd[i] = w;
22683
+ }
22034
22684
  }
22685
+ const alpha = 2 / (len + 1);
22686
+ const out = new Array(src.length).fill(Number.NaN);
22687
+ let prev2 = Number.NaN;
22688
+ for (let i = 0; i < src.length; i++) {
22689
+ const x = src[i];
22690
+ const u = su[i];
22691
+ const w = sd[i];
22692
+ if (!Number.isFinite(x) || !Number.isFinite(u) || !Number.isFinite(w)) continue;
22693
+ const total = u + w;
22694
+ const k = total === 0 ? 0 : Math.abs((u - w) / total);
22695
+ prev2 = Number.isFinite(prev2) ? alpha * k * x + (1 - alpha * k) * prev2 : x;
22696
+ out[i] = prev2;
22697
+ }
22698
+ return { plots: [{ key: "vidya", title: "VIDYA", values: out, color: str3(inputs, "color", SERIES_LINE), width: 2 }] };
22699
+ }
22700
+ };
22701
+ var maEnvelope = {
22702
+ type: "ma-envelope",
22703
+ title: "Moving Average Envelope",
22704
+ shortTitle: "MA Env",
22705
+ overlay: true,
22706
+ inputs: [
22707
+ { key: "maType", title: "Type", type: "string", defval: "SMA", options: ["SMA", "EMA"] },
22708
+ lengthInput(20),
22709
+ sourceInput(),
22710
+ { key: "percent", title: "Percent", type: "float", defval: 2.5, min: 0.01, max: 50, step: 0.1 },
22711
+ colorInput()
22712
+ ],
22713
+ compute: (bars, inputs) => {
22714
+ const len = num3(inputs, "length", 20);
22715
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
22716
+ const basis = str3(inputs, "maType", "SMA") === "EMA" ? ema(src, len) : sma(src, len);
22717
+ const k = num3(inputs, "percent", 2.5) / 100;
22718
+ const ink = str3(inputs, "color", SERIES_LINE);
22719
+ return {
22720
+ plots: [
22721
+ { key: "basis", title: "Basis", values: basis, color: ink, width: 2 },
22722
+ { key: "upper", title: "Upper", values: map(basis, (x) => x * (1 + k)), color: ink },
22723
+ { key: "lower", title: "Lower", values: map(basis, (x) => x * (1 - k)), color: ink }
22724
+ ],
22725
+ bands: [{ key: "envelope", from: "upper", to: "lower", color: withAlpha2(ink) }]
22726
+ };
22727
+ }
22728
+ };
22729
+ var linearRegression = {
22730
+ type: "linear-regression",
22731
+ title: "Linear Regression Curve",
22732
+ shortTitle: "LinReg",
22733
+ overlay: true,
22734
+ inputs: [lengthInput(14), sourceInput(), colorInput()],
22735
+ compute: (bars, inputs) => ({
22736
+ plots: [
22737
+ {
22738
+ key: "linreg",
22739
+ title: "LinReg",
22740
+ values: linreg(sourceValues(bars, str3(inputs, "source", "Close")), num3(inputs, "length", 14)),
22741
+ color: str3(inputs, "color", SERIES_LINE),
22742
+ width: 2
22743
+ }
22744
+ ]
22745
+ })
22746
+ };
22747
+ var averageSpecs = [simpleMa, exponentialMa, movingAverage, smoothedMa, zlema, vidya, maEnvelope, linearRegression];
22748
+
22749
+ // src/core/native-indicators/classics/bands.ts
22750
+ function bollinger(bars, inputs) {
22751
+ const len = num3(inputs, "length", 20);
22752
+ const mult = num3(inputs, "mult", 2);
22753
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
22754
+ const basis = sma(src, len);
22755
+ const dev = map(stdev(src, len), (x) => x * mult);
22756
+ return { basis, upper: zip(basis, dev, (b, d) => b + d), lower: zip(basis, dev, (b, d) => b - d) };
22757
+ }
22758
+ var BB_INPUTS = [lengthInput(20), sourceInput(), { key: "mult", title: "StdDev", type: "float", defval: 2, min: 0.1, max: 50, step: 0.1 }];
22759
+ var bollingerBands = {
22760
+ type: "bollinger-bands",
22761
+ title: "Bollinger Bands",
22762
+ shortTitle: "BB",
22763
+ overlay: true,
22764
+ inputs: [...BB_INPUTS, colorInput()],
22765
+ compute: (bars, inputs) => {
22766
+ const { basis, upper, lower } = bollinger(bars, inputs);
22767
+ const ink = str3(inputs, "color", SERIES_LINE);
22768
+ return {
22769
+ plots: [
22770
+ { key: "basis", title: "Basis", values: basis, color: WARNING },
22771
+ { key: "upper", title: "Upper", values: upper, color: ink },
22772
+ { key: "lower", title: "Lower", values: lower, color: ink }
22773
+ ],
22774
+ bands: [{ key: "bb", from: "upper", to: "lower", color: withAlpha2(ink) }]
22775
+ };
22776
+ }
22777
+ };
22778
+ var percentB = {
22779
+ type: "percent-b",
22780
+ title: "Bollinger Bands %B",
22781
+ shortTitle: "%B",
22782
+ overlay: false,
22783
+ inputs: [...BB_INPUTS, colorInput()],
22784
+ compute: (bars, inputs) => {
22785
+ const { upper, lower } = bollinger(bars, inputs);
22786
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
22787
+ const values = src.map((x, i) => {
22788
+ const u = upper[i];
22789
+ const l = lower[i];
22790
+ return Number.isFinite(u) && Number.isFinite(l) && u !== l ? (x - l) / (u - l) : Number.NaN;
22791
+ });
22792
+ return {
22793
+ plots: [{ key: "pb", title: "%B", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
22794
+ levels: [
22795
+ { key: "upper", price: 1, color: NEUTRAL },
22796
+ { key: "middle", price: 0.5, color: NEUTRAL, lineStyle: "dotted" },
22797
+ { key: "lower", price: 0, color: NEUTRAL }
22798
+ ]
22799
+ };
22800
+ }
22801
+ };
22802
+ var bandwidth = {
22803
+ type: "bandwidth",
22804
+ title: "Bollinger Bands Width",
22805
+ shortTitle: "BandWidth",
22806
+ overlay: false,
22807
+ inputs: [...BB_INPUTS, colorInput()],
22808
+ compute: (bars, inputs) => {
22809
+ const { basis, upper, lower } = bollinger(bars, inputs);
22810
+ const values = basis.map((b, i) => {
22811
+ const u = upper[i];
22812
+ const l = lower[i];
22813
+ return Number.isFinite(b) && b !== 0 && Number.isFinite(u) && Number.isFinite(l) ? (u - l) / b * 100 : Number.NaN;
22814
+ });
22815
+ return { plots: [{ key: "bbw", title: "BBW", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }] };
22816
+ }
22817
+ };
22818
+ var keltner = {
22819
+ type: "keltner-channels",
22820
+ title: "Keltner Channels",
22821
+ shortTitle: "KC",
22822
+ overlay: true,
22823
+ inputs: [
22824
+ lengthInput(20),
22825
+ { key: "mult", title: "Multiplier", type: "float", defval: 2, min: 0.1, max: 50, step: 0.1 },
22826
+ { key: "atrLength", title: "ATR length", type: "int", defval: 10, min: 1, max: 500, step: 1 },
22827
+ sourceInput(),
22828
+ colorInput()
22829
+ ],
22830
+ compute: (bars, inputs) => {
22831
+ const len = num3(inputs, "length", 20);
22832
+ const mult = num3(inputs, "mult", 2);
22833
+ const basis = ema(sourceValues(bars, str3(inputs, "source", "Close")), len);
22834
+ const range = map(atr(bars, num3(inputs, "atrLength", 10)), (x) => x * mult);
22835
+ const ink = str3(inputs, "color", SERIES_LINE);
22836
+ return {
22837
+ plots: [
22838
+ { key: "basis", title: "Basis", values: basis, color: WARNING },
22839
+ { key: "upper", title: "Upper", values: zip(basis, range, (b, r) => b + r), color: ink },
22840
+ { key: "lower", title: "Lower", values: zip(basis, range, (b, r) => b - r), color: ink }
22841
+ ],
22842
+ bands: [{ key: "kc", from: "upper", to: "lower", color: withAlpha2(ink) }]
22843
+ };
22844
+ }
22845
+ };
22846
+ var donchian = {
22847
+ type: "donchian-channels",
22848
+ title: "Donchian Channels",
22849
+ shortTitle: "DC",
22850
+ overlay: true,
22851
+ inputs: [lengthInput(20), colorInput()],
22852
+ compute: (bars, inputs) => {
22853
+ const len = num3(inputs, "length", 20);
22854
+ const upper = highest(highs(bars), len);
22855
+ const lower = lowest(lows(bars), len);
22856
+ const ink = str3(inputs, "color", SERIES_LINE);
22857
+ return {
22858
+ plots: [
22859
+ { key: "basis", title: "Basis", values: zip(upper, lower, (u, l) => (u + l) / 2), color: WARNING },
22860
+ { key: "upper", title: "Upper", values: upper, color: ink },
22861
+ { key: "lower", title: "Lower", values: lower, color: ink }
22862
+ ],
22863
+ bands: [{ key: "dc", from: "upper", to: "lower", color: withAlpha2(ink) }]
22864
+ };
22865
+ }
22866
+ };
22867
+ var chandelier = {
22868
+ type: "chandelier-exit",
22869
+ title: "Chandelier Exit",
22870
+ shortTitle: "CE",
22871
+ overlay: true,
22872
+ inputs: [lengthInput(22), { key: "mult", title: "ATR multiplier", type: "float", defval: 3, min: 0.1, max: 50, step: 0.1 }],
22873
+ compute: (bars, inputs) => {
22874
+ const len = num3(inputs, "length", 22);
22875
+ const range = map(atr(bars, len), (x) => x * num3(inputs, "mult", 3));
22876
+ const long = zip(highest(highs(bars), len), range, (h, r) => h - r);
22877
+ const short = zip(lowest(lows(bars), len), range, (l, r) => l + r);
22878
+ return {
22879
+ plots: [
22880
+ { key: "long", title: "Long stop", values: long, color: BULLISH },
22881
+ { key: "short", title: "Short stop", values: short, color: BEARISH }
22882
+ ]
22883
+ };
22884
+ }
22885
+ };
22886
+ var chandeKroll = {
22887
+ type: "chande-kroll-stop",
22888
+ title: "Chande Kroll Stop",
22889
+ shortTitle: "CKS",
22890
+ overlay: true,
22891
+ inputs: [
22892
+ { key: "p", title: "ATR length", type: "int", defval: 10, min: 1, max: 500, step: 1 },
22893
+ { key: "x", title: "ATR multiplier", type: "float", defval: 1, min: 0.1, max: 50, step: 0.1 },
22894
+ { key: "q", title: "Stop length", type: "int", defval: 9, min: 1, max: 500, step: 1 }
22895
+ ],
22896
+ compute: (bars, inputs) => {
22897
+ const p = num3(inputs, "p", 10);
22898
+ const x = num3(inputs, "x", 1);
22899
+ const q = num3(inputs, "q", 9);
22900
+ const range = map(atr(bars, p), (v) => v * x);
22901
+ const firstHigh = zip(highest(highs(bars), p), range, (h, r) => h - r);
22902
+ const firstLow = zip(lowest(lows(bars), p), range, (l, r) => l + r);
22903
+ return {
22904
+ plots: [
22905
+ { key: "stopShort", title: "Stop short", values: highest(firstHigh, q), color: BEARISH },
22906
+ { key: "stopLong", title: "Stop long", values: lowest(firstLow, q), color: BULLISH }
22907
+ ]
22908
+ };
22909
+ }
22910
+ };
22911
+ var supertrend = {
22912
+ type: "supertrend",
22913
+ title: "SuperTrend",
22914
+ shortTitle: "SuperTrend",
22915
+ overlay: true,
22916
+ inputs: [
22917
+ { key: "atrLength", title: "ATR length", type: "int", defval: 10, min: 1, max: 500, step: 1 },
22918
+ { key: "mult", title: "Factor", type: "float", defval: 3, min: 0.1, max: 50, step: 0.1 }
22919
+ ],
22920
+ compute: (bars, inputs) => {
22921
+ const len = num3(inputs, "atrLength", 10);
22922
+ const mult = num3(inputs, "mult", 3);
22923
+ const range = atr(bars, len);
22924
+ const n = bars.length;
22925
+ const st = new Array(n).fill(Number.NaN);
22926
+ const dir = new Array(n).fill(1);
22927
+ let up = Number.NaN;
22928
+ let dn = Number.NaN;
22929
+ let trend = 1;
22930
+ for (let i = 0; i < n; i++) {
22931
+ const r = range[i];
22932
+ if (!Number.isFinite(r)) continue;
22933
+ const b = bars[i];
22934
+ const mid = (b.high + b.low) / 2;
22935
+ const basicUp = mid - mult * r;
22936
+ const basicDn = mid + mult * r;
22937
+ const prevClose = i > 0 ? bars[i - 1].close : b.close;
22938
+ up = Number.isFinite(up) && prevClose > up ? Math.max(basicUp, up) : basicUp;
22939
+ dn = Number.isFinite(dn) && prevClose < dn ? Math.min(basicDn, dn) : basicDn;
22940
+ if (trend === 1 && b.close < up) trend = -1;
22941
+ else if (trend === -1 && b.close > dn) trend = 1;
22942
+ dir[i] = trend;
22943
+ st[i] = trend === 1 ? up : dn;
22944
+ }
22945
+ const colors = dir.map((d, i) => Number.isFinite(st[i]) ? d === 1 ? BULLISH : BEARISH : null);
22946
+ return { plots: [{ key: "st", title: "SuperTrend", values: st, color: BULLISH, width: 2, colors }] };
22947
+ }
22948
+ };
22949
+ var alligator = {
22950
+ type: "williams-alligator",
22951
+ title: "Williams Alligator",
22952
+ shortTitle: "Alligator",
22953
+ overlay: true,
22954
+ inputs: [
22955
+ { key: "jawLength", title: "Jaw length", type: "int", defval: 13, min: 1, max: 500, step: 1 },
22956
+ { key: "teethLength", title: "Teeth length", type: "int", defval: 8, min: 1, max: 500, step: 1 },
22957
+ { key: "lipsLength", title: "Lips length", type: "int", defval: 5, min: 1, max: 500, step: 1 },
22958
+ { key: "jawOffset", title: "Jaw offset", type: "int", defval: 8, min: 0, max: 100, step: 1 },
22959
+ { key: "teethOffset", title: "Teeth offset", type: "int", defval: 5, min: 0, max: 100, step: 1 },
22960
+ { key: "lipsOffset", title: "Lips offset", type: "int", defval: 3, min: 0, max: 100, step: 1 }
22961
+ ],
22962
+ compute: (bars, inputs) => {
22963
+ const hl2 = sourceValues(bars, "HL2");
22964
+ const jaw = shift(rma(hl2, num3(inputs, "jawLength", 13)), num3(inputs, "jawOffset", 8));
22965
+ const teeth = shift(rma(hl2, num3(inputs, "teethLength", 8)), num3(inputs, "teethOffset", 5));
22966
+ const lips = shift(rma(hl2, num3(inputs, "lipsLength", 5)), num3(inputs, "lipsOffset", 3));
22967
+ return {
22968
+ plots: [
22969
+ { key: "jaw", title: "Jaw", values: jaw, color: SERIES_LINE },
22970
+ { key: "teeth", title: "Teeth", values: teeth, color: BEARISH },
22971
+ { key: "lips", title: "Lips", values: lips, color: BULLISH }
22972
+ ]
22973
+ };
22974
+ }
22975
+ };
22976
+ var gator = {
22977
+ type: "gator-oscillator",
22978
+ title: "Gator Oscillator",
22979
+ shortTitle: "Gator Oscillator",
22980
+ overlay: false,
22981
+ inputs: [],
22982
+ compute: (bars) => {
22983
+ const hl2 = sourceValues(bars, "HL2");
22984
+ const jaw = shift(rma(hl2, 13), 8);
22985
+ const teeth = shift(rma(hl2, 8), 5);
22986
+ const lips = shift(rma(hl2, 5), 3);
22987
+ const upper = map(sub(jaw, teeth), Math.abs);
22988
+ const lower = map(sub(teeth, lips), (x) => -Math.abs(x));
22989
+ const colorByExpansion = (v) => v.map((x, i) => {
22990
+ if (!Number.isFinite(x)) return null;
22991
+ const prev2 = i > 0 ? v[i - 1] : Number.NaN;
22992
+ return Number.isFinite(prev2) && Math.abs(x) > Math.abs(prev2) ? BULLISH : BEARISH;
22993
+ });
22994
+ return {
22995
+ plots: [
22996
+ { key: "upper", title: "Upper", values: upper, kind: "histogram", color: BULLISH, colors: colorByExpansion(upper), base: 0 },
22997
+ { key: "lower", title: "Lower", values: lower, kind: "histogram", color: BEARISH, colors: colorByExpansion(lower), base: 0 }
22998
+ ]
22999
+ };
23000
+ }
23001
+ };
23002
+ var bandSpecs = [bollingerBands, percentB, bandwidth, keltner, donchian, chandelier, chandeKroll, supertrend, alligator, gator];
23003
+
23004
+ // src/core/native-indicators/classics/oscillators.ts
23005
+ function guideLevels(low, high, mid) {
23006
+ const levels = [
23007
+ { key: "upper", price: high, color: NEUTRAL },
23008
+ { key: "lower", price: low, color: NEUTRAL }
23009
+ ];
23010
+ if (mid != null) levels.splice(1, 0, { key: "middle", price: mid, color: NEUTRAL, lineStyle: "dotted" });
23011
+ return levels;
23012
+ }
23013
+ var ZERO_LEVEL = [{ key: "zero", price: 0, color: NEUTRAL }];
23014
+ function histColors(values) {
23015
+ return values.map((x, i) => {
23016
+ if (!Number.isFinite(x)) return null;
23017
+ const prev2 = i > 0 ? values[i - 1] : Number.NaN;
23018
+ const rising = Number.isFinite(prev2) ? x >= prev2 : true;
23019
+ if (x >= 0) return rising ? BULLISH : `${BULLISH}80`;
23020
+ return rising ? `${BEARISH}80` : BEARISH;
23021
+ });
23022
+ }
23023
+ var rsiSpec = {
23024
+ type: "rsi",
23025
+ title: "Relative Strength Index",
23026
+ shortTitle: "RSI",
23027
+ overlay: false,
23028
+ inputs: [lengthInput(14), sourceInput(), colorInput()],
23029
+ compute: (bars, inputs) => ({
23030
+ plots: [
23031
+ {
23032
+ key: "rsi",
23033
+ title: "RSI",
23034
+ values: rsi(sourceValues(bars, str3(inputs, "source", "Close")), num3(inputs, "length", 14)),
23035
+ color: str3(inputs, "color", SERIES_LINE),
23036
+ width: 2
23037
+ }
23038
+ ],
23039
+ levels: guideLevels(30, 70, 50)
23040
+ })
23041
+ };
23042
+ var stochastic = {
23043
+ type: "stochastic",
23044
+ title: "Stochastic",
23045
+ shortTitle: "Stoch",
23046
+ overlay: false,
23047
+ inputs: [
23048
+ lengthInput(14, "kLength", "%K length"),
23049
+ lengthInput(1, "kSmoothing", "%K smoothing", 500),
23050
+ lengthInput(3, "dLength", "%D smoothing", 500)
23051
+ ],
23052
+ compute: (bars, inputs) => {
23053
+ const k = sma(stoch(closes(bars), highs(bars), lows(bars), num3(inputs, "kLength", 14)), num3(inputs, "kSmoothing", 1));
23054
+ const d = sma(k, num3(inputs, "dLength", 3));
23055
+ return {
23056
+ plots: [
23057
+ { key: "k", title: "%K", values: k, color: SERIES_LINE, width: 2 },
23058
+ { key: "d", title: "%D", values: d, color: WARNING }
23059
+ ],
23060
+ levels: guideLevels(20, 80)
23061
+ };
23062
+ }
23063
+ };
23064
+ var stochasticRsi = {
23065
+ type: "stochastic-rsi",
23066
+ title: "Stochastic RSI",
23067
+ shortTitle: "Stoch RSI",
23068
+ overlay: false,
23069
+ inputs: [
23070
+ lengthInput(14, "rsiLength", "RSI length"),
23071
+ lengthInput(14, "stochLength", "Stochastic length"),
23072
+ lengthInput(3, "kSmoothing", "%K smoothing", 500),
23073
+ lengthInput(3, "dLength", "%D smoothing", 500),
23074
+ sourceInput()
23075
+ ],
23076
+ compute: (bars, inputs) => {
23077
+ const r = rsi(sourceValues(bars, str3(inputs, "source", "Close")), num3(inputs, "rsiLength", 14));
23078
+ const k = sma(stoch(r, r, r, num3(inputs, "stochLength", 14)), num3(inputs, "kSmoothing", 3));
23079
+ const d = sma(k, num3(inputs, "dLength", 3));
23080
+ return {
23081
+ plots: [
23082
+ { key: "k", title: "%K", values: k, color: SERIES_LINE, width: 2 },
23083
+ { key: "d", title: "%D", values: d, color: WARNING }
23084
+ ],
23085
+ levels: guideLevels(20, 80)
23086
+ };
23087
+ }
23088
+ };
23089
+ var macd = {
23090
+ type: "macd",
23091
+ title: "MACD",
23092
+ overlay: false,
23093
+ inputs: [
23094
+ lengthInput(12, "fastLength", "Fast length"),
23095
+ lengthInput(26, "slowLength", "Slow length"),
23096
+ lengthInput(9, "signalLength", "Signal smoothing", 500),
23097
+ sourceInput()
23098
+ ],
23099
+ compute: (bars, inputs) => {
23100
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
23101
+ const line = sub(ema(src, num3(inputs, "fastLength", 12)), ema(src, num3(inputs, "slowLength", 26)));
23102
+ const signal = ema(line, num3(inputs, "signalLength", 9));
23103
+ const hist = sub(line, signal);
23104
+ return {
23105
+ plots: [
23106
+ { key: "hist", title: "Histogram", values: hist, kind: "histogram", color: BULLISH, colors: histColors(hist), base: 0 },
23107
+ { key: "macd", title: "MACD", values: line, color: SERIES_LINE, width: 2 },
23108
+ { key: "signal", title: "Signal", values: signal, color: WARNING }
23109
+ ],
23110
+ levels: ZERO_LEVEL
23111
+ };
23112
+ }
23113
+ };
23114
+ var ppo = {
23115
+ type: "ppo",
23116
+ title: "Percentage Price Oscillator",
23117
+ shortTitle: "PPO",
23118
+ overlay: false,
23119
+ inputs: [
23120
+ lengthInput(12, "fastLength", "Fast length"),
23121
+ lengthInput(26, "slowLength", "Slow length"),
23122
+ lengthInput(9, "signalLength", "Signal smoothing", 500),
23123
+ sourceInput()
23124
+ ],
23125
+ compute: (bars, inputs) => {
23126
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
23127
+ const fast = ema(src, num3(inputs, "fastLength", 12));
23128
+ const slow = ema(src, num3(inputs, "slowLength", 26));
23129
+ const line = zip(fast, slow, (f, s) => s === 0 ? Number.NaN : (f - s) / s * 100);
23130
+ const signal = ema(line, num3(inputs, "signalLength", 9));
23131
+ const hist = sub(line, signal);
23132
+ return {
23133
+ plots: [
23134
+ { key: "hist", title: "Histogram", values: hist, kind: "histogram", color: BULLISH, colors: histColors(hist), base: 0 },
23135
+ { key: "ppo", title: "PPO", values: line, color: SERIES_LINE, width: 2 },
23136
+ { key: "signal", title: "Signal", values: signal, color: WARNING }
23137
+ ],
23138
+ levels: ZERO_LEVEL
23139
+ };
23140
+ }
23141
+ };
23142
+ var awesome = {
23143
+ type: "awesome-oscillator",
23144
+ title: "Awesome Oscillator",
23145
+ shortTitle: "AO",
23146
+ overlay: false,
23147
+ inputs: [],
23148
+ compute: (bars) => {
23149
+ const hl2 = sourceValues(bars, "HL2");
23150
+ const ao = sub(sma(hl2, 5), sma(hl2, 34));
23151
+ const colors = ao.map((x, i) => {
23152
+ if (!Number.isFinite(x)) return null;
23153
+ const prev2 = i > 0 ? ao[i - 1] : Number.NaN;
23154
+ return Number.isFinite(prev2) && x < prev2 ? BEARISH : BULLISH;
23155
+ });
23156
+ return {
23157
+ plots: [{ key: "ao", title: "AO", values: ao, kind: "histogram", color: BULLISH, colors, base: 0 }],
23158
+ levels: ZERO_LEVEL
23159
+ };
23160
+ }
23161
+ };
23162
+ var cci = {
23163
+ type: "commodity-channel-index",
23164
+ title: "Commodity Channel Index",
23165
+ shortTitle: "CCI",
23166
+ overlay: false,
23167
+ inputs: [lengthInput(20), sourceInput("HLC3"), colorInput()],
23168
+ compute: (bars, inputs) => {
23169
+ const len = num3(inputs, "length", 20);
23170
+ const src = sourceValues(bars, str3(inputs, "source", "HLC3"));
23171
+ const basis = sma(src, len);
23172
+ const dev = new Array(src.length).fill(Number.NaN);
23173
+ for (let i = len - 1; i < src.length; i++) {
23174
+ const mean = basis[i];
23175
+ if (!Number.isFinite(mean)) continue;
23176
+ let d = 0;
23177
+ for (let k = i - len + 1; k <= i; k++) d += Math.abs(src[k] - mean);
23178
+ dev[i] = d / len;
23179
+ }
23180
+ const values = src.map((x, i) => {
23181
+ const b = basis[i];
23182
+ const d = dev[i];
23183
+ return Number.isFinite(b) && Number.isFinite(d) && d !== 0 ? (x - b) / (0.015 * d) : Number.NaN;
23184
+ });
23185
+ return {
23186
+ plots: [{ key: "cci", title: "CCI", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23187
+ levels: guideLevels(-100, 100, 0)
23188
+ };
23189
+ }
23190
+ };
23191
+ var williamsR = {
23192
+ type: "williams-percent-r",
23193
+ title: "Williams %R",
23194
+ shortTitle: "%R",
23195
+ overlay: false,
23196
+ inputs: [lengthInput(14), colorInput()],
23197
+ compute: (bars, inputs) => {
23198
+ const values = map(stoch(closes(bars), highs(bars), lows(bars), num3(inputs, "length", 14)), (x) => x - 100);
23199
+ return {
23200
+ plots: [{ key: "wr", title: "%R", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23201
+ levels: guideLevels(-80, -20)
23202
+ };
23203
+ }
23204
+ };
23205
+ var cmo = {
23206
+ type: "chande-momentum-oscillator",
23207
+ title: "Chande Momentum Oscillator",
23208
+ shortTitle: "CMO",
23209
+ overlay: false,
23210
+ inputs: [lengthInput(9), sourceInput(), colorInput()],
23211
+ compute: (bars, inputs) => {
23212
+ const len = num3(inputs, "length", 9);
23213
+ const d = change(sourceValues(bars, str3(inputs, "source", "Close")));
23214
+ const su = sum(map(d, (x) => Math.max(x, 0)), len);
23215
+ const sd = sum(map(d, (x) => Math.max(-x, 0)), len);
23216
+ const values = zip(su, sd, (u, w) => u + w === 0 ? 0 : 100 * (u - w) / (u + w));
23217
+ return {
23218
+ plots: [{ key: "cmo", title: "CMO", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23219
+ levels: guideLevels(-50, 50, 0)
23220
+ };
23221
+ }
23222
+ };
23223
+ var connorsRsi = {
23224
+ type: "connors-rsi",
23225
+ title: "Connors RSI",
23226
+ shortTitle: "CRSI",
23227
+ overlay: false,
23228
+ inputs: [
23229
+ lengthInput(3, "rsiLength", "RSI length"),
23230
+ lengthInput(2, "streakLength", "Streak RSI length"),
23231
+ lengthInput(100, "rankLength", "Percent-rank length"),
23232
+ colorInput()
23233
+ ],
23234
+ compute: (bars, inputs) => {
23235
+ const c = closes(bars);
23236
+ const streak = new Array(c.length).fill(0);
23237
+ for (let i = 1; i < c.length; i++) {
23238
+ const d = c[i] - c[i - 1];
23239
+ streak[i] = d > 0 ? Math.max(streak[i - 1], 0) + 1 : d < 0 ? Math.min(streak[i - 1], 0) - 1 : 0;
23240
+ }
23241
+ const a = rsi(c, num3(inputs, "rsiLength", 3));
23242
+ const b = rsi(streak, num3(inputs, "streakLength", 2));
23243
+ const r = percentRank(roc(c, 1), num3(inputs, "rankLength", 100));
23244
+ const values = c.map((_, i) => {
23245
+ const x = a[i];
23246
+ const y = b[i];
23247
+ const z = r[i];
23248
+ return Number.isFinite(x) && Number.isFinite(y) && Number.isFinite(z) ? (x + y + z) / 3 : Number.NaN;
23249
+ });
23250
+ return {
23251
+ plots: [{ key: "crsi", title: "CRSI", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23252
+ levels: guideLevels(20, 80, 50)
23253
+ };
23254
+ }
23255
+ };
23256
+ var fisher = {
23257
+ type: "fisher-transform",
23258
+ title: "Fisher Transform",
23259
+ shortTitle: "Fisher",
23260
+ overlay: false,
23261
+ inputs: [lengthInput(9)],
23262
+ compute: (bars, inputs) => {
23263
+ const len = num3(inputs, "length", 9);
23264
+ const hl2 = sourceValues(bars, "HL2");
23265
+ const hi = highest(hl2, len);
23266
+ const lo = lowest(hl2, len);
23267
+ const n = bars.length;
23268
+ const out = new Array(n).fill(Number.NaN);
23269
+ let value = 0;
23270
+ let fish = 0;
23271
+ for (let i = 0; i < n; i++) {
23272
+ const h = hi[i];
23273
+ const l = lo[i];
23274
+ if (!Number.isFinite(h) || !Number.isFinite(l)) continue;
23275
+ const norm = h === l ? 0 : (hl2[i] - l) / (h - l) - 0.5;
23276
+ value = Math.max(-0.999, Math.min(0.999, 0.66 * norm + 0.67 * value));
23277
+ fish = 0.5 * Math.log((1 + value) / (1 - value)) + 0.5 * fish;
23278
+ out[i] = fish;
23279
+ }
23280
+ return {
23281
+ plots: [
23282
+ { key: "fisher", title: "Fisher", values: out, color: SERIES_LINE, width: 2 },
23283
+ { key: "trigger", title: "Trigger", values: shift(out, 1), color: WARNING }
23284
+ ],
23285
+ levels: ZERO_LEVEL
23286
+ };
23287
+ }
23288
+ };
23289
+ var trix = {
23290
+ type: "trix",
23291
+ title: "TRIX",
23292
+ overlay: false,
23293
+ inputs: [lengthInput(18), colorInput()],
23294
+ compute: (bars, inputs) => {
23295
+ const len = num3(inputs, "length", 18);
23296
+ const logClose = map(closes(bars), Math.log);
23297
+ const smooth = ema(ema(ema(logClose, len), len), len);
23298
+ const values = map(change(smooth), (x) => x * 1e4);
23299
+ return {
23300
+ plots: [{ key: "trix", title: "TRIX", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23301
+ levels: ZERO_LEVEL
23302
+ };
23303
+ }
23304
+ };
23305
+ function tsiLine(src, long, short) {
23306
+ const mom = change(src);
23307
+ const numer = ema(ema(mom, long), short);
23308
+ const denom = ema(ema(map(mom, Math.abs), long), short);
23309
+ return zip(numer, denom, (a, b) => b === 0 ? 0 : 100 * a / b);
23310
+ }
23311
+ var tsi = {
23312
+ type: "true-strength-index",
23313
+ title: "True Strength Index",
23314
+ shortTitle: "TSI",
23315
+ overlay: false,
23316
+ inputs: [lengthInput(25, "longLength", "Long length"), lengthInput(13, "shortLength", "Short length"), lengthInput(13, "signalLength", "Signal length")],
23317
+ compute: (bars, inputs) => {
23318
+ const line = tsiLine(closes(bars), num3(inputs, "longLength", 25), num3(inputs, "shortLength", 13));
23319
+ return {
23320
+ plots: [
23321
+ { key: "tsi", title: "TSI", values: line, color: SERIES_LINE, width: 2 },
23322
+ { key: "signal", title: "Signal", values: ema(line, num3(inputs, "signalLength", 13)), color: WARNING }
23323
+ ],
23324
+ levels: ZERO_LEVEL
23325
+ };
23326
+ }
23327
+ };
23328
+ var smiErgodic = {
23329
+ type: "smi-ergodic",
23330
+ title: "SMI Ergodic Oscillator",
23331
+ shortTitle: "SMIE",
23332
+ overlay: false,
23333
+ inputs: [lengthInput(5, "shortLength", "Short length"), lengthInput(20, "longLength", "Long length"), lengthInput(5, "signalLength", "Signal length")],
23334
+ compute: (bars, inputs) => {
23335
+ const line = tsiLine(closes(bars), num3(inputs, "longLength", 20), num3(inputs, "shortLength", 5));
23336
+ const signal = ema(line, num3(inputs, "signalLength", 5));
23337
+ const osc = sub(line, signal);
23338
+ return {
23339
+ plots: [
23340
+ { key: "osc", title: "Oscillator", values: osc, kind: "histogram", color: BULLISH, colors: histColors(osc), base: 0 },
23341
+ { key: "smi", title: "SMI", values: line, color: SERIES_LINE, width: 2 },
23342
+ { key: "signal", title: "Signal", values: signal, color: WARNING }
23343
+ ],
23344
+ levels: ZERO_LEVEL
23345
+ };
23346
+ }
23347
+ };
23348
+ var stc = {
23349
+ type: "schaff-trend-cycle",
23350
+ title: "Schaff Trend Cycle",
23351
+ shortTitle: "Schaff Trend Cycle",
23352
+ overlay: false,
23353
+ inputs: [lengthInput(10, "cycleLength", "Cycle length"), lengthInput(23, "fastLength", "Fast length"), lengthInput(50, "slowLength", "Slow length")],
23354
+ compute: (bars, inputs) => {
23355
+ const len = num3(inputs, "cycleLength", 10);
23356
+ const src = closes(bars);
23357
+ const macdLine = sub(ema(src, num3(inputs, "fastLength", 23)), ema(src, num3(inputs, "slowLength", 50)));
23358
+ const stochOf = (v) => {
23359
+ const lo = lowest(v, len);
23360
+ const hi = highest(v, len);
23361
+ const out = new Array(v.length).fill(Number.NaN);
23362
+ let prevRaw = Number.NaN;
23363
+ let smoothed = Number.NaN;
23364
+ for (let i = 0; i < v.length; i++) {
23365
+ const x = v[i];
23366
+ const l = lo[i];
23367
+ const h = hi[i];
23368
+ if (!Number.isFinite(x) || !Number.isFinite(l) || !Number.isFinite(h)) continue;
23369
+ const raw = h > l ? (x - l) / (h - l) * 100 : Number.isFinite(prevRaw) ? prevRaw : 50;
23370
+ prevRaw = raw;
23371
+ smoothed = Number.isFinite(smoothed) ? smoothed + 0.5 * (raw - smoothed) : raw;
23372
+ out[i] = smoothed;
23373
+ }
23374
+ return out;
23375
+ };
23376
+ const values = stochOf(stochOf(macdLine));
23377
+ return {
23378
+ plots: [{ key: "stc", title: "STC", values, color: SERIES_LINE, width: 2 }],
23379
+ levels: guideLevels(25, 75)
23380
+ };
23381
+ }
23382
+ };
23383
+ var kst = {
23384
+ type: "know-sure-thing",
23385
+ title: "Know Sure Thing",
23386
+ shortTitle: "KST",
23387
+ overlay: false,
23388
+ inputs: [sourceInput(), lengthInput(9, "signalLength", "Signal length")],
23389
+ compute: (bars, inputs) => {
23390
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
23391
+ const line = [
23392
+ sma(roc(src, 10), 10),
23393
+ sma(roc(src, 15), 10),
23394
+ sma(roc(src, 20), 10),
23395
+ sma(roc(src, 30), 15)
23396
+ ].reduce((acc, r, i) => zip(acc, r, (a, b) => a + b * (i + 1)), new Array(src.length).fill(0));
23397
+ return {
23398
+ plots: [
23399
+ { key: "kst", title: "KST", values: line, color: SERIES_LINE, width: 2 },
23400
+ { key: "signal", title: "Signal", values: sma(line, num3(inputs, "signalLength", 9)), color: WARNING }
23401
+ ],
23402
+ levels: ZERO_LEVEL
23403
+ };
23404
+ }
23405
+ };
23406
+ var coppock = {
23407
+ type: "coppock-curve",
23408
+ title: "Coppock Curve",
23409
+ shortTitle: "Coppock",
23410
+ overlay: false,
23411
+ inputs: [
23412
+ lengthInput(10, "wmaLength", "WMA length"),
23413
+ lengthInput(14, "longRoc", "Long RoC length"),
23414
+ lengthInput(11, "shortRoc", "Short RoC length"),
23415
+ sourceInput(),
23416
+ colorInput()
23417
+ ],
23418
+ compute: (bars, inputs) => {
23419
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
23420
+ const values = wma(zip(roc(src, num3(inputs, "longRoc", 14)), roc(src, num3(inputs, "shortRoc", 11)), (a, b) => a + b), num3(inputs, "wmaLength", 10));
23421
+ return {
23422
+ plots: [{ key: "coppock", title: "Coppock", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23423
+ levels: ZERO_LEVEL
23424
+ };
23425
+ }
23426
+ };
23427
+ var dpo = {
23428
+ type: "detrended-price-oscillator",
23429
+ title: "Detrended Price Oscillator",
23430
+ shortTitle: "DPO",
23431
+ overlay: false,
23432
+ inputs: [lengthInput(21), colorInput()],
23433
+ compute: (bars, inputs) => {
23434
+ const len = num3(inputs, "length", 21);
23435
+ const barsback = Math.floor(len / 2) + 1;
23436
+ const values = sub(closes(bars), shift(sma(closes(bars), len), barsback));
23437
+ return {
23438
+ plots: [{ key: "dpo", title: "DPO", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23439
+ levels: ZERO_LEVEL
23440
+ };
23441
+ }
23442
+ };
23443
+ var rocSpec = {
23444
+ type: "rate-of-change",
23445
+ title: "Rate of Change",
23446
+ shortTitle: "ROC",
23447
+ overlay: false,
23448
+ inputs: [lengthInput(9), sourceInput(), colorInput()],
23449
+ compute: (bars, inputs) => ({
23450
+ plots: [
23451
+ {
23452
+ key: "roc",
23453
+ title: "RoC",
23454
+ values: roc(sourceValues(bars, str3(inputs, "source", "Close")), num3(inputs, "length", 9)),
23455
+ color: str3(inputs, "color", SERIES_LINE),
23456
+ width: 2
23457
+ }
23458
+ ],
23459
+ levels: ZERO_LEVEL
23460
+ })
23461
+ };
23462
+ var ultimate = {
23463
+ type: "ultimate-oscillator",
23464
+ title: "Ultimate Oscillator",
23465
+ shortTitle: "UO",
23466
+ overlay: false,
23467
+ inputs: [lengthInput(7, "fastLength", "Fast length"), lengthInput(14, "middleLength", "Middle length"), lengthInput(28, "slowLength", "Slow length"), colorInput()],
23468
+ compute: (bars, inputs) => {
23469
+ const n = bars.length;
23470
+ const bp = new Array(n).fill(Number.NaN);
23471
+ const tr = new Array(n).fill(Number.NaN);
23472
+ for (let i = 0; i < n; i++) {
23473
+ const b = bars[i];
23474
+ const pc = i > 0 ? bars[i - 1].close : b.close;
23475
+ const lo = Math.min(b.low, pc);
23476
+ const hi = Math.max(b.high, pc);
23477
+ bp[i] = b.close - lo;
23478
+ tr[i] = hi - lo;
23479
+ }
23480
+ const avg = (len) => zip(sum(bp, len), sum(tr, len), (a, b) => b === 0 ? Number.NaN : a / b);
23481
+ const a7 = avg(num3(inputs, "fastLength", 7));
23482
+ const a14 = avg(num3(inputs, "middleLength", 14));
23483
+ const a28 = avg(num3(inputs, "slowLength", 28));
23484
+ const values = a7.map((x, i) => {
23485
+ const y = a14[i];
23486
+ const z = a28[i];
23487
+ return Number.isFinite(x) && Number.isFinite(y) && Number.isFinite(z) ? 100 * (4 * x + 2 * y + z) / 7 : Number.NaN;
23488
+ });
23489
+ return {
23490
+ plots: [{ key: "uo", title: "UO", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23491
+ levels: guideLevels(30, 70, 50)
23492
+ };
23493
+ }
23494
+ };
23495
+ var rvgi = {
23496
+ type: "relative-vigor-index",
23497
+ title: "Relative Vigor Index",
23498
+ shortTitle: "RVGI",
23499
+ overlay: false,
23500
+ inputs: [lengthInput(10)],
23501
+ compute: (bars, inputs) => {
23502
+ const len = num3(inputs, "length", 10);
23503
+ const co = swma(sub(closes(bars), opens(bars)));
23504
+ const hl = swma(sub(highs(bars), lows(bars)));
23505
+ const line = zip(sum(co, len), sum(hl, len), (a, b) => b === 0 ? 0 : a / b);
23506
+ return {
23507
+ plots: [
23508
+ { key: "rvgi", title: "RVGI", values: line, color: SERIES_LINE, width: 2 },
23509
+ { key: "signal", title: "Signal", values: swma(line), color: WARNING }
23510
+ ],
23511
+ levels: ZERO_LEVEL
23512
+ };
23513
+ }
23514
+ };
23515
+ var relVolatility = {
23516
+ type: "relative-volatility-index",
23517
+ title: "Relative Volatility Index",
23518
+ shortTitle: "RVI",
23519
+ overlay: false,
23520
+ inputs: [lengthInput(10, "stdevLength", "StdDev length"), lengthInput(14, "smoothLength", "Smoothing length"), sourceInput(), colorInput()],
23521
+ compute: (bars, inputs) => {
23522
+ const smoothLen = num3(inputs, "smoothLength", 14);
23523
+ const src = sourceValues(bars, str3(inputs, "source", "Close"));
23524
+ const dev = stdev(src, num3(inputs, "stdevLength", 10));
23525
+ const d = change(src);
23526
+ const up = rma(zip(dev, d, (s, x) => x > 0 ? s : 0), smoothLen);
23527
+ const dn = rma(zip(dev, d, (s, x) => x <= 0 ? s : 0), smoothLen);
23528
+ const values = zip(up, dn, (u, w) => u + w === 0 ? 50 : 100 * u / (u + w));
23529
+ return {
23530
+ plots: [{ key: "rvi", title: "RVI", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23531
+ levels: guideLevels(20, 80, 50)
23532
+ };
23533
+ }
23534
+ };
23535
+ var bop = {
23536
+ type: "balance-of-power",
23537
+ title: "Balance of Power",
23538
+ shortTitle: "BOP",
23539
+ overlay: false,
23540
+ inputs: [colorInput()],
23541
+ compute: (bars, inputs) => {
23542
+ const values = bars.map((b) => b.high === b.low ? 0 : (b.close - b.open) / (b.high - b.low));
23543
+ return {
23544
+ plots: [{ key: "bop", title: "BOP", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23545
+ levels: ZERO_LEVEL
23546
+ };
23547
+ }
23548
+ };
23549
+ var elderRay = {
23550
+ type: "elder-ray",
23551
+ title: "Elder Ray",
23552
+ overlay: false,
23553
+ inputs: [lengthInput(13)],
23554
+ compute: (bars, inputs) => {
23555
+ const basis = ema(closes(bars), num3(inputs, "length", 13));
23556
+ return {
23557
+ plots: [
23558
+ { key: "bull", title: "Bull power", values: sub(highs(bars), basis), kind: "histogram", color: BULLISH, base: 0 },
23559
+ { key: "bear", title: "Bear power", values: sub(lows(bars), basis), kind: "histogram", color: BEARISH, base: 0 }
23560
+ ],
23561
+ levels: ZERO_LEVEL
23562
+ };
23563
+ }
23564
+ };
23565
+ var ttmSqueeze = {
23566
+ type: "ttm-squeeze",
23567
+ title: "TTM Squeeze",
23568
+ overlay: false,
23569
+ inputs: [
23570
+ lengthInput(20),
23571
+ { key: "bbMult", title: "Bollinger multiplier", type: "float", defval: 2, min: 0.1, max: 50, step: 0.1 },
23572
+ { key: "kcMult", title: "Keltner multiplier", type: "float", defval: 1.5, min: 0.1, max: 50, step: 0.1 }
23573
+ ],
23574
+ compute: (bars, inputs) => {
23575
+ const len = num3(inputs, "length", 20);
23576
+ const c = closes(bars);
23577
+ const basis = sma(c, len);
23578
+ const dev = map(stdev(c, len), (x) => x * num3(inputs, "bbMult", 2));
23579
+ const kcRange = map(rma(trueRange(bars), len), (x) => x * num3(inputs, "kcMult", 1.5));
23580
+ const donchianMid = zip(highest(highs(bars), len), lowest(lows(bars), len), (h, l) => (h + l) / 2);
23581
+ const anchor = zip(donchianMid, basis, (d, b) => (d + b) / 2);
23582
+ const mom = linreg(sub(c, anchor), len);
23583
+ const squeezed = c.map((_, i) => {
23584
+ const d = dev[i];
23585
+ const r = kcRange[i];
23586
+ return Number.isFinite(d) && Number.isFinite(r) ? d < r : false;
23587
+ });
23588
+ const stateColors = squeezed.map((s, i) => Number.isFinite(mom[i]) ? s ? BEARISH : BULLISH : null);
23589
+ return {
23590
+ plots: [
23591
+ { key: "mom", title: "Momentum", values: mom, kind: "histogram", color: BULLISH, colors: histColors(mom), base: 0 },
23592
+ { key: "squeeze", title: "Squeeze", values: mom.map((x) => Number.isFinite(x) ? 0 : Number.NaN), kind: "cross", color: INFO, colors: stateColors }
23593
+ ]
23594
+ };
23595
+ }
23596
+ };
23597
+ var oscillatorSpecs = [
23598
+ rsiSpec,
23599
+ stochastic,
23600
+ stochasticRsi,
23601
+ macd,
23602
+ ppo,
23603
+ awesome,
23604
+ cci,
23605
+ williamsR,
23606
+ cmo,
23607
+ connorsRsi,
23608
+ fisher,
23609
+ trix,
23610
+ tsi,
23611
+ smiErgodic,
23612
+ stc,
23613
+ kst,
23614
+ coppock,
23615
+ dpo,
23616
+ rocSpec,
23617
+ ultimate,
23618
+ rvgi,
23619
+ relVolatility,
23620
+ bop,
23621
+ elderRay,
23622
+ ttmSqueeze
23623
+ ];
23624
+
23625
+ // src/core/native-indicators/classics/trend.ts
23626
+ var aroon = {
23627
+ type: "aroon",
23628
+ title: "Aroon",
23629
+ overlay: false,
23630
+ inputs: [lengthInput(14)],
23631
+ compute: (bars, inputs) => {
23632
+ const len = num3(inputs, "length", 14);
23633
+ const up = map(barsSinceHighest(highs(bars), len + 1), (x) => 100 * (len - x) / len);
23634
+ const down = map(barsSinceLowest(lows(bars), len + 1), (x) => 100 * (len - x) / len);
23635
+ return {
23636
+ plots: [
23637
+ { key: "up", title: "Aroon up", values: up, color: BULLISH, width: 2 },
23638
+ { key: "down", title: "Aroon down", values: down, color: BEARISH, width: 2 }
23639
+ ],
23640
+ levels: [
23641
+ { key: "upper", price: 100, color: NEUTRAL, lineStyle: "dotted" },
23642
+ { key: "lower", price: 0, color: NEUTRAL, lineStyle: "dotted" }
23643
+ ]
23644
+ };
23645
+ }
23646
+ };
23647
+ var adx = {
23648
+ type: "average-directional-index",
23649
+ title: "Average Directional Index",
23650
+ shortTitle: "ADX",
23651
+ overlay: false,
23652
+ inputs: [lengthInput(14, "diLength", "DI length"), lengthInput(14, "adxLength", "ADX smoothing")],
23653
+ compute: (bars, inputs) => {
23654
+ const diLen = num3(inputs, "diLength", 14);
23655
+ const adxLen = num3(inputs, "adxLength", 14);
23656
+ const n = bars.length;
23657
+ const plusDm = new Array(n).fill(Number.NaN);
23658
+ const minusDm = new Array(n).fill(Number.NaN);
23659
+ for (let i = 1; i < n; i++) {
23660
+ const up = bars[i].high - bars[i - 1].high;
23661
+ const dn = bars[i - 1].low - bars[i].low;
23662
+ plusDm[i] = up > dn && up > 0 ? up : 0;
23663
+ minusDm[i] = dn > up && dn > 0 ? dn : 0;
23664
+ }
23665
+ const atrLine = rma(trueRange(bars), diLen);
23666
+ const plusDi = zip(rma(plusDm, diLen), atrLine, (d, a) => a === 0 ? 0 : 100 * d / a);
23667
+ const minusDi = zip(rma(minusDm, diLen), atrLine, (d, a) => a === 0 ? 0 : 100 * d / a);
23668
+ const dx = zip(plusDi, minusDi, (p, m) => p + m === 0 ? 0 : 100 * Math.abs(p - m) / (p + m));
23669
+ return {
23670
+ plots: [
23671
+ { key: "adx", title: "ADX", values: rma(dx, adxLen), color: SERIES_LINE, width: 2 },
23672
+ { key: "plusDi", title: "+DI", values: plusDi, color: BULLISH },
23673
+ { key: "minusDi", title: "-DI", values: minusDi, color: BEARISH }
23674
+ ],
23675
+ levels: [{ key: "threshold", price: 25, color: NEUTRAL, lineStyle: "dotted" }]
23676
+ };
23677
+ }
23678
+ };
23679
+ var vortex = {
23680
+ type: "vortex-indicator",
23681
+ title: "Vortex Indicator",
23682
+ shortTitle: "VI",
23683
+ overlay: false,
23684
+ inputs: [lengthInput(14)],
23685
+ compute: (bars, inputs) => {
23686
+ const len = num3(inputs, "length", 14);
23687
+ const n = bars.length;
23688
+ const vmPlus = new Array(n).fill(Number.NaN);
23689
+ const vmMinus = new Array(n).fill(Number.NaN);
23690
+ for (let i = 1; i < n; i++) {
23691
+ vmPlus[i] = Math.abs(bars[i].high - bars[i - 1].low);
23692
+ vmMinus[i] = Math.abs(bars[i].low - bars[i - 1].high);
23693
+ }
23694
+ const trSum = sum(trueRange(bars), len);
23695
+ return {
23696
+ plots: [
23697
+ { key: "viPlus", title: "VI+", values: zip(sum(vmPlus, len), trSum, (v, t) => t === 0 ? Number.NaN : v / t), color: BULLISH, width: 2 },
23698
+ { key: "viMinus", title: "VI-", values: zip(sum(vmMinus, len), trSum, (v, t) => t === 0 ? Number.NaN : v / t), color: BEARISH, width: 2 }
23699
+ ],
23700
+ levels: [{ key: "one", price: 1, color: NEUTRAL, lineStyle: "dotted" }]
23701
+ };
23702
+ }
23703
+ };
23704
+ var trendSpecs = [aroon, adx, vortex];
23705
+
23706
+ // src/core/native-indicators/classics/volatility.ts
23707
+ var atrSpec = {
23708
+ type: "average-true-range",
23709
+ title: "Average True Range",
23710
+ shortTitle: "ATR",
23711
+ overlay: false,
23712
+ inputs: [lengthInput(14), colorInput()],
23713
+ compute: (bars, inputs) => ({
23714
+ plots: [{ key: "atr", title: "ATR", values: atr(bars, num3(inputs, "length", 14)), color: str3(inputs, "color", SERIES_LINE), width: 2 }]
23715
+ })
23716
+ };
23717
+ var historicalVolatility = {
23718
+ type: "historical-volatility",
23719
+ title: "Historical Volatility",
23720
+ shortTitle: "HV",
23721
+ overlay: false,
23722
+ inputs: [
23723
+ lengthInput(10),
23724
+ { 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)" },
23725
+ colorInput()
23726
+ ],
23727
+ compute: (bars, inputs) => {
23728
+ const logReturns = map(change(map(closes(bars), Math.log)), (x) => x);
23729
+ const values = map(stdev(logReturns, num3(inputs, "length", 10)), (x) => x * Math.sqrt(num3(inputs, "annual", 365)) * 100);
23730
+ return { plots: [{ key: "hv", title: "HV", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }] };
23731
+ }
23732
+ };
23733
+ var chaikinVolatility = {
23734
+ type: "chaikin-volatility",
23735
+ title: "Chaikin Volatility",
23736
+ shortTitle: "CHV",
23737
+ overlay: false,
23738
+ inputs: [lengthInput(10, "emaLength", "EMA length"), lengthInput(10, "rocLength", "RoC length"), colorInput()],
23739
+ compute: (bars, inputs) => {
23740
+ const range = ema(sub(highs(bars), lows(bars)), num3(inputs, "emaLength", 10));
23741
+ return {
23742
+ plots: [{ key: "cv", title: "Chaikin Vol", values: roc(range, num3(inputs, "rocLength", 10)), color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23743
+ levels: [{ key: "zero", price: 0, color: NEUTRAL }]
23744
+ };
23745
+ }
23746
+ };
23747
+ var massIndex = {
23748
+ type: "mass-index",
23749
+ title: "Mass Index",
23750
+ shortTitle: "MI",
23751
+ overlay: false,
23752
+ inputs: [lengthInput(25), colorInput()],
23753
+ compute: (bars, inputs) => {
23754
+ const range = sub(highs(bars), lows(bars));
23755
+ const single = ema(range, 9);
23756
+ const ratio = zip(single, ema(single, 9), (a, b) => b === 0 ? Number.NaN : a / b);
23757
+ return {
23758
+ plots: [{ key: "mi", title: "Mass Index", values: sum(ratio, num3(inputs, "length", 25)), color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23759
+ levels: [{ key: "reversal", price: 27, color: NEUTRAL, lineStyle: "dotted" }]
23760
+ };
23761
+ }
23762
+ };
23763
+ var standardDeviation = {
23764
+ type: "standard-deviation",
23765
+ title: "Standard Deviation",
23766
+ shortTitle: "StdDev",
23767
+ overlay: false,
23768
+ inputs: [lengthInput(20), sourceInput(), colorInput()],
23769
+ compute: (bars, inputs) => ({
23770
+ plots: [
23771
+ {
23772
+ key: "stdev",
23773
+ title: "StdDev",
23774
+ values: stdev(sourceValues(bars, str3(inputs, "source", "Close")), num3(inputs, "length", 20)),
23775
+ color: str3(inputs, "color", SERIES_LINE),
23776
+ width: 2
23777
+ }
23778
+ ]
23779
+ })
23780
+ };
23781
+ var ulcerIndex = {
23782
+ type: "ulcer-index",
23783
+ title: "Ulcer Index",
23784
+ overlay: false,
23785
+ inputs: [lengthInput(14), colorInput()],
23786
+ compute: (bars, inputs) => {
23787
+ const len = num3(inputs, "length", 14);
23788
+ const c = closes(bars);
23789
+ const drawdown = zip(c, highest(c, len), (x, h) => h === 0 ? 0 : 100 * (x - h) / h);
23790
+ const values = map(sma(map(drawdown, (d) => d * d), len), Math.sqrt);
23791
+ return { plots: [{ key: "ui", title: "Ulcer", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }] };
23792
+ }
23793
+ };
23794
+ var choppiness = {
23795
+ type: "choppiness-index",
23796
+ title: "Choppiness Index",
23797
+ shortTitle: "CHOP",
23798
+ overlay: false,
23799
+ inputs: [lengthInput(14), colorInput()],
23800
+ compute: (bars, inputs) => {
23801
+ const len = num3(inputs, "length", 14);
23802
+ const trSum = sum(trueRange(bars), len);
23803
+ const span = zip(highest(highs(bars), len), lowest(lows(bars), len), (h, l) => h - l);
23804
+ const values = zip(trSum, span, (t, s) => s > 0 && t > 0 ? 100 * Math.log10(t / s) / Math.log10(len) : Number.NaN);
23805
+ return {
23806
+ plots: [{ key: "chop", title: "CHOP", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23807
+ levels: [
23808
+ { key: "upper", price: 61.8, color: NEUTRAL },
23809
+ { key: "lower", price: 38.2, color: NEUTRAL }
23810
+ ]
23811
+ };
23812
+ }
23813
+ };
23814
+ var volatilitySpecs = [atrSpec, historicalVolatility, chaikinVolatility, massIndex, standardDeviation, ulcerIndex, choppiness];
23815
+
23816
+ // src/core/native-indicators/classics/volume.ts
23817
+ function moneyFlowVolume(bars) {
23818
+ return bars.map((b) => {
23819
+ if (b.volume == null || !Number.isFinite(b.volume)) return Number.NaN;
23820
+ if (b.high === b.low) return 0;
23821
+ return (2 * b.close - b.high - b.low) / (b.high - b.low) * b.volume;
23822
+ });
23823
+ }
23824
+ var obv = {
23825
+ type: "on-balance-volume",
23826
+ title: "On Balance Volume",
23827
+ shortTitle: "OBV",
23828
+ overlay: false,
23829
+ inputs: [colorInput()],
23830
+ compute: (bars, inputs) => {
23831
+ const vol = volumes(bars);
23832
+ const d = change(closes(bars));
23833
+ const signed2 = zip(vol, d, (v, x) => x > 0 ? v : x < 0 ? -v : 0);
23834
+ return { plots: [{ key: "obv", title: "OBV", values: cumSum(signed2), color: str3(inputs, "color", SERIES_LINE), width: 2 }] };
23835
+ }
23836
+ };
23837
+ var accDist = {
23838
+ type: "accumulation-distribution",
23839
+ title: "Accumulation / Distribution",
23840
+ shortTitle: "A/D",
23841
+ overlay: false,
23842
+ inputs: [colorInput()],
23843
+ compute: (bars, inputs) => ({
23844
+ plots: [{ key: "ad", title: "A/D", values: cumSum(moneyFlowVolume(bars)), color: str3(inputs, "color", SERIES_LINE), width: 2 }]
23845
+ })
23846
+ };
23847
+ var cmf = {
23848
+ type: "chaikin-money-flow",
23849
+ title: "Chaikin Money Flow",
23850
+ shortTitle: "CMF",
23851
+ overlay: false,
23852
+ inputs: [lengthInput(20), colorInput()],
23853
+ compute: (bars, inputs) => {
23854
+ const len = num3(inputs, "length", 20);
23855
+ const values = zip(sum(moneyFlowVolume(bars), len), sum(volumes(bars), len), (m, v) => v === 0 ? 0 : m / v);
23856
+ return {
23857
+ plots: [{ key: "cmf", title: "CMF", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23858
+ levels: [{ key: "zero", price: 0, color: NEUTRAL }]
23859
+ };
23860
+ }
23861
+ };
23862
+ var chaikinOscillator = {
23863
+ type: "chaikin-oscillator",
23864
+ title: "Chaikin Oscillator",
23865
+ shortTitle: "Chaikin Osc",
23866
+ overlay: false,
23867
+ inputs: [lengthInput(3, "fastLength", "Fast length"), lengthInput(10, "slowLength", "Slow length"), colorInput()],
23868
+ compute: (bars, inputs) => {
23869
+ const ad = cumSum(moneyFlowVolume(bars));
23870
+ const values = sub(ema(ad, num3(inputs, "fastLength", 3)), ema(ad, num3(inputs, "slowLength", 10)));
23871
+ return {
23872
+ plots: [{ key: "co", title: "Chaikin Osc", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23873
+ levels: [{ key: "zero", price: 0, color: NEUTRAL }]
23874
+ };
23875
+ }
23876
+ };
23877
+ var eom = {
23878
+ type: "ease-of-movement",
23879
+ title: "Ease of Movement",
23880
+ shortTitle: "EOM",
23881
+ overlay: false,
23882
+ inputs: [lengthInput(14), { key: "divisor", title: "Divisor", type: "int", defval: 1e4, min: 1, max: 1e9, step: 1 }, colorInput()],
23883
+ compute: (bars, inputs) => {
23884
+ const div = num3(inputs, "divisor", 1e4);
23885
+ const hl2 = sourceValues(bars, "HL2");
23886
+ const move = change(hl2);
23887
+ const raw = bars.map((b, i) => {
23888
+ const m = move[i];
23889
+ if (b.volume == null || !Number.isFinite(b.volume) || b.volume === 0 || !Number.isFinite(m)) return Number.NaN;
23890
+ return div * m * (b.high - b.low) / b.volume;
23891
+ });
23892
+ return {
23893
+ plots: [{ key: "eom", title: "EOM", values: sma(raw, num3(inputs, "length", 14)), color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23894
+ levels: [{ key: "zero", price: 0, color: NEUTRAL }]
23895
+ };
23896
+ }
23897
+ };
23898
+ var forceIndex = {
23899
+ type: "force-index",
23900
+ title: "Force Index",
23901
+ shortTitle: "FI",
23902
+ overlay: false,
23903
+ inputs: [lengthInput(13), colorInput()],
23904
+ compute: (bars, inputs) => {
23905
+ const raw = zip(change(closes(bars)), volumes(bars), (d, v) => d * v);
23906
+ return {
23907
+ plots: [{ key: "force", title: "Force", values: ema(raw, num3(inputs, "length", 13)), color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23908
+ levels: [{ key: "zero", price: 0, color: NEUTRAL }]
23909
+ };
23910
+ }
23911
+ };
23912
+ var klinger = {
23913
+ type: "klinger-oscillator",
23914
+ title: "Klinger Oscillator",
23915
+ shortTitle: "KVO",
23916
+ overlay: false,
23917
+ inputs: [lengthInput(34, "fastLength", "Fast length"), lengthInput(55, "slowLength", "Slow length"), lengthInput(13, "signalLength", "Signal length")],
23918
+ compute: (bars, inputs) => {
23919
+ const hlc3 = sourceValues(bars, "HLC3");
23920
+ const trend = change(hlc3);
23921
+ const signedVolume = zip(volumes(bars), trend, (v, t) => t >= 0 ? v : -v);
23922
+ const line = sub(ema(signedVolume, num3(inputs, "fastLength", 34)), ema(signedVolume, num3(inputs, "slowLength", 55)));
23923
+ return {
23924
+ plots: [
23925
+ { key: "kvo", title: "KVO", values: line, color: SERIES_LINE, width: 2 },
23926
+ { key: "signal", title: "Signal", values: ema(line, num3(inputs, "signalLength", 13)), color: WARNING }
23927
+ ],
23928
+ levels: [{ key: "zero", price: 0, color: NEUTRAL }]
23929
+ };
23930
+ }
23931
+ };
23932
+ var mfi = {
23933
+ type: "money-flow-index",
23934
+ title: "Money Flow Index",
23935
+ shortTitle: "MFI",
23936
+ overlay: false,
23937
+ inputs: [lengthInput(14), colorInput()],
23938
+ compute: (bars, inputs) => {
23939
+ const len = num3(inputs, "length", 14);
23940
+ const tp = sourceValues(bars, "HLC3");
23941
+ const rawFlow = zip(tp, volumes(bars), (p, v) => p * v);
23942
+ const d = change(tp);
23943
+ const pos = sum(zip(rawFlow, d, (f, x) => x > 0 ? f : 0), len);
23944
+ const neg = sum(zip(rawFlow, d, (f, x) => x < 0 ? f : 0), len);
23945
+ const values = zip(pos, neg, (p, n) => n === 0 ? 100 : 100 - 100 / (1 + p / n));
23946
+ return {
23947
+ plots: [{ key: "mfi", title: "MFI", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
23948
+ levels: [
23949
+ { key: "upper", price: 80, color: NEUTRAL },
23950
+ { key: "lower", price: 20, color: NEUTRAL }
23951
+ ]
23952
+ };
23953
+ }
23954
+ };
23955
+ function volumeIndex(bars, when) {
23956
+ const out = new Array(bars.length).fill(Number.NaN);
23957
+ let index = 1e3;
23958
+ let started = false;
23959
+ for (let i = 1; i < bars.length; i++) {
23960
+ const v = bars[i].volume;
23961
+ const pv = bars[i - 1].volume;
23962
+ const pc = bars[i - 1].close;
23963
+ if (v == null || pv == null || !Number.isFinite(v) || !Number.isFinite(pv) || pc === 0) {
23964
+ if (started) out[i] = index;
23965
+ continue;
23966
+ }
23967
+ started = true;
23968
+ if (when(v, pv)) index += (bars[i].close - pc) / pc * index;
23969
+ out[i] = index;
23970
+ }
23971
+ return out;
23972
+ }
23973
+ var nvi = {
23974
+ type: "negative-volume-index",
23975
+ title: "Negative Volume Index",
23976
+ shortTitle: "Negative Volume Index",
23977
+ overlay: false,
23978
+ inputs: [lengthInput(255, "signalLength", "Signal length"), colorInput()],
23979
+ compute: (bars, inputs) => {
23980
+ const line = volumeIndex(bars, (v, pv) => v < pv);
23981
+ return {
23982
+ plots: [
23983
+ { key: "nvi", title: "NVI", values: line, color: str3(inputs, "color", SERIES_LINE), width: 2 },
23984
+ { key: "signal", title: "Signal", values: ema(line, num3(inputs, "signalLength", 255)), color: WARNING }
23985
+ ]
23986
+ };
23987
+ }
23988
+ };
23989
+ var pvi = {
23990
+ type: "positive-volume-index",
23991
+ title: "Positive Volume Index",
23992
+ shortTitle: "Positive Volume Index",
23993
+ overlay: false,
23994
+ inputs: [lengthInput(255, "signalLength", "Signal length"), colorInput()],
23995
+ compute: (bars, inputs) => {
23996
+ const line = volumeIndex(bars, (v, pv) => v > pv);
23997
+ return {
23998
+ plots: [
23999
+ { key: "pvi", title: "PVI", values: line, color: str3(inputs, "color", SERIES_LINE), width: 2 },
24000
+ { key: "signal", title: "Signal", values: ema(line, num3(inputs, "signalLength", 255)), color: WARNING }
24001
+ ]
24002
+ };
24003
+ }
24004
+ };
24005
+ var pvt = {
24006
+ type: "price-volume-trend",
24007
+ title: "Price Volume Trend",
24008
+ shortTitle: "PVT",
24009
+ overlay: false,
24010
+ inputs: [colorInput()],
24011
+ compute: (bars, inputs) => {
24012
+ const c = closes(bars);
24013
+ const raw = bars.map((b, i) => {
24014
+ if (i === 0 || b.volume == null || !Number.isFinite(b.volume)) return Number.NaN;
24015
+ const pc = c[i - 1];
24016
+ return pc === 0 ? 0 : (c[i] - pc) / pc * b.volume;
24017
+ });
24018
+ return { plots: [{ key: "pvt", title: "PVT", values: cumSum(raw), color: str3(inputs, "color", SERIES_LINE), width: 2 }] };
24019
+ }
24020
+ };
24021
+ var volumeOscillator = {
24022
+ type: "volume-oscillator",
24023
+ title: "Volume Oscillator",
24024
+ shortTitle: "Vol Osc",
24025
+ overlay: false,
24026
+ inputs: [lengthInput(5, "fastLength", "Fast length"), lengthInput(10, "slowLength", "Slow length"), colorInput()],
24027
+ compute: (bars, inputs) => {
24028
+ const vol = volumes(bars);
24029
+ const fast = ema(vol, num3(inputs, "fastLength", 5));
24030
+ const slow = ema(vol, num3(inputs, "slowLength", 10));
24031
+ const values = zip(fast, slow, (f, s) => s === 0 ? Number.NaN : 100 * (f - s) / s);
24032
+ return {
24033
+ plots: [{ key: "vo", title: "Volume Osc", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
24034
+ levels: [{ key: "zero", price: 0, color: NEUTRAL }]
24035
+ };
24036
+ }
24037
+ };
24038
+ var pvo = {
24039
+ type: "pvo",
24040
+ title: "Percentage Volume Oscillator",
24041
+ shortTitle: "PVO",
24042
+ overlay: false,
24043
+ inputs: [lengthInput(12, "fastLength", "Fast length"), lengthInput(26, "slowLength", "Slow length"), lengthInput(9, "signalLength", "Signal length")],
24044
+ compute: (bars, inputs) => {
24045
+ const vol = volumes(bars);
24046
+ const fast = ema(vol, num3(inputs, "fastLength", 12));
24047
+ const slow = ema(vol, num3(inputs, "slowLength", 26));
24048
+ const line = zip(fast, slow, (f, s) => s === 0 ? Number.NaN : 100 * (f - s) / s);
24049
+ const signal = ema(line, num3(inputs, "signalLength", 9));
24050
+ const hist = sub(line, signal);
24051
+ return {
24052
+ plots: [
24053
+ { key: "hist", title: "Histogram", values: hist, kind: "histogram", color: BULLISH, colors: hist.map((x) => Number.isFinite(x) ? x >= 0 ? BULLISH : BEARISH : null), base: 0 },
24054
+ { key: "pvo", title: "PVO", values: line, color: SERIES_LINE, width: 2 },
24055
+ { key: "signal", title: "Signal", values: signal, color: WARNING }
24056
+ ],
24057
+ levels: [{ key: "zero", price: 0, color: NEUTRAL }]
24058
+ };
24059
+ }
24060
+ };
24061
+ var vfi = {
24062
+ type: "volume-flow-indicator",
24063
+ title: "Volume Flow Indicator",
24064
+ shortTitle: "Volume Flow Indicator",
24065
+ overlay: false,
24066
+ inputs: [
24067
+ lengthInput(130),
24068
+ { key: "coef", title: "Cutoff coefficient", type: "float", defval: 0.2, min: 0.01, max: 10, step: 0.01 },
24069
+ { key: "volCoef", title: "Volume cap", type: "float", defval: 2.5, min: 0.1, max: 50, step: 0.1 },
24070
+ lengthInput(3, "smoothLength", "Smoothing length")
24071
+ ],
24072
+ compute: (bars, inputs) => {
24073
+ const len = num3(inputs, "length", 130);
24074
+ const coef = num3(inputs, "coef", 0.2);
24075
+ const volCoef = num3(inputs, "volCoef", 2.5);
24076
+ const typical = map(sourceValues(bars, "HLC3"), Math.log);
24077
+ const inter = change(typical);
24078
+ const vInter = stdev(inter, 30);
24079
+ const vol = volumes(bars);
24080
+ const vAve = sma(vol, len);
24081
+ const mf = change(sourceValues(bars, "HLC3"));
24082
+ const raw = bars.map((b, i) => {
24083
+ const cutoff = Number.isFinite(vInter[i]) ? coef * vInter[i] * b.close : Number.NaN;
24084
+ const v = vol[i];
24085
+ const ave = i > 0 ? vAve[i - 1] : Number.NaN;
24086
+ const m = mf[i];
24087
+ if (!Number.isFinite(cutoff) || !Number.isFinite(v) || !Number.isFinite(ave) || !Number.isFinite(m)) return Number.NaN;
24088
+ const capped = Math.min(v, ave * volCoef);
24089
+ return m > cutoff ? capped : m < -cutoff ? -capped : 0;
24090
+ });
24091
+ const values = bars.map((_, i) => {
24092
+ const ave = i > 0 ? vAve[i - 1] : Number.NaN;
24093
+ return Number.isFinite(ave) && ave !== 0 ? ave : Number.NaN;
24094
+ });
24095
+ const flowSum = sum(raw, len);
24096
+ const line = zip(flowSum, values, (f, a) => f / a);
24097
+ return {
24098
+ plots: [{ key: "vfi", title: "VFI", values: sma(line, num3(inputs, "smoothLength", 3)), color: SERIES_LINE, width: 2 }],
24099
+ levels: [{ key: "zero", price: 0, color: NEUTRAL }]
24100
+ };
24101
+ }
24102
+ };
24103
+ var intradayIntensity = {
24104
+ type: "intraday-intensity",
24105
+ title: "Intraday Intensity",
24106
+ shortTitle: "Intraday Intensity",
24107
+ overlay: false,
24108
+ inputs: [lengthInput(21), colorInput()],
24109
+ compute: (bars, inputs) => {
24110
+ const len = num3(inputs, "length", 21);
24111
+ const values = zip(sum(moneyFlowVolume(bars), len), sum(volumes(bars), len), (m, v) => v === 0 ? 0 : 100 * m / v);
24112
+ return {
24113
+ plots: [{ key: "ii", title: "II%", values, color: str3(inputs, "color", SERIES_LINE), width: 2 }],
24114
+ levels: [{ key: "zero", price: 0, color: NEUTRAL }]
24115
+ };
24116
+ }
24117
+ };
24118
+ var volumeSpecs = [
24119
+ obv,
24120
+ accDist,
24121
+ cmf,
24122
+ chaikinOscillator,
24123
+ eom,
24124
+ forceIndex,
24125
+ klinger,
24126
+ mfi,
24127
+ nvi,
24128
+ pvi,
24129
+ pvt,
24130
+ volumeOscillator,
24131
+ pvo,
24132
+ vfi,
24133
+ intradayIntensity
24134
+ ];
24135
+
24136
+ // src/core/native-indicators/classics/overlays.ts
24137
+ var DAY_MS2 = 864e5;
24138
+ function periodKey(anchor, time) {
24139
+ if (anchor === "Week") return Math.floor((Math.floor(time / DAY_MS2) + 3) / 7);
24140
+ if (anchor === "Month") {
24141
+ const d = new Date(time);
24142
+ return d.getUTCFullYear() * 12 + d.getUTCMonth();
24143
+ }
24144
+ return Math.floor(time / DAY_MS2);
24145
+ }
24146
+ var parabolicSar = {
24147
+ type: "parabolic-sar",
24148
+ title: "Parabolic SAR",
24149
+ shortTitle: "SAR",
24150
+ overlay: true,
24151
+ inputs: [
24152
+ { key: "start", title: "Start", type: "float", defval: 0.02, min: 1e-3, max: 1, step: 1e-3 },
24153
+ { key: "increment", title: "Increment", type: "float", defval: 0.02, min: 1e-3, max: 1, step: 1e-3 },
24154
+ { key: "maximum", title: "Max value", type: "float", defval: 0.2, min: 0.01, max: 1, step: 0.01 }
24155
+ ],
24156
+ compute: (bars, inputs) => {
24157
+ const start = num3(inputs, "start", 0.02);
24158
+ const inc = num3(inputs, "increment", 0.02);
24159
+ const max = num3(inputs, "maximum", 0.2);
24160
+ const n = bars.length;
24161
+ const values = new Array(n).fill(Number.NaN);
24162
+ const colors = new Array(n).fill(null);
24163
+ if (n >= 2) {
24164
+ let long = bars[1].close >= bars[0].close;
24165
+ let sar = long ? bars[0].low : bars[0].high;
24166
+ let ep = long ? bars[0].high : bars[0].low;
24167
+ let af = start;
24168
+ for (let i = 1; i < n; i++) {
24169
+ const b = bars[i];
24170
+ sar += af * (ep - sar);
24171
+ if (long) sar = Math.min(sar, bars[i - 1].low, i >= 2 ? bars[i - 2].low : bars[i - 1].low);
24172
+ else sar = Math.max(sar, bars[i - 1].high, i >= 2 ? bars[i - 2].high : bars[i - 1].high);
24173
+ const reversed = long ? b.low < sar : b.high > sar;
24174
+ if (reversed) {
24175
+ long = !long;
24176
+ sar = ep;
24177
+ ep = long ? b.high : b.low;
24178
+ af = start;
24179
+ } else if (long && b.high > ep) {
24180
+ ep = b.high;
24181
+ af = Math.min(max, af + inc);
24182
+ } else if (!long && b.low < ep) {
24183
+ ep = b.low;
24184
+ af = Math.min(max, af + inc);
24185
+ }
24186
+ values[i] = sar;
24187
+ colors[i] = long ? BULLISH : BEARISH;
24188
+ }
24189
+ }
24190
+ return { plots: [{ key: "sar", title: "PSAR", values, kind: "circles", color: SERIES_LINE, colors }] };
24191
+ }
24192
+ };
24193
+ var vwap = {
24194
+ type: "vwap",
24195
+ title: "Volume Weighted Average Price",
24196
+ shortTitle: "VWAP",
24197
+ overlay: true,
24198
+ inputs: [
24199
+ { key: "anchor", title: "Anchor", type: "string", defval: "Day", options: ["Day", "Week", "Month"], tooltip: "Where the accumulation resets (UTC periods)" },
24200
+ { key: "source", title: "Source", type: "string", defval: "HLC3", options: ["HLC3", "OHLC4", "Close"] },
24201
+ colorInput(INFO)
24202
+ ],
24203
+ compute: (bars, inputs) => {
24204
+ const anchor = str3(inputs, "anchor", "Day");
24205
+ const source = str3(inputs, "source", "HLC3");
24206
+ const values = new Array(bars.length).fill(Number.NaN);
24207
+ let period = Number.NaN;
24208
+ let cumPV = 0;
24209
+ let cumV = 0;
24210
+ for (let i = 0; i < bars.length; i++) {
24211
+ const b = bars[i];
24212
+ const key = periodKey(anchor, b.time);
24213
+ if (key !== period) {
24214
+ period = key;
24215
+ cumPV = 0;
24216
+ cumV = 0;
24217
+ }
24218
+ const v = b.volume;
24219
+ if (v != null && Number.isFinite(v) && v > 0) {
24220
+ const tp = source === "Close" ? b.close : source === "OHLC4" ? (b.open + b.high + b.low + b.close) / 4 : (b.high + b.low + b.close) / 3;
24221
+ cumPV += tp * v;
24222
+ cumV += v;
24223
+ }
24224
+ if (cumV > 0) values[i] = cumPV / cumV;
24225
+ }
24226
+ return { plots: [{ key: "vwap", title: "VWAP", values, color: str3(inputs, "color", INFO), width: 2 }] };
24227
+ }
24228
+ };
24229
+ var pivotPoints = {
24230
+ type: "pivot-points",
24231
+ title: "Pivot Points",
24232
+ shortTitle: "Pivots",
24233
+ overlay: true,
24234
+ inputs: [{ key: "anchor", title: "Period", type: "string", defval: "Day", options: ["Day", "Week", "Month"] }],
24235
+ compute: (bars, inputs) => {
24236
+ const anchor = str3(inputs, "anchor", "Day");
24237
+ const aggs = /* @__PURE__ */ new Map();
24238
+ for (const b of bars) {
24239
+ const key = periodKey(anchor, b.time);
24240
+ const a = aggs.get(key);
24241
+ if (!a) aggs.set(key, { high: b.high, low: b.low, close: b.close });
24242
+ else {
24243
+ a.high = Math.max(a.high, b.high);
24244
+ a.low = Math.min(a.low, b.low);
24245
+ a.close = b.close;
24246
+ }
24247
+ }
24248
+ const n = bars.length;
24249
+ const mk = () => new Array(n).fill(Number.NaN);
24250
+ const levels = { p: mk(), r1: mk(), s1: mk(), r2: mk(), s2: mk(), r3: mk(), s3: mk() };
24251
+ for (let i = 0; i < n; i++) {
24252
+ const prev2 = aggs.get(periodKey(anchor, bars[i].time) - 1);
24253
+ if (!prev2) continue;
24254
+ const p = (prev2.high + prev2.low + prev2.close) / 3;
24255
+ levels.p[i] = p;
24256
+ levels.r1[i] = 2 * p - prev2.low;
24257
+ levels.s1[i] = 2 * p - prev2.high;
24258
+ levels.r2[i] = p + (prev2.high - prev2.low);
24259
+ levels.s2[i] = p - (prev2.high - prev2.low);
24260
+ levels.r3[i] = prev2.high + 2 * (p - prev2.low);
24261
+ levels.s3[i] = prev2.low - 2 * (prev2.high - p);
24262
+ }
24263
+ const plot = (key, title, color) => ({ key, title, kind: "step", values: levels[key], color });
24264
+ return {
24265
+ plots: [
24266
+ plot("p", "P", WARNING),
24267
+ plot("r1", "R1", BEARISH),
24268
+ plot("s1", "S1", BULLISH),
24269
+ plot("r2", "R2", BEARISH),
24270
+ plot("s2", "S2", BULLISH),
24271
+ plot("r3", "R3", BEARISH),
24272
+ plot("s3", "S3", BULLISH)
24273
+ ]
24274
+ };
24275
+ }
24276
+ };
24277
+ var fiftyTwoWeek = {
24278
+ type: "52-week-high-low",
24279
+ title: "52 Week High/Low",
24280
+ shortTitle: "52W H/L",
24281
+ overlay: true,
24282
+ inputs: [{ key: "weeks", title: "Weeks", type: "int", defval: 52, min: 1, max: 520, step: 1 }],
24283
+ compute: (bars, inputs) => {
24284
+ const span = num3(inputs, "weeks", 52) * 7 * DAY_MS2;
24285
+ const n = bars.length;
24286
+ const hi = new Array(n).fill(Number.NaN);
24287
+ const lo = new Array(n).fill(Number.NaN);
24288
+ const maxIdx = [];
24289
+ const minIdx = [];
24290
+ let from = 0;
24291
+ for (let i = 0; i < n; i++) {
24292
+ const b = bars[i];
24293
+ while (maxIdx.length > 0 && bars[maxIdx[maxIdx.length - 1]].high <= b.high) maxIdx.pop();
24294
+ maxIdx.push(i);
24295
+ while (minIdx.length > 0 && bars[minIdx[minIdx.length - 1]].low >= b.low) minIdx.pop();
24296
+ minIdx.push(i);
24297
+ while (bars[from].time < b.time - span) from += 1;
24298
+ while (maxIdx[0] < from) maxIdx.shift();
24299
+ while (minIdx[0] < from) minIdx.shift();
24300
+ hi[i] = bars[maxIdx[0]].high;
24301
+ lo[i] = bars[minIdx[0]].low;
24302
+ }
24303
+ return {
24304
+ plots: [
24305
+ { key: "high", title: "52W high", values: hi, kind: "step", color: BULLISH },
24306
+ { key: "low", title: "52W low", values: lo, kind: "step", color: BEARISH }
24307
+ ]
24308
+ };
24309
+ }
24310
+ };
24311
+ var zigzag = {
24312
+ type: "zigzag",
24313
+ title: "ZigZag",
24314
+ overlay: true,
24315
+ inputs: [
24316
+ { key: "deviation", title: "Deviation %", type: "float", defval: 5, min: 0.01, max: 100, step: 0.1 },
24317
+ { key: "depth", title: "Depth", type: "int", defval: 10, min: 1, max: 500, step: 1 },
24318
+ colorInput()
24319
+ ],
24320
+ compute: (bars, inputs) => {
24321
+ const dev = num3(inputs, "deviation", 5) / 100;
24322
+ const depth = num3(inputs, "depth", 10);
24323
+ const pivots = [];
24324
+ if (bars.length > 1) {
24325
+ let up = bars[1].close >= bars[0].close;
24326
+ let ext = up ? { i: 0, price: bars[0].high } : { i: 0, price: bars[0].low };
24327
+ for (let i = 1; i < bars.length; i++) {
24328
+ const b = bars[i];
24329
+ if (up) {
24330
+ if (b.high >= ext.price) ext = { i, price: b.high };
24331
+ else if (b.low <= ext.price * (1 - dev) && i - (pivots[pivots.length - 1]?.i ?? -depth) >= depth) {
24332
+ pivots.push(ext);
24333
+ up = false;
24334
+ ext = { i, price: b.low };
24335
+ }
24336
+ } else if (b.low <= ext.price) {
24337
+ ext = { i, price: b.low };
24338
+ } else if (b.high >= ext.price * (1 + dev) && i - (pivots[pivots.length - 1]?.i ?? -depth) >= depth) {
24339
+ pivots.push(ext);
24340
+ up = true;
24341
+ ext = { i, price: b.high };
24342
+ }
24343
+ }
24344
+ pivots.push(ext);
24345
+ }
24346
+ return {
24347
+ plots: [],
24348
+ polylines: [
24349
+ {
24350
+ key: "zigzag",
24351
+ points: pivots.map((p) => ({ xloc: "bar_time", x: bars[p.i].time, price: p.price })),
24352
+ curved: false,
24353
+ closed: false,
24354
+ lineColor: str3(inputs, "color", SERIES_LINE),
24355
+ lineWidth: 2,
24356
+ lineStyle: "solid",
24357
+ arrowLeft: false,
24358
+ arrowRight: false
24359
+ }
24360
+ ]
24361
+ };
24362
+ }
24363
+ };
24364
+ var williamsFractal = {
24365
+ type: "williams-fractal",
24366
+ title: "Williams Fractal",
24367
+ shortTitle: "Fractals",
24368
+ overlay: true,
24369
+ inputs: [{ key: "periods", title: "Periods", type: "int", defval: 2, min: 1, max: 50, step: 1 }],
24370
+ compute: (bars, inputs) => {
24371
+ const p = num3(inputs, "periods", 2);
24372
+ const markers = [];
24373
+ const isExtreme = (i, pick, better) => {
24374
+ const v = pick(bars[i]);
24375
+ for (let k = i - p; k <= i + p; k++) {
24376
+ if (k === i) continue;
24377
+ if (!better(v, pick(bars[k]))) return false;
24378
+ }
24379
+ return true;
24380
+ };
24381
+ for (let i = p; i < bars.length - p; i++) {
24382
+ if (isExtreme(i, (b) => b.high, (a, b) => a >= b)) {
24383
+ markers.push({ time: bars[i].time, position: "aboveBar", shape: "triangleup", color: BULLISH });
24384
+ }
24385
+ if (isExtreme(i, (b) => b.low, (a, b) => a <= b)) {
24386
+ markers.push({ time: bars[i].time, position: "belowBar", shape: "triangledown", color: BEARISH });
24387
+ }
24388
+ }
24389
+ return { plots: [], markers };
24390
+ }
24391
+ };
24392
+ var overlaySpecs = [parabolicSar, vwap, pivotPoints, fiftyTwoWeek, zigzag, williamsFractal];
24393
+
24394
+ // src/core/native-indicators/classics/index.ts
24395
+ var classicSpecs = [
24396
+ ...averageSpecs,
24397
+ ...bandSpecs,
24398
+ ...oscillatorSpecs,
24399
+ ...trendSpecs,
24400
+ ...volatilitySpecs,
24401
+ ...volumeSpecs,
24402
+ ...overlaySpecs
24403
+ ];
24404
+ function registerClassicIndicators() {
24405
+ for (const spec of classicSpecs) registerNativeIndicator(classicDescriptor(spec));
24406
+ }
24407
+
24408
+ // src/Vela.ts
24409
+ var Vela = class {
24410
+ constructor(container, options = {}, deps = {}) {
24411
+ registerBuiltinChartTypes();
24412
+ registerVolume();
24413
+ registerVpvr();
24414
+ registerClassicIndicators();
24415
+ const element = resolveElement(container);
24416
+ const theme = resolveTheme(options.theme);
22035
24417
  const display = {
22036
24418
  currentPriceLine: options.currentPriceLine ?? true,
22037
24419
  logScale: options.logScale ?? false,
22038
24420
  nativeBackend: options.nativeBackend ?? "auto",
22039
- animZoom,
22040
- animPan,
24421
+ ...resolveAnimations(options.animations),
22041
24422
  glow: options.glow ?? 0,
22042
24423
  upColor: options.upColor ?? BULLISH,
22043
24424
  downColor: options.downColor ?? BEARISH,
@@ -22095,25 +24476,28 @@ var Vela = class {
22095
24476
  }
22096
24477
  /**
22097
24478
  * 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.
24479
+ * e.g. `'vpvr'` or `'sma'`. It becomes a first-class indicator (legend row, settings, hide,
24480
+ * remove, events). Layer-backed types (`'volume'`, `'vpvr'`) are single-instance — a second
24481
+ * call returns the existing handle; the classic studies allow several instances, so each call
24482
+ * adds one more. Returns a fail-soft handle for an unregistered type. Native renderer only.
22101
24483
  */
22102
24484
  addNativeIndicator(type, options) {
22103
24485
  return this.orchestrator.addNativeIndicator(type, options);
22104
24486
  }
22105
24487
  /**
22106
24488
  * 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).
24489
+ * `type`, `title`, whether it `supported`s the current symbol, whether it's already `present`,
24490
+ * and whether it allows several instances (`multiInstance`; otherwise a second
24491
+ * `addNativeIndicator` is a no-op). Lets a host "add indicator" UI list them, gate unsupported
24492
+ * ones, and de-duplicate the single-instance ones. Async because support may probe the
24493
+ * provider (a type may need data the symbol lacks).
22111
24494
  */
22112
24495
  availableNativeIndicators() {
22113
24496
  return this.orchestrator.availableNativeIndicators();
22114
24497
  }
22115
24498
  /**
22116
- * The native-indicator types PRESENT on the chart right now — the synchronous slice of
24499
+ * The native-indicator types PRESENT on the chart right now, one entry per instance (a
24500
+ * multi-instance type repeats) — the synchronous slice of
22117
24501
  * {@link availableNativeIndicators} (only support probing is async; presence never is).
22118
24502
  * Persistence snapshots read this: an unload-time flush must see an add/remove that
22119
24503
  * happened microseconds ago, which an async catalog mirror cannot guarantee.
@@ -22349,4 +24733,4 @@ function resolveElement(container) {
22349
24733
  return element;
22350
24734
  }
22351
24735
 
22352
- export { BUILTIN_PRICE_STYLES, BarStore, CachingDataFeed, DARK_THEME, DataControl, DrawingStore, DrawingToolbar, DrawingsControl, LEGEND_AT_TOP_ATTR, LIGHT_THEME, MultiProviderFeed, NativeRenderer, RendererControl, TIMEZONES, TypedEventBus, Vela, applyAttributionMarkTheme, buildToolbar, createAttributionMark, createCustomMark, defaultMemberOf, defaultToolbar, groupKeyOf, groupMembers, isGroupRow, normalizeTimezone, parseSymbol, priceStyleIds, registerBuiltinChartTypes, resolveTheme, sharedBarStore, timeframeToMs, tzButtonLabel, tzMenuLabel, tzOffset };
24736
+ export { BUILTIN_PRICE_STYLES, BarStore, CachingDataFeed, DARK_THEME, DataControl, DrawingStore, DrawingToolbar, DrawingsControl, LEGEND_AT_TOP_ATTR, LIGHT_THEME, MultiProviderFeed, NativeRenderer, RendererControl, TIMEZONES, TypedEventBus, Vela, applyAttributionMarkTheme, buildToolbar, createAttributionMark, createCustomMark, defaultMemberOf, defaultToolbar, groupKeyOf, groupMembers, isGroupRow, normalizeSession, normalizeTimezone, parseSymbol, priceStyleIds, registerBuiltinChartTypes, resolveTheme, sharedBarStore, timeframeToMs, tzButtonLabel, tzMenuLabel, tzOffset };