acttrader-charts 1.3.0-beta.2 → 1.3.0-beta.21

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.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as ChartConfig, O as OHLCVBar, I as IIndicator, a as IndicatorParams, b as ChartEventMap, T as Timeframe, S as SeriesType, c as ChartState, D as DrawingToolType, d as IDrawing, A as AnyDrawingStyle, e as IWebSocketAdapter, f as TradeLevelType, L as LayoutSyncState, g as ChartTheme, h as AnyLevel, i as DrawingPoint, j as ScaleManager, V as Viewport, P as PriceRange$1, k as DrawingHandle, l as SerializedDrawing, m as Tick, n as StreamStatus } from './MACD-D1i-sEFR.js';
2
- export { o as ActiveIndicatorState, B as BollingerBands, p as BottomBarColors, q as BottomBarLabels, r as BottomBarUiConfig, s as CanvasColorSettings, t as ChartLabels, u as ChartMiscLabels, v as ChartThemeUi, w as CrosshairPosition, x as CrosshairUiConfig, y as DARK_THEME, z as DEFAULT_BOTTOM_BAR_CONFIG, E as DEFAULT_CROSSHAIR_CONFIG, F as DEFAULT_DRAWING_TOOLBAR_CONFIG, G as DEFAULT_INDICATOR_OVERLAY_CONFIG, H as DEFAULT_LABELS, J as DEFAULT_LAYOUT_SYNC, K as DEFAULT_PRICE_AXIS_CONFIG, M as DEFAULT_TIME_AXIS_CONFIG, N as DEFAULT_TOP_BAR_CONFIG, Q as DEFAULT_TRADE_BUTTON_CONFIG, R as DEFAULT_UI_CONFIG, U as DataLoaderParams, W as DeepPartial, X as DeepPartialChartTheme, Y as DialogLabels, Z as DrawingToolbarColors, _ as DrawingToolbarLabels, $ as DrawingToolbarUiConfig, a0 as Duration, a1 as EMA, a2 as IndicatorOverlayColors, a3 as IndicatorOverlayUiConfig, a4 as IndicatorResult, a5 as IndicatorStateEntry, a6 as LAYOUT_PRESETS, a7 as LIGHT_THEME, a8 as LayoutPreset, a9 as MACD, aa as OhlcLabels, ab as OrderSubmit, ac as PRESETS_BY_COUNT, ad as PRESET_COUNTS, ae as PRESET_LABEL, af as Padding, ag as PendingOrderLevel, ah as PositionLevel, ai as PositionRenderStyle, aj as PriceAxisUiConfig, ak as RSI, al as SMA, am as Theme, an as ThemeOverrides, ao as TimeAxisUiConfig, ap as TopBarColors, aq as TopBarLabels, ar as TopBarUiConfig, as as TradeButtonUiConfig, at as TradeDisplayFilter, au as TradeLabels, av as TradeLevel, aw as TradeLevelColors, ax as TradePanelColors, ay as UiConfig, az as deepMergeTheme, aA as getAllPresets, aB as getCustomPresets, aC as getPreset, aD as registerCustomPreset, aE as resolveCssVarsInTheme, aF as resolveLabels, aG as resolveUiConfig, aH as unregisterCustomPreset } from './MACD-D1i-sEFR.js';
1
+ import { C as ChartConfig, O as OHLCVBar, I as IIndicator, a as IndicatorParams, b as ChartEventMap, T as Timeframe, D as Duration, S as SeriesType, c as ChartState, d as InstrumentSpec, R as ResolvedInstrument, A as AccountSpec, e as DrawingToolType, f as IDrawing, g as AnyDrawingStyle, h as IWebSocketAdapter, i as TradeLevelType, L as LayoutSyncState, j as ChartTheme, P as PositionLevel, k as AnyLevel, l as DrawingPoint, m as ScaleManager, V as Viewport, n as PriceRange$1, o as DrawingHandle, p as DrawingRenderContext, q as SerializedDrawing, r as Tick, s as StreamStatus } from './MACD-B6SoR6iH.js';
2
+ export { t as ActiveIndicatorState, B as BollingerBands, u as BottomBarColors, v as BottomBarLabels, w as BottomBarUiConfig, x as CanvasColorSettings, y as ChartLabels, z as ChartMiscLabels, E as ChartThemeUi, F as CrosshairPosition, G as CrosshairUiConfig, H as DARK_THEME, J as DEFAULT_BOTTOM_BAR_CONFIG, K as DEFAULT_CROSSHAIR_CONFIG, M as DEFAULT_DRAWING_TOOLBAR_CONFIG, N as DEFAULT_INDICATOR_OVERLAY_CONFIG, Q as DEFAULT_LABELS, U as DEFAULT_LAYOUT_SYNC, W as DEFAULT_PRICE_AXIS_CONFIG, X as DEFAULT_TIME_AXIS_CONFIG, Y as DEFAULT_TOP_BAR_CONFIG, Z as DEFAULT_TRADE_BUTTON_CONFIG, _ as DEFAULT_UI_CONFIG, $ as DataLoaderParams, a0 as DeepPartial, a1 as DeepPartialChartTheme, a2 as DialogLabels, a3 as DrawingToolbarColors, a4 as DrawingToolbarLabels, a5 as DrawingToolbarUiConfig, a6 as EMA, a7 as IndicatorOverlayColors, a8 as IndicatorOverlayUiConfig, a9 as IndicatorResult, aa as IndicatorStateEntry, ab as LAYOUT_PRESETS, ac as LIGHT_THEME, ad as LayoutPreset, ae as MACD, af as OhlcLabels, ag as OrderSubmit, ah as PRESETS_BY_COUNT, ai as PRESET_COUNTS, aj as PRESET_LABEL, ak as Padding, al as PendingOrderLevel, am as PnlPillConfig, an as PnlPillLabels, ao as PositionRenderStyle, ap as PriceAxisUiConfig, aq as RSI, ar as SMA, as as Theme, at as ThemeOverrides, au as TimeAxisUiConfig, av as TopBarColors, aw as TopBarLabels, ax as TopBarUiConfig, ay as TradeButtonUiConfig, az as TradeDisplayFilter, aA as TradeLabels, aB as TradeLevel, aC as TradeLevelColors, aD as TradePanelColors, aE as UiConfig, aF as deepMergeTheme, aG as formatDuration, aH as formatVolume, aI as getAllPresets, aJ as getCustomPresets, aK as getPreset, aL as inferPipSize, aM as registerCustomPreset, aN as resolveCssVarsInTheme, aO as resolveInstrument, aP as resolveLabels, aQ as resolveUiConfig, aR as toMoney, aS as toPips, aT as unregisterCustomPreset } from './MACD-B6SoR6iH.js';
3
3
 
4
4
  declare class ChartEngine {
5
5
  private wrapper;
@@ -49,6 +49,9 @@ declare class ChartEngine {
49
49
  private chartSettingsDialog;
50
50
  private tradeButton;
51
51
  private tradePopover;
52
+ /** Segmented click bar (`features.tradeActionBar`); replaces the popover as the trade button's target. */
53
+ private tradeActionBar;
54
+ private _onAddAlert;
52
55
  private drawingStylePopover;
53
56
  private draftOrderLabel;
54
57
  private _minLots;
@@ -71,11 +74,28 @@ declare class ChartEngine {
71
74
  private scaleManager;
72
75
  private emitter;
73
76
  private theme;
77
+ /**
78
+ * Theme for the DOM chrome (toolbars, dialogs, popovers). Tracks `theme`
79
+ * except that the user's Chart Settings surface picks are excluded, so a
80
+ * custom canvas background/axis colour cannot repaint the surrounding UI.
81
+ */
82
+ private chromeTheme;
74
83
  private themeName;
75
84
  private _debug;
76
85
  private series;
77
86
  private showVolume;
78
87
  private symbol;
88
+ /** Contract specs for the current symbol, as supplied by the host. */
89
+ private instrumentSpec;
90
+ /**
91
+ * `instrumentSpec` with fallbacks applied, rebuilt only when the spec or the
92
+ * feed's decimal count changes — resolving it per frame would re-derive the
93
+ * inferred pip size on every render.
94
+ */
95
+ private resolvedInstrument;
96
+ private resolvedInstrumentPrecision;
97
+ /** Account equity and per-trade risk used to size the position tools. */
98
+ private accountSpec;
79
99
  private showBidAskLines;
80
100
  /** Per-line visibility checks. `undefined` falls back to the legacy
81
101
  * `showBidAskLines` behavior (both when true, source side when false). */
@@ -85,6 +105,11 @@ declare class ChartEngine {
85
105
  private hideLevelConfirmCancel;
86
106
  private deselectActiveOnOutsideClick;
87
107
  private tradeLevelButtonScale;
108
+ /** `tradeLevelBadgeScale` / `tradeLevelBadgeAnchor` — size and horizontal home of the main-level tag. */
109
+ private tradeLevelBadgeScale;
110
+ private tradeLevelBadgeAnchor;
111
+ /** What the SL/TP bracket pills read — see `ChartConfig.bracketLabelMode`. */
112
+ private _bracketLabel;
88
113
  private levelClusteringEnabled;
89
114
  private clusterThresholdDistance;
90
115
  private enableThemeToggle;
@@ -114,6 +139,13 @@ declare class ChartEngine {
114
139
  /** Pending resize — committed only after mouse moves past DRAW_GESTURE_THRESHOLD. */
115
140
  private _pendingDrawResize;
116
141
  private static readonly DRAW_GESTURE_THRESHOLD;
142
+ /**
143
+ * Extra px of reach when hit-testing drawing handles from a touch. Drawings
144
+ * use a ~8px mouse-sized radius; a fingertip is far wider, so without this a
145
+ * grab that looks on-target misses and the gesture falls through to
146
+ * pan/pinch — the chart zooms instead of resizing the shape.
147
+ */
148
+ private static readonly TOUCH_HANDLE_SLOP_PX;
117
149
  /**
118
150
  * Pending trade-level drag — committed only after mouse moves past TRADE_DRAG_PIXEL_THRESHOLD.
119
151
  * A mouseup before the threshold means it was a click: route to _openTradeLevelEditForm instead
@@ -121,6 +153,8 @@ declare class ChartEngine {
121
153
  */
122
154
  private _pendingTradeDrag;
123
155
  private static readonly TRADE_DRAG_PIXEL_THRESHOLD;
156
+ /** Movement a dual-axis badge grab needs before it commits to time vs price. */
157
+ private static readonly AXIS_LOCK_PIXEL_THRESHOLD;
124
158
  private paneHeightOverrides;
125
159
  private separatorDrag;
126
160
  private readonly priceAxisWidth;
@@ -189,6 +223,8 @@ declare class ChartEngine {
189
223
  /** Last traded price from the most recent tick that carried one ('ltp' source mode). */
190
224
  private liveLtp;
191
225
  private tickClosePriceSource;
226
+ /** Re-fetch history through `dataLoader` when the price source changes (`reloadOnPriceSourceChange`). */
227
+ private _reloadOnPriceSourceChange;
192
228
  /** Visibility of the LTP price marker (dashed line + axis tag). `undefined`
193
229
  * follows `tickClosePriceSource` — marker shown only in 'ltp' mode. */
194
230
  private showLtpPrice;
@@ -228,6 +264,18 @@ declare class ChartEngine {
228
264
  private orderTimeDragHandler;
229
265
  /** Live (clamped) bar index of the badge during a horizontal time-drag; null when idle. */
230
266
  private tradeDragNewBarIndex;
267
+ /** The host's `features` block, forwarded to drawings that gate their output. */
268
+ private _features;
269
+ /** Floating P/L pill (`features.pnlPill`); null when the feature is off. */
270
+ private pnlPill;
271
+ /** "Positions shown" mode — the pill body was clicked; every position cluster stays fanned out. */
272
+ private pnlPillExpanded;
273
+ /** Re-entrancy guard for the one extra trade-layer pass that mode needs. */
274
+ private _pnlPillExpandPass;
275
+ /** The user's P/L pill preference (header switch / `setPnlPillVisible`). */
276
+ private pnlPillVisible;
277
+ /** Names of the `features` flags the host switched on, for gated UI. */
278
+ private _enabledGates;
231
279
  /** Whether horizontal order-line time-drag is enabled (`features.orderLineTimeDrag`). */
232
280
  private _orderTimeDragEnabled;
233
281
  /** Snap a horizontally-dragged order line to the nearest candle on release (`orderLineDragSnap`). */
@@ -238,6 +286,15 @@ declare class ChartEngine {
238
286
  * `timestamp` back via `setLevels` (auto-released on match). Survives PnL-only refreshes.
239
287
  */
240
288
  private _timeAnchorOverride;
289
+ /**
290
+ * Time anchors persisted in localStorage (`orderLineAnchorPersistence`), keyed by level
291
+ * label — the candle each badge was last dropped on. Restored at construction so a
292
+ * horizontally-dragged order line comes back where the user left it after a reload.
293
+ * Null when persistence is disabled or time-drag is off.
294
+ */
295
+ private _orderLineAnchorStore;
296
+ /** Where an un-dragged `timeDraggable` badge sits (`orderLineDefaultAnchor`). */
297
+ private _orderLineDefaultAnchor;
241
298
  private tradeDragNewPrice;
242
299
  /** Derived SL preview price during an entry drag in follow-brackets mode. */
243
300
  private tradeDragNewSLPrice;
@@ -253,6 +310,8 @@ declare class ChartEngine {
253
310
  private _isBracketNew;
254
311
  private pendingLevelDrags;
255
312
  private tfcEnabled;
313
+ /** Mouse crosshair (and the trade button that rides on it) — `ChartConfig.crosshairEnabled`. */
314
+ private crosshairEnabled;
256
315
  private tfcActive;
257
316
  private tradeDisplayFilter;
258
317
  private positionRenderStyle;
@@ -265,6 +324,8 @@ declare class ChartEngine {
265
324
  private noMoreData;
266
325
  private _loadGeneration;
267
326
  private maxCandles;
327
+ /** Duration → timeframe pairing (built-in defaults merged with the host's overrides). */
328
+ private _durationTimeframeMap;
268
329
  private minInitialBars;
269
330
  private maxLookbackMs;
270
331
  private prefetchThreshold;
@@ -278,11 +339,16 @@ declare class ChartEngine {
278
339
  private showCandleCountdown;
279
340
  private candleCountdownTimeframes;
280
341
  private showTradeLevelsAlways;
342
+ /** Widen the price axis when a level gains an SL/TP outside the visible range (`revealNewBrackets`). */
343
+ private _revealNewBrackets;
281
344
  private showPriceAxisCountdown;
282
345
  private readonly SHOW_SLTP_ALWAYS_LS_KEY;
283
346
  private readonly PRICE_AXIS_COUNTDOWN_LS_KEY;
284
347
  private loadingOverlayEl;
285
348
  private noDataOverlayEl;
349
+ /** Label pills inside the two overlays — painted by `applyOverlayLabelStyles()`. */
350
+ private loadingLabelEl;
351
+ private noDataLabelEl;
286
352
  private logoImage;
287
353
  constructor(config: ChartConfig);
288
354
  /**
@@ -358,11 +424,91 @@ declare class ChartEngine {
358
424
  private openIndicatorSettings;
359
425
  private updateIndicatorParams;
360
426
  on<K extends keyof ChartEventMap>(event: K, listener: (payload: ChartEventMap[K]) => void): () => void;
427
+ /**
428
+ * Turn the user's Chart Settings colour picks into a theme override.
429
+ *
430
+ * `includeSurface` decides whether the *surface* colours the settings dialog
431
+ * exposes — background, grid, axis text, axis border, crosshair — are part of
432
+ * the result. Those five belong to the canvas alone. The very same
433
+ * `background` / `axisText` / `axisBorder` tokens also drive every panel,
434
+ * dialog, toolbar and popover in the chart chrome, so merging a canvas pick
435
+ * into the chrome theme repainted the top/bottom/left bars with the chart
436
+ * background and put the user's "Axis Border" colour on the borders of the
437
+ * settings dialog itself. Series colours (candle / volume) are included
438
+ * either way — legends and indicator pills are meant to track them.
439
+ */
440
+ private canvasColorOverride;
441
+ /**
442
+ * Colour for the text laid directly over the canvas — the symbol name and
443
+ * the OHLC strip.
444
+ *
445
+ * Those labels sit on the chart background, so once the user picks their own
446
+ * background the fixed theme grey can land dark-on-dark and become
447
+ * unreadable (the reported symptom). Precedence: an explicit "Axis Text"
448
+ * pick wins; otherwise, if the user chose a custom background, use maximum
449
+ * contrast against it; otherwise return the stock theme colour untouched, so
450
+ * the default dark and light themes render exactly as before.
451
+ */
452
+ private overlayTextColor;
453
+ /** Theme used to paint the canvas — includes the user's surface picks. */
361
454
  private buildTheme;
455
+ /**
456
+ * Theme used to paint the chart *chrome* — toolbars, dialogs, popovers.
457
+ * Identical to the canvas theme except that the user's canvas surface picks
458
+ * are left out, so recolouring the chart background or axis no longer
459
+ * repaints the surrounding controls.
460
+ */
461
+ private buildChromeTheme;
462
+ /**
463
+ * Push the freshly built themes into every sub-component.
464
+ *
465
+ * Chrome components (bars, dialogs, popovers) get `chromeTheme`; components
466
+ * that are drawn over the canvas and must relate to it — the trade button,
467
+ * indicator pills, compare legend, sub-pane overlays — get the canvas
468
+ * `theme`. Called from every place that rebuilds the theme so the routing
469
+ * lives in exactly one spot.
470
+ */
471
+ /** Build the floating P/L pill and keep it in step with the levels, the TFC state and the theme. */
472
+ private initPnlPill;
473
+ /** Recount the positions on the chart for the pill. No-op without the feature. */
474
+ private refreshPnlPill;
475
+ /**
476
+ * Show or hide the on-chart P/L pill on the user's behalf — what the header's
477
+ * P/L switch does. Hidden wins over the position count; the switch and the
478
+ * pill stay in step either way, and `pnlPillToggle` tells the host so it can
479
+ * persist the choice. Requires `features.pnlPill`.
480
+ */
481
+ setPnlPillVisible(visible: boolean): this;
482
+ /**
483
+ * The pill body was clicked. On: switch TFC on if it was off and keep every
484
+ * position cluster fanned out (see the tail of `renderTradeLayer`), so the
485
+ * trader sees each position's own badge — P/L, side, quantity, edit, close.
486
+ * Off: collapse them again. Escape also leaves the mode.
487
+ */
488
+ private togglePnlPillExpanded;
489
+ /** The pill's ✕: hand every counted position to the host, which owns the broker call. */
490
+ private emitPnlPillCloseAll;
491
+ private applyThemeToComponents;
362
492
  setTheme(theme: 'dark' | 'light'): this;
363
493
  /** Current active timeframe — lightweight read for cross-pane sync. */
364
494
  getTimeframe(): Timeframe;
365
495
  /** Programmatically change the active timeframe and reload data. */
496
+ /**
497
+ * Change the duration programmatically — the same path the bottom-bar
498
+ * buttons take, so the paired timeframe, the x-axis and the data all move
499
+ * together.
500
+ *
501
+ * Pairs the timeframe from `durationTimeframeMap` unless `timeframe` is
502
+ * given explicitly, updates the bottom/top bars, emits `durationChange`,
503
+ * and refetches through `dataLoader`. The x-axis rescales from the new
504
+ * bars, so no reinitialisation is needed and nothing stale is left behind.
505
+ *
506
+ * @param d Duration to select.
507
+ * @param timeframe Optional override for the paired timeframe.
508
+ */
509
+ setDuration(d: Duration, timeframe?: Timeframe): this;
510
+ /** Currently selected duration, or `null` when the user is on a free timeframe. */
511
+ getDuration(): Duration | null;
366
512
  setTimeframe(tf: Timeframe): this;
367
513
  /**
368
514
  * Programmatically place the crosshair at a given bar index + price, or clear it.
@@ -396,6 +542,18 @@ declare class ChartEngine {
396
542
  timeToBarIndex(timeMs: number): number | null;
397
543
  /** Update per-theme deep-partial color overrides and rebuild the active theme. */
398
544
  setThemeOverrides(overrides: ChartConfig['themeOverrides']): this;
545
+ /**
546
+ * Recolour the **canvas only** at runtime — the same per-theme picks the Chart
547
+ * Settings dialog offers (`canvasColors`): `background`, `grid`, `axisText`,
548
+ * `axisBorder`, `crosshair` repaint the plot and its axes and leave the chrome
549
+ * (header, bottom bar, drawing toolbar, dialogs, popovers) on the theme from
550
+ * `themeOverrides`; candle / volume picks apply everywhere so legends track them.
551
+ * Use this rather than `setThemeOverrides({ dark: { background } })` when a host
552
+ * wants a chart background that must not bleed into the toolbars. Replaces the
553
+ * current picks (partial per theme); `null` clears them. Persisted in `getState()`
554
+ * like the dialog's own picks, and `stateChange` fires.
555
+ */
556
+ setCanvasColors(colors: ChartConfig['canvasColors'] | null): this;
399
557
  /**
400
558
  * Changes the display timezone for all time-axis and crosshair labels.
401
559
  * Accepts any IANA string (e.g. `"America/New_York"`), `"UTC"`, or `"local"`.
@@ -408,6 +566,16 @@ declare class ChartEngine {
408
566
  * Only functional when `tfcEnabled` config option is `true` (default).
409
567
  */
410
568
  setTfcActive(enabled: boolean): this;
569
+ /**
570
+ * Show or hide the mouse crosshair at runtime — and with it the floating
571
+ * "place order at this price" button, which rides on the crosshair's
572
+ * horizontal line and should not appear once the line is gone.
573
+ * Off: the current crosshair is cleared (listeners get `crosshair: null`),
574
+ * mouse moves no longer place one, `setCrosshair()` mirrors are ignored, the
575
+ * mobile long-press crosshair does not arm, and the trade button hides.
576
+ * Trade levels, drawings and the TFC state are untouched.
577
+ */
578
+ setCrosshairEnabled(enabled: boolean): this;
411
579
  private clearDuration;
412
580
  setVolume(show: boolean): this;
413
581
  /**
@@ -495,6 +663,22 @@ declare class ChartEngine {
495
663
  * and emits a `stateChange` event so consumers can persist the cleared state.
496
664
  */
497
665
  resetChartSettings(): this;
666
+ /**
667
+ * Replaces the contract specs used by measurement and position tools. Call it
668
+ * alongside `setSymbol()` — specs belong to the instrument, and a stale
669
+ * pip size silently reports the wrong number rather than failing visibly.
670
+ */
671
+ setInstrument(instrument: InstrumentSpec | undefined): this;
672
+ /** Contract specs with fallbacks applied, as the drawing tools see them. */
673
+ getInstrument(): ResolvedInstrument;
674
+ /**
675
+ * Updates the account equity and per-trade risk the Long/Short position
676
+ * tools size against. Push it whenever equity moves — a sketch drawn against
677
+ * a stale balance quietly reports the wrong quantity.
678
+ */
679
+ setAccount(account: AccountSpec | undefined): this;
680
+ /** Account figures currently used to size the position tools. */
681
+ getAccount(): AccountSpec | undefined;
498
682
  setSymbol(symbol: string): this;
499
683
  /**
500
684
  * Update the list of symbols available in the ISIN picker modal.
@@ -513,6 +697,20 @@ declare class ChartEngine {
513
697
  * @param qty Lot size to display and submit.
514
698
  */
515
699
  setDraftQty(qty: number): this;
700
+ /**
701
+ * Update the quantity (lots) shown on an EXISTING level's pill — the
702
+ * host-panel counterpart of the chart's own qty-pill editing, mirroring
703
+ * `updateLevelBracket` / `updateLevelMainPrice` for the quantity field.
704
+ *
705
+ * Exists because a modify panel that edits lots had no way to reflect the
706
+ * change on the chart: `setLevelQty` was private and only reachable for
707
+ * drafts, so the pill kept the server's old size until the broker echoed
708
+ * the modify (reported: panel showed 0.13 while the chart pill stayed
709
+ * 0.07). Goes through the same staging as chart-side qty edits, so the
710
+ * value survives `setLevels` refreshes and is included in `tradeLevelEdit`
711
+ * / reverted by `cancelCurrentEdit` exactly like a pill edit.
712
+ */
713
+ updateLevelQty(label: string, qty: number): this;
516
714
  private setLevelQty;
517
715
  private openDraftQtyFlyout;
518
716
  /**
@@ -544,6 +742,28 @@ declare class ChartEngine {
544
742
  updateDrawingStyle(id: string, patch: Partial<AnyDrawingStyle>): this;
545
743
  connectStream(adapter: IWebSocketAdapter): this;
546
744
  disconnectStream(): this;
745
+ /** Switch which price drives candle close/high/low at runtime
746
+ * (`'bid'`, `'ask'` or `'ltp'`). Also syncs the header price-source
747
+ * dropdown when `priceSourceSelector` is enabled, and — unless
748
+ * `reloadOnPriceSourceChange` is `false` — re-runs `dataLoader` with the
749
+ * new `params.priceSource` so the loaded history is rebuilt from that
750
+ * price too. Programmatic calls do not emit `priceSourceChange` — that
751
+ * event is reserved for user picks from the dropdown. */
752
+ setTickClosePriceSource(source: 'bid' | 'ask' | 'ltp'): this;
753
+ /**
754
+ * Applies a new candle price source. Shared by the header dropdown pick and
755
+ * `setTickClosePriceSource()`.
756
+ *
757
+ * Switching the field alone only changes how the NEXT live ticks extend the
758
+ * last candle; every bar already on screen was fetched with the previous
759
+ * source, so the chart kept showing e.g. bid history under an "LTP" label.
760
+ * When the source actually changes and a `dataLoader` is configured, the
761
+ * whole window is therefore re-fetched (`triggerLoad('priceSource')`) — the
762
+ * loader receives the source as `params.priceSource` to forward to its API.
763
+ * The visible time window is restored after the reload so the user does not
764
+ * lose their scroll/zoom position.
765
+ */
766
+ private _applyPriceSource;
547
767
  /** Show/hide the LTP (last traded price) marker — dashed line + axis tag.
548
768
  * `true` always shows it when the feed supplies an LTP, `false` hides it
549
769
  * even in `'ltp'` mode; pass `undefined` to restore the default
@@ -648,6 +868,13 @@ declare class ChartEngine {
648
868
  * Pass `null` to clear the text.
649
869
  * Use this to display the consumer-calculated estimated profit/loss next to the bracket.
650
870
  */
871
+ /**
872
+ * Switch the SL/TP bracket pills between the bracket price and the money
873
+ * that bracket is worth. `'amount'` needs each level to carry `lots` plus
874
+ * `contractSize` / `valuePerPoint`; any level missing them keeps showing its
875
+ * price, so a partial rollout degrades level by level rather than all at once.
876
+ */
877
+ setBracketLabelMode(mode: 'price' | 'amount' | 'priceAndAmount', currencySymbol?: string): this;
651
878
  setDraftBracketPnl(type: 'sl' | 'tp', pnlText: string | null): this;
652
879
  /**
653
880
  * Update the stop-loss or take-profit price on the current draft order.
@@ -754,17 +981,34 @@ declare class ChartEngine {
754
981
  private toggleDrawingTools;
755
982
  private _applyCssFullscreen;
756
983
  private toggleFullscreen;
984
+ /** The style the user picked, or the auto rule (dot above the threshold) — what persists and what the settings dialog shows. */
985
+ private baseRenderStyle;
757
986
  private recomputeRenderStyle;
758
987
  private renderTradeLayer;
759
988
  /**
760
989
  * Resolves the horizontal badge center X (canvas px) for a level during render, or `null`
761
990
  * to keep the legacy chart-centered placement. Priority:
762
991
  * 1. the live drag bar index, for the level currently being time-dragged;
763
- * 2. a staged drop override (set on the last drag release);
764
- * 3. the level's own `timestamp`.
992
+ * 2. the resolved time anchor — see `_resolvedTimeAnchor`.
765
993
  * Returns `null` when time-drag is disabled or the level has no time anchor.
766
994
  */
767
995
  private _orderLineAnchorX;
996
+ /**
997
+ * The unix-ms time anchor a level's badge should sit at (ignoring any live drag), or
998
+ * `undefined` for the legacy chart-centered placement. Priority:
999
+ * 1. a drop override staged this session (set on the last drag release);
1000
+ * 2. the anchor persisted in localStorage from an earlier session
1001
+ * (`orderLineAnchorPersistence`);
1002
+ * 3. the level's own `timestamp` — unless `orderLineDefaultAnchor: 'center'` and the
1003
+ * level is `timeDraggable`, in which case an un-dragged badge stays centered.
1004
+ */
1005
+ private _resolvedTimeAnchor;
1006
+ /**
1007
+ * Whether a level's own `timestamp` positions its badge. False only for `timeDraggable`
1008
+ * levels under `orderLineDefaultAnchor: 'center'`, whose un-dragged badge is centered
1009
+ * regardless of `timestamp` (a dragged one is positioned by its override / persisted anchor).
1010
+ */
1011
+ private _honorsLevelTimestamp;
768
1012
  /** Returns the effective entry price for a level, preferring any staged main drag. */
769
1013
  private _effectiveEntryPrice;
770
1014
  /**
@@ -886,11 +1130,27 @@ declare class ChartEngine {
886
1130
  * label, so repeat opens (e.g. pencil click followed by drag) don't spam the event.
887
1131
  */
888
1132
  private _openTradeLevelEditForm;
1133
+ /**
1134
+ * Whether grabbing `dragHit` must wait for the drag direction before choosing an axis:
1135
+ * the badge of a `timeDraggable` level that can ALSO be price-dragged (pending orders,
1136
+ * entry-editable positions). Fixed-price positions keep the immediate horizontal drag.
1137
+ */
1138
+ private _needsDragAxisLock;
1139
+ /**
1140
+ * Upgrades an armed `_pendingTradeDrag` to a real drag once the pointer has moved far
1141
+ * enough from where it went down. A dual-axis badge (`axisLock`) commits to the dominant
1142
+ * direction: sideways → horizontal time-drag (price locked), up/down → the default
1143
+ * vertical price-drag, started from the same hit with its `timeDrag` flag cleared.
1144
+ * Returns true when a drag started, false while still waiting for movement.
1145
+ */
1146
+ private _upgradePendingTradeDrag;
889
1147
  /**
890
1148
  * Shared helper: initiate a trade level drag from a hit area.
891
1149
  * Starts a HORIZONTAL (time-axis) drag when the hit area is the badge of a `timeDraggable`
892
1150
  * level and `features.orderLineTimeDrag` is on; otherwise starts the default vertical
893
1151
  * price drag. `hitX` is required for the horizontal path (defaults to the badge anchor).
1152
+ * Dual-axis badges never reach this directly — `_upgradePendingTradeDrag` resolves the
1153
+ * axis first and clears `timeDrag` on the hit when the drag is vertical.
894
1154
  */
895
1155
  private _startTradeDragFromHit;
896
1156
  /**
@@ -946,10 +1206,31 @@ declare class ChartEngine {
946
1206
  * Used by off-viewport level indicator clicks.
947
1207
  */
948
1208
  private scrollToCenterPrice;
1209
+ /**
1210
+ * Bring `prices` into the visible price range by WIDENING (compressing) the axis —
1211
+ * never by panning the current view away — so a freshly placed SL/TP and the candles
1212
+ * the trader was looking at stay on screen together (`revealNewBrackets`). No-op while
1213
+ * every price is already inside the range, in compare (percent) mode, or without data.
1214
+ * Expressed as the same scale + pan knobs the axis drag uses, on top of the range the
1215
+ * trader currently sees, and animated like the off-viewport pill's scroll.
1216
+ */
1217
+ private _revealPrices;
1218
+ /**
1219
+ * The price the header's Alert entry hands to `onAddAlert`: the live ask (what a
1220
+ * host's alert form seeds with), else the live bid, else the last bar's close.
1221
+ */
1222
+ private _currentMarketPrice;
949
1223
  private resetPriceAxis;
950
1224
  private resetTimeAxis;
951
1225
  /** Reset both price and time axes to their default state. */
952
1226
  resetView(): void;
1227
+ /**
1228
+ * Paints the "Loading…" / "No data available" pills from the theme. They used
1229
+ * to be a hardcoded dark scrim with pale grey text, which read as a black box
1230
+ * on the light theme; they now track `tooltip` like every other floating
1231
+ * label and are repainted on `setTheme`.
1232
+ */
1233
+ private applyOverlayLabelStyles;
953
1234
  private applyResetViewBtnStyles;
954
1235
  private applyScrollToEndBtnStyles;
955
1236
  }
@@ -961,6 +1242,15 @@ declare class AdvancedToolbar {
961
1242
  private onLayoutClick?;
962
1243
  private onOneClickTradeToggle?;
963
1244
  private onScreenshotClick?;
1245
+ private onPriceSourceChange;
1246
+ private onCrosshairToggle;
1247
+ private onPnlPillToggle;
1248
+ /**
1249
+ * ⋯ menu "Alert" entry (`headerOverflowMenu`). Set when the host configured
1250
+ * `onAddAlert`; the engine hands the host the current market price so the same
1251
+ * Create Alert form the trade action bar's chip opens comes up. Null leaves it out.
1252
+ */
1253
+ private onAlertClick;
964
1254
  readonly el: HTMLDivElement;
965
1255
  private theme;
966
1256
  private currentSeries;
@@ -972,18 +1262,51 @@ declare class AdvancedToolbar {
972
1262
  private layoutBtn;
973
1263
  private layoutIconEl;
974
1264
  private snapshotBtn;
1265
+ private crosshairBtn;
1266
+ private crosshairEnabled;
1267
+ private pnlPillBtn;
1268
+ private pnlPillVisible;
1269
+ private pnlPillSymbol;
1270
+ /** `headerOverflowMenu`: Indicators / Compare / Snapshot / Settings live in a ⋯ menu; Layout is icon-only. */
1271
+ private overflowMenu;
1272
+ private moreBtn;
1273
+ private showSnapshotItem;
1274
+ private onSettingsClickCb;
1275
+ private onDrawingToolsToggleCb;
1276
+ private drawingActive;
1277
+ private tfMoreBtn;
1278
+ /** Timeframes hidden behind the ⌄ in the reference header. */
1279
+ private tfMore;
1280
+ private onTimeframeChangeCb;
975
1281
  private tfBtns;
976
1282
  private indBtn;
977
1283
  private indicatorOptions;
978
1284
  private activeIndicators;
979
1285
  private volHasData;
1286
+ private priceSourceBtn;
1287
+ private priceSourceOptions;
1288
+ private currentPriceSource;
980
1289
  private flyout;
981
1290
  private openFlyout;
982
1291
  private docClickHandler;
983
- constructor(onSeriesChange: (s: SeriesType) => void, onTimeframeChange: (tf: Timeframe) => void, onAddIndicator: (name: string) => void, onSettingsClick: () => void, onFullscreenToggle: () => void, onDrawingToolsToggle: (() => void) | null, showDrawingBtn: boolean, _showSettingsBtn: boolean, showFullscreenBtn: boolean, initialSeries: SeriesType, initialTf: Timeframe, theme: ChartTheme, onCompareClick?: (() => void) | undefined, onLayoutClick?: (() => void) | undefined, onOneClickTradeToggle?: ((active: boolean) => void) | undefined, onScreenshotClick?: (() => void) | undefined, showLayoutBtn?: boolean, showSnapshotBtn?: boolean);
1292
+ constructor(onSeriesChange: (s: SeriesType) => void, onTimeframeChange: (tf: Timeframe) => void, onAddIndicator: (name: string) => void, onSettingsClick: () => void, onFullscreenToggle: () => void, onDrawingToolsToggle: (() => void) | null, showDrawingBtn: boolean, _showSettingsBtn: boolean, showFullscreenBtn: boolean, initialSeries: SeriesType, initialTf: Timeframe, theme: ChartTheme, onCompareClick?: (() => void) | undefined, onLayoutClick?: (() => void) | undefined, onOneClickTradeToggle?: ((active: boolean) => void) | undefined, onScreenshotClick?: (() => void) | undefined, showLayoutBtn?: boolean, showSnapshotBtn?: boolean, priceSourceOptions?: Array<'bid' | 'ask' | 'ltp'> | null, initialPriceSource?: 'bid' | 'ask' | 'ltp', onPriceSourceChange?: ((source: 'bid' | 'ask' | 'ltp') => void) | null, showCrosshairBtn?: boolean, initialCrosshairEnabled?: boolean, onCrosshairToggle?: ((enabled: boolean) => void) | null, showPnlPillBtn?: boolean, initialPnlPillVisible?: boolean, onPnlPillToggle?: ((visible: boolean) => void) | null, overflowMenu?: boolean,
1293
+ /** `ChartConfig.timeframes` — the header's timeframe list; null keeps the classic set. */
1294
+ timeframes?: Timeframe[] | null,
1295
+ /**
1296
+ * ⋯ menu "Alert" entry (`headerOverflowMenu`). Set when the host configured
1297
+ * `onAddAlert`; the engine hands the host the current market price so the same
1298
+ * Create Alert form the trade action bar's chip opens comes up. Null leaves it out.
1299
+ */
1300
+ onAlertClick?: (() => void) | null);
984
1301
  setTimeframe(tf: Timeframe): void;
985
1302
  setFullscreen(active: boolean): void;
986
1303
  setDrawingsBtnActive(active: boolean): void;
1304
+ /** Icon size on the right cluster: 12px in the reference header, 14px otherwise. */
1305
+ private iconSize;
1306
+ /** Reflect the crosshair on/off state in the header switch. No-op when the button is not rendered. */
1307
+ setCrosshairActive(enabled: boolean): void;
1308
+ /** Reflect the P/L pill's shown/hidden state (and the symbol named in the tooltip) in the header switch. */
1309
+ setPnlPillActive(visible: boolean, symbol?: string): void;
987
1310
  setIndicatorOptions(options: {
988
1311
  value: string;
989
1312
  label: string;
@@ -993,6 +1316,10 @@ declare class AdvancedToolbar {
993
1316
  setVolumeControl(_show: boolean, hasVol: boolean): void;
994
1317
  setTheme(theme: ChartTheme): void;
995
1318
  setSeries(series: SeriesType): void;
1319
+ /** Sync the price-source dropdown to `source` (e.g. after a programmatic
1320
+ * `setTickClosePriceSource()` from the host's trade settings). No-op when
1321
+ * the dropdown isn't enabled. Does not fire `onPriceSourceChange`. */
1322
+ setPriceSource(source: 'bid' | 'ask' | 'ltp'): void;
996
1323
  /** Update the Layout button's preset icon and mark it active/inactive. No-op if the Layout button is disabled. */
997
1324
  setLayoutPresetIcon(svgHtml: string, active?: boolean): void;
998
1325
  /** Mark the Layout button as active (selected) or inactive without changing the icon. */
@@ -1022,9 +1349,15 @@ declare class AdvancedToolbar {
1022
1349
  private makeGroup;
1023
1350
  private makeIconBtn;
1024
1351
  private applyIconBtnStyles;
1352
+ private applyCrosshairBtnStyles;
1353
+ private applyPnlPillBtnStyles;
1354
+ private applyOverflowIconBtnStyles;
1355
+ /** Layout is a labelled text button by default and an icon-only square in overflow mode. */
1356
+ private styleLayoutBtn;
1025
1357
  private makeTextBtn;
1026
1358
  private applyTextBtnStyles;
1027
1359
  private applyTfPillStyles;
1360
+ private applyTfMoreStyles;
1028
1361
  private addHover;
1029
1362
  private makeCategoryHeader;
1030
1363
  private makeRow;
@@ -1043,12 +1376,16 @@ declare class AdvancedToolbar {
1043
1376
  declare class CompactToolbar {
1044
1377
  private onSeriesChange;
1045
1378
  private onAddIndicator;
1379
+ private onCrosshairToggle;
1046
1380
  readonly el: HTMLDivElement;
1047
1381
  private theme;
1048
1382
  private currentSeries;
1049
1383
  private currentTf;
1050
1384
  private seriesBtn;
1051
1385
  private indBtn;
1386
+ /** Crosshair on/off switch (`enableCrossHairHeader`) — null when not requested. */
1387
+ private crosshairBtn;
1388
+ private crosshairEnabled;
1052
1389
  private tfBtns;
1053
1390
  private indicatorOptions;
1054
1391
  private activeIndicators;
@@ -1056,7 +1393,7 @@ declare class CompactToolbar {
1056
1393
  private flyout;
1057
1394
  private openFlyout;
1058
1395
  private docClickHandler;
1059
- constructor(onSeriesChange: (s: SeriesType) => void, onTimeframeChange: (tf: Timeframe) => void, onAddIndicator: (name: string) => void, initialSeries: SeriesType, initialTf: Timeframe, theme: ChartTheme);
1396
+ constructor(onSeriesChange: (s: SeriesType) => void, onTimeframeChange: (tf: Timeframe) => void, onAddIndicator: (name: string) => void, initialSeries: SeriesType, initialTf: Timeframe, theme: ChartTheme, showCrosshairBtn?: boolean, initialCrosshairEnabled?: boolean, onCrosshairToggle?: ((enabled: boolean) => void) | null);
1060
1397
  setTimeframe(tf: Timeframe): void;
1061
1398
  setSeries(series: SeriesType): void;
1062
1399
  setIndicatorOptions(options: {
@@ -1066,6 +1403,8 @@ declare class CompactToolbar {
1066
1403
  }[]): void;
1067
1404
  setIndicatorOptionActive(name: string, active: boolean, _count?: number): void;
1068
1405
  setVolumeControl(_show: boolean, hasVol: boolean): void;
1406
+ /** Reflect the crosshair on/off state in the header switch. No-op when the button is not rendered. */
1407
+ setCrosshairActive(enabled: boolean): void;
1069
1408
  setTheme(theme: ChartTheme): void;
1070
1409
  closeDropdown(): void;
1071
1410
  get hasOpenDropdown(): boolean;
@@ -1092,6 +1431,7 @@ declare class CompactToolbar {
1092
1431
  private applyBarStyles;
1093
1432
  private applyFlyoutStyles;
1094
1433
  private makeIconBtn;
1434
+ private applyCrosshairBtnStyles;
1095
1435
  private applyIconBtnStyles;
1096
1436
  private applyTfPillStyles;
1097
1437
  private addHover;
@@ -1283,6 +1623,80 @@ declare class ChartGroup {
1283
1623
  */
1284
1624
  declare function aggregateCandles(bars: OHLCVBar[], targetIntervalMs: number): OHLCVBar[];
1285
1625
 
1626
+ /**
1627
+ * What the on-chart P/L pill shows for the levels currently on the chart.
1628
+ * Pure — derived from the `setLevels` payload only, so it stays in step with
1629
+ * the lines actually drawn. The chart never computes money itself: `pnl` is
1630
+ * the sum of the numeric `pnl` values the host attached to each position.
1631
+ */
1632
+ interface PnlPillSummary {
1633
+ /** Open positions among the levels. Entry orders and an unconfirmed draft are not counted. */
1634
+ count: number;
1635
+ /** Sum of the counted positions' numeric `pnl`. `0` when none carried one — check `hasPnl`. */
1636
+ pnl: number;
1637
+ /** True when at least one counted position carried a finite numeric `pnl`. */
1638
+ hasPnl: boolean;
1639
+ /** The counted positions, in level order — the set a close-all acts on. */
1640
+ positions: PositionLevel[];
1641
+ /** Counted positions per side (a position without `side` counts for neither). */
1642
+ buys: number;
1643
+ sells: number;
1644
+ /** Both directions open at once — one SL / TP price cannot be valid for both, so bulk levels are off. */
1645
+ mixed: boolean;
1646
+ }
1647
+ /**
1648
+ * Count the open positions in `levels` and total their `pnl`.
1649
+ *
1650
+ * Pending orders never count. Neither do `entryPriceEditable` positions — those
1651
+ * are entry (EL/ES) orders the host renders position-style — nor the level whose
1652
+ * label is `draftLabel`, the client-side draft that has not reached the broker.
1653
+ * A position without a numeric `pnl` still counts but adds nothing to the total.
1654
+ */
1655
+ declare function summarisePositionLevels(levels: readonly AnyLevel[], draftLabel?: string | null): PnlPillSummary;
1656
+ /**
1657
+ * Default money text for the pill: an explicit sign, two decimals and the
1658
+ * currency code after a space — `+72.65 USD`, `-8.10 USD`. Zero and amounts
1659
+ * that round to zero read `+0.00` so the pill never shows `-0.00`. Without a
1660
+ * currency the code and its space are left off.
1661
+ */
1662
+ declare function formatPnlPillValue(pnl: number, currency?: string): string;
1663
+
1664
+ /** Which bracket the SL / TP / TSL chips ask the host to set on every open position. */
1665
+ type BulkLevelKind = 'sl' | 'tp' | 'tsl';
1666
+ /** What the Pending sub-menu needs to know about the clicked price. */
1667
+ interface TradeActionBarContext {
1668
+ pricePrecision: number;
1669
+ /** Order type a buy at this price would be — `limit` below the market, `stop` above. */
1670
+ buyOrderType: 'limit' | 'stop';
1671
+ sellOrderType: 'limit' | 'stop';
1672
+ /** Symbol named in the chips' "unavailable" tooltips. */
1673
+ symbol?: string;
1674
+ /** Open positions on the symbol; `0` greys the SL / TP / TSL chips out. Omit to keep them available. */
1675
+ positionCount?: number;
1676
+ /** Buys and sells both open — one price cannot suit both directions, so the chips grey out. */
1677
+ mixedSides?: boolean;
1678
+ }
1679
+
1680
+ /** Per-render sizes of the main-level info box; everything scales together with `tradeLevelBadgeScale`. */
1681
+ interface BadgeMetrics {
1682
+ /** Effective scale, clamped to [0.5, 1]. */
1683
+ k: number;
1684
+ padX: number;
1685
+ padY: number;
1686
+ rowH: number;
1687
+ pillPadX: number;
1688
+ pillGap: number;
1689
+ pnlGap: number;
1690
+ fontMain: string;
1691
+ fontPnl: string;
1692
+ gripW: number;
1693
+ gripDotR: number;
1694
+ /** Plot-edge margin for a left-homed tag (`tradeLevelBadgeAnchor: 'left'`). */
1695
+ leftMargin: number;
1696
+ }
1697
+ /** Sizes for a badge scale — `1` reproduces the long-standing constants exactly. */
1698
+ declare function badgeMetrics(scale: number): BadgeMetrics;
1699
+ type BadgeAnchor = 'auto' | 'left';
1286
1700
  /** Drag handle hit area for the main level line (non-bracket). */
1287
1701
  interface TradeLevelDragArea {
1288
1702
  label: string;
@@ -1301,10 +1715,12 @@ interface TradeLevelDragArea {
1301
1715
  */
1302
1716
  isBox?: boolean;
1303
1717
  /**
1304
- * True when grabbing this area should start a HORIZONTAL (time-axis) drag instead of the
1718
+ * True when grabbing this area can start a HORIZONTAL (time-axis) drag instead of the
1305
1719
  * default vertical price drag. Set only on the badge/box area of a `timeDraggable` level
1306
- * when `features.orderLineTimeDrag` is enabled. The full-width line area stays vertical,
1307
- * so price-drag remains reachable by grabbing the line.
1720
+ * when `features.orderLineTimeDrag` is enabled. For a level that is also price-draggable
1721
+ * (pending orders, entry-editable positions) ChartEngine waits for the first movement and
1722
+ * picks the axis from its direction; for fixed-price positions the grab is horizontal at
1723
+ * once. The full-width line area always stays vertical.
1308
1724
  */
1309
1725
  timeDrag?: boolean;
1310
1726
  }
@@ -1334,12 +1750,81 @@ interface TradeLevelBracketDragArea {
1334
1750
  }
1335
1751
  type AnyDragArea = TradeLevelDragArea | TradeLevelBracketDragArea;
1336
1752
 
1753
+ /**
1754
+ * Money maths for trade levels.
1755
+ *
1756
+ * The chart has no access to a broker's contract specs or account currency, so
1757
+ * everything needed to turn a price distance into money is supplied per level:
1758
+ * `lots`, `contractSize`, and `valuePerPoint` (which folds in any quote →
1759
+ * account currency conversion). Hosts that would rather compute the figure
1760
+ * themselves can keep passing a pre-formatted `pnlText` instead.
1761
+ */
1762
+ /** Inputs for a single bracket's money value. */
1763
+ interface BracketPnlInput {
1764
+ /** Level entry price. */
1765
+ entryPrice: number;
1766
+ /** SL or TP price. */
1767
+ bracketPrice: number;
1768
+ /** Position direction — a sell profits when price falls. */
1769
+ side: 'buy' | 'sell';
1770
+ /** Position size in lots. */
1771
+ lots: number;
1772
+ /** Units per lot (e.g. 100 for XAUUSD, 100_000 for most FX pairs). Default 1. */
1773
+ contractSize?: number;
1774
+ /** Account-currency value of one price unit per contract unit. Default 1. */
1775
+ valuePerPoint?: number;
1776
+ }
1777
+ /**
1778
+ * Money the position gains (positive) or loses (negative) if price reaches the
1779
+ * bracket. Returns `null` when an input is missing or non-finite, so callers can
1780
+ * fall back to showing the price rather than rendering `NaN`.
1781
+ */
1782
+ declare function bracketPnl(input: BracketPnlInput): number | null;
1783
+ /**
1784
+ * Formats money with the currency symbol in front of the number and the sign in
1785
+ * front of the symbol — `-$290.80`, `+$18.92` — matching how hosts format the
1786
+ * `pnlText` already shown on the main info box.
1787
+ *
1788
+ * A value that rounds to zero is rendered without a sign (`$0.00`) rather than
1789
+ * as a misleading `+$0.00`/`-$0.00`.
1790
+ */
1791
+ declare function formatMoney(amount: number, symbol?: string, decimals?: number): string;
1792
+ /** Inputs for sizing a position from an account's risk budget. */
1793
+ interface PositionSizeInput {
1794
+ /** Intended entry price. */
1795
+ entryPrice: number;
1796
+ /** Protective stop price. */
1797
+ stopPrice: number;
1798
+ /** Account equity in the account currency. */
1799
+ accountSize: number;
1800
+ /** Percent of the account risked if the stop is hit. */
1801
+ riskPercent: number;
1802
+ /** Account-currency value of one price unit per contract unit. Default 1. */
1803
+ valuePerPoint?: number;
1804
+ }
1805
+ /**
1806
+ * Position size in contract units such that hitting the stop costs exactly
1807
+ * `riskPercent` of the account.
1808
+ *
1809
+ * Returns `null` when an input is missing, non-finite, or would divide by zero
1810
+ * (entry and stop at the same price), so callers can omit the quantity rather
1811
+ * than rendering `Infinity`.
1812
+ */
1813
+ declare function positionQty(input: PositionSizeInput): number | null;
1814
+ /**
1815
+ * Reward divided by risk — how many times the stop distance the target sits
1816
+ * away. `null` when the stop is at the entry, which has no defined ratio.
1817
+ */
1818
+ declare function riskRewardRatio(entryPrice: number, targetPrice: number, stopPrice: number): number | null;
1819
+
1337
1820
  declare class DrawingManager {
1338
1821
  private drawings;
1339
1822
  private activeDrawing;
1340
1823
  private selectedDrawing;
1341
1824
  private activeTool;
1342
1825
  private state;
1826
+ /** `features.enableForecasting` — see `canDrawFreehand`. */
1827
+ private freehandEnabled;
1343
1828
  private history;
1344
1829
  private redoStack;
1345
1830
  private onCompleteCallback;
@@ -1366,8 +1851,16 @@ declare class DrawingManager {
1366
1851
  private dragStartPoints;
1367
1852
  private resizeDrawing;
1368
1853
  private resizePointIndex;
1854
+ /** Handle being dragged — carries the axis split for compound corners. */
1855
+ private resizeHandle;
1369
1856
  /** Called by ChartEngine to be notified when a drawing is finished. */
1370
1857
  setOnComplete(cb: () => void): void;
1858
+ /**
1859
+ * Enables press-drag-release drawing for Brush and Highlighter
1860
+ * (`ChartConfig.features.enableForecasting`). When off they behave as
1861
+ * click-per-point tools ended by a double-click, as they always have.
1862
+ */
1863
+ setFreehandEnabled(enabled: boolean): void;
1371
1864
  /** Toggle the text-editing guard so Delete/Backspace are not intercepted. */
1372
1865
  setEditingText(editing: boolean): void;
1373
1866
  setActiveTool(tool: DrawingToolType | null): void;
@@ -1375,9 +1868,22 @@ declare class DrawingManager {
1375
1868
  /** Returns true if the drawing system consumed the event */
1376
1869
  onMouseDown(point: DrawingPoint): boolean;
1377
1870
  onMouseMove(point: DrawingPoint): void;
1871
+ /** True while a freehand stroke is in progress (pointer still down). */
1872
+ isFreehandStroke(): boolean;
1378
1873
  /**
1379
- * Finalizes an open-ended drawing (Polyline, Path, Brush, Highlighter)
1380
- * on double-click. Removes the dangling preview point and marks complete.
1874
+ * Ends a freehand stroke on pointer-up: thins the samples down to the ones
1875
+ * that carry the shape, then completes the drawing. Returns true when a
1876
+ * stroke was ended, so the caller knows the event was consumed.
1877
+ *
1878
+ * Thinning happens here rather than during the drag because the tolerance is
1879
+ * a screen distance — it can only be applied against the scale the stroke was
1880
+ * actually drawn at, and re-measuring every sample on every move would cost
1881
+ * more than it saves.
1882
+ */
1883
+ finishFreehand(scale: ScaleManager, viewport: Viewport, priceRange: PriceRange$1, chartW: number, chartH: number): boolean;
1884
+ /**
1885
+ * Finalizes an open-ended drawing (Polyline, Path) on double-click.
1886
+ * Removes the dangling preview point and marks complete.
1381
1887
  */
1382
1888
  finalizePlacing(): void;
1383
1889
  startDrag(drawing: IDrawing, mouse: DrawingPoint): void;
@@ -1385,13 +1891,22 @@ declare class DrawingManager {
1385
1891
  isDraggingDrawing(): boolean;
1386
1892
  /** True when a multi-point drawing has been started and is awaiting further touch/click input. */
1387
1893
  isPlacing(): boolean;
1388
- startResize(drawing: IDrawing, pointIndex: number): void;
1894
+ startResize(drawing: IDrawing, pointIndex: number, handle?: DrawingHandle): void;
1389
1895
  endResize(): void;
1390
1896
  isResizingDrawing(): boolean;
1391
1897
  /** Shift all drawings' barIndex values by `delta` (called when bars are prepended to DataStore). */
1392
1898
  shiftBarIndices(delta: number): void;
1393
1899
  /** Returns the handle under (x,y) across all complete drawings, or null */
1394
- getHandleAtPoint(x: number, y: number, scale: ScaleManager, viewport: Viewport, priceRange: PriceRange$1, chartW: number, chartH: number): {
1900
+ getHandleAtPoint(x: number, y: number, scale: ScaleManager, viewport: Viewport, priceRange: PriceRange$1, chartW: number, chartH: number,
1901
+ /**
1902
+ * Extra reach in px, for touch. Drawings hit-test their handles against a
1903
+ * mouse-sized radius (~8px); a fingertip covers far more than that, so a
1904
+ * grab that looks on-target misses and the gesture falls through to
1905
+ * pan/pinch — the chart zooms instead of resizing the shape. Rather than
1906
+ * widen the tolerance in all 69 drawing classes, probe outward in rings
1907
+ * from the touch point and take the nearest handle found.
1908
+ */
1909
+ searchRadius?: number): {
1395
1910
  drawing: IDrawing;
1396
1911
  handle: DrawingHandle;
1397
1912
  } | null;
@@ -1415,7 +1930,7 @@ declare class DrawingManager {
1415
1930
  showAll(): void;
1416
1931
  lockAll(): void;
1417
1932
  unlockAll(): void;
1418
- render(ctx: CanvasRenderingContext2D, scale: ScaleManager, viewport: Viewport, priceRange: PriceRange$1, chartW: number, chartH: number, pricePrecision?: number, theme?: ChartTheme): void;
1933
+ render(ctx: CanvasRenderingContext2D, scale: ScaleManager, viewport: Viewport, priceRange: PriceRange$1, chartW: number, chartH: number, pricePrecision?: number, theme?: ChartTheme, context?: DrawingRenderContext): void;
1419
1934
  getDrawings(): IDrawing[];
1420
1935
  /** Returns a JSON-serializable snapshot of all drawings (data fields only, no methods). */
1421
1936
  getDrawingsSnapshot(): SerializedDrawing[];
@@ -1428,6 +1943,72 @@ declare class DrawingManager {
1428
1943
  private createDrawing;
1429
1944
  }
1430
1945
 
1946
+ /**
1947
+ * Risk/reward sketch for a trade that hasn't been placed.
1948
+ *
1949
+ * Three prices — entry, target, stop — drawn as a green profit zone and a red
1950
+ * risk zone over a span of bars, with the money and quantity the account's risk
1951
+ * budget implies. Requested on AWT-2642 as the headline gap against TradingView.
1952
+ *
1953
+ * Deliberately **not** Trade-From-Chart: `setLevels` draws real broker orders and
1954
+ * committing one sends it. This is a drawing. Nothing here reaches the broker.
1955
+ *
1956
+ * Points, in order:
1957
+ * - `0` entry price, and the left edge of the span
1958
+ * - `1` target price, and the right edge of the span
1959
+ * - `2` stop price (its `barIndex` is unused — the span comes from 0 and 1)
1960
+ *
1961
+ * The user places 0 and 1; [`onPlaced`](../core/types.ts) materialises 2 at
1962
+ * [`DEFAULT_RR`], so the common case is two clicks rather than three.
1963
+ */
1964
+ declare abstract class PositionTool implements IDrawing {
1965
+ readonly id: string;
1966
+ abstract readonly type: 'longPosition' | 'shortPosition';
1967
+ /** Long profits above entry; short profits below. Flips zones and P&L sign. */
1968
+ protected abstract readonly isLong: boolean;
1969
+ readonly requiredPoints = 2;
1970
+ points: DrawingPoint[];
1971
+ complete: boolean;
1972
+ style?: AnyDrawingStyle;
1973
+ /**
1974
+ * Per-instance overrides for the account figures. Seeded from the chart's
1975
+ * `account` config on first render; set them to let one sketch size against
1976
+ * different assumptions than the rest of the chart.
1977
+ */
1978
+ accountSize?: number;
1979
+ riskPercent?: number;
1980
+ constructor(first: DrawingPoint, idPrefix: string);
1981
+ /**
1982
+ * Places the stop once entry and target are known.
1983
+ *
1984
+ * Two clicks give a direction and a reward; the risk that pairs with them is
1985
+ * a convention, not information the user supplied — so the tool assumes
1986
+ * [`DEFAULT_RR`] and leaves the handle there to be dragged.
1987
+ */
1988
+ onPlaced(): void;
1989
+ /** Entry / target / stop, with the stop derived if placement hasn't finished. */
1990
+ protected prices(): {
1991
+ entry: number;
1992
+ target: number;
1993
+ stop: number;
1994
+ };
1995
+ render(ctx: CanvasRenderingContext2D, scale: ScaleManager, viewport: Viewport, priceRange: PriceRange$1, chartW: number, chartH: number, isSelected: boolean, pricePrecision?: number, theme?: ChartTheme, context?: DrawingRenderContext): void;
1996
+ hitTest(x: number, y: number, scale: ScaleManager, viewport: Viewport, priceRange: PriceRange$1, chartW: number, chartH: number): boolean;
1997
+ getHandle(x: number, y: number, scale: ScaleManager, viewport: Viewport, priceRange: PriceRange$1, chartW: number, chartH: number): DrawingHandle | null;
1998
+ }
1999
+ /** Profit above entry, risk below. */
2000
+ declare class LongPosition extends PositionTool {
2001
+ readonly type: "longPosition";
2002
+ protected readonly isLong = true;
2003
+ constructor(first: DrawingPoint);
2004
+ }
2005
+ /** Profit below entry, risk above. */
2006
+ declare class ShortPosition extends PositionTool {
2007
+ readonly type: "shortPosition";
2008
+ protected readonly isLong = false;
2009
+ constructor(first: DrawingPoint);
2010
+ }
2011
+
1431
2012
  declare class HorizontalLine implements IDrawing {
1432
2013
  readonly id: string;
1433
2014
  readonly type: "horizontalLine";
@@ -1807,14 +2388,54 @@ declare class DatePriceRange implements IDrawing {
1807
2388
  getHandle(x: number, y: number, scale: ScaleManager, viewport: Viewport, priceRange: PriceRange$1, chartW: number, chartH: number): DrawingHandle | null;
1808
2389
  }
1809
2390
 
2391
+ /**
2392
+ * Measures a move: how far in price, percent and pips, over how many bars and
2393
+ * how much wall-clock time, on what volume.
2394
+ *
2395
+ * Replaces a version that drew a bare line labelled with a bar count and a raw
2396
+ * price difference. The client's feedback (AWT-2642) was that the tool is
2397
+ * unusable next to TradingView's, whose readout answers all of those at once —
2398
+ * and a forex desk asking for a "pip counter" is asking for the pips line
2399
+ * specifically, which the old tool could not produce at all.
2400
+ */
1810
2401
  declare class Ruler implements IDrawing {
1811
2402
  readonly id: string;
1812
2403
  readonly type: "ruler";
1813
2404
  points: DrawingPoint[];
1814
2405
  complete: boolean;
1815
2406
  style?: AnyDrawingStyle;
2407
+ /**
2408
+ * Last-seen value of `features.enableForecasting`, cached during `render`.
2409
+ *
2410
+ * `hitTest` and `getHandle` need it too — the measured box grabs on its four
2411
+ * edges, the classic line grabs along itself — but their signatures carry no
2412
+ * render context. A drawing is always rendered before it can be clicked, so
2413
+ * the cache is populated by then; it defaults to the classic behaviour.
2414
+ */
2415
+ private detailed;
1816
2416
  constructor(first: DrawingPoint);
1817
- render(ctx: CanvasRenderingContext2D, scale: ScaleManager, viewport: Viewport, priceRange: PriceRange$1, chartW: number, chartH: number, isSelected: boolean, pricePrecision?: number, theme?: ChartTheme): void;
2417
+ /**
2418
+ * Volume traded across the measured span, or `null` when the host didn't
2419
+ * pass bars (the readout then drops the line rather than showing a zero that
2420
+ * would read as "no volume traded").
2421
+ */
2422
+ private rangeVolume;
2423
+ /**
2424
+ * The readout, one entry per line.
2425
+ *
2426
+ * Two shapes, chosen by `features.enableForecasting`: the historical bar-count
2427
+ * and raw-delta pair, or the full measurement with percent, pips, duration
2428
+ * and volume. Gated because it changes what an existing tool reports.
2429
+ */
2430
+ private buildLabel;
2431
+ render(ctx: CanvasRenderingContext2D, scale: ScaleManager, viewport: Viewport, priceRange: PriceRange$1, chartW: number, chartH: number, isSelected: boolean, pricePrecision?: number, theme?: ChartTheme, context?: DrawingRenderContext): void;
2432
+ /**
2433
+ * The ruler exactly as it was before AWT-2642: a styled line with two
2434
+ * perpendicular end ticks, a bar count and a raw price delta at the midpoint,
2435
+ * and endpoint dots. Used whenever `features.enableForecasting` is off, so an
2436
+ * un-flagged host sees no change at all.
2437
+ */
2438
+ private renderClassic;
1818
2439
  hitTest(x: number, y: number, scale: ScaleManager, viewport: Viewport, priceRange: PriceRange$1, chartW: number, chartH: number): boolean;
1819
2440
  getHandle(x: number, y: number, scale: ScaleManager, viewport: Viewport, priceRange: PriceRange$1, chartW: number, chartH: number): DrawingHandle | null;
1820
2441
  }
@@ -2230,12 +2851,32 @@ declare class ElliottWxy implements IDrawing {
2230
2851
  declare class Brush implements IDrawing {
2231
2852
  readonly id: string;
2232
2853
  readonly type: "brush";
2854
+ /**
2855
+ * Drawn in one press-drag-release gesture: points are sampled continuously
2856
+ * along the pointer path rather than placed one click at a time. See
2857
+ * DrawingManager's freehand branch.
2858
+ */
2859
+ readonly freehand = true;
2860
+ /**
2861
+ * Also declared open-ended so the tool still works when
2862
+ * `features.enableForecasting` is off: DrawingManager checks freehand first and
2863
+ * falls through to this click-per-point path when the flag is not set.
2864
+ */
2233
2865
  readonly openEnded = true;
2234
2866
  points: DrawingPoint[];
2235
2867
  complete: boolean;
2236
2868
  style?: AnyDrawingStyle;
2869
+ /**
2870
+ * Last-seen value of `features.enableForecasting`, cached during `render`.
2871
+ *
2872
+ * `hitTest` takes no render context but must agree with what was drawn: a
2873
+ * freehand stroke has no dangling preview point, a click-per-point one does.
2874
+ * A drawing is always rendered before it can be clicked, so the cache is
2875
+ * populated by then; it defaults to the classic behaviour.
2876
+ */
2877
+ private freehandMode;
2237
2878
  constructor(first: DrawingPoint);
2238
- render(ctx: CanvasRenderingContext2D, scale: ScaleManager, viewport: Viewport, priceRange: PriceRange$1, chartW: number, chartH: number, isSelected: boolean, _pricePrecision?: number, theme?: ChartTheme): void;
2879
+ render(ctx: CanvasRenderingContext2D, scale: ScaleManager, viewport: Viewport, priceRange: PriceRange$1, chartW: number, chartH: number, isSelected: boolean, _pricePrecision?: number, theme?: ChartTheme, context?: DrawingRenderContext): void;
2239
2880
  hitTest(x: number, y: number, scale: ScaleManager, viewport: Viewport, priceRange: PriceRange$1, chartW: number, chartH: number): boolean;
2240
2881
  getHandle(x: number, y: number, scale: ScaleManager, viewport: Viewport, priceRange: PriceRange$1, chartW: number, chartH: number): DrawingHandle | null;
2241
2882
  }
@@ -2243,12 +2884,32 @@ declare class Brush implements IDrawing {
2243
2884
  declare class Highlighter implements IDrawing {
2244
2885
  readonly id: string;
2245
2886
  readonly type: "highlighter";
2887
+ /**
2888
+ * Drawn in one press-drag-release gesture: points are sampled continuously
2889
+ * along the pointer path rather than placed one click at a time. See
2890
+ * DrawingManager's freehand branch.
2891
+ */
2892
+ readonly freehand = true;
2893
+ /**
2894
+ * Also declared open-ended so the tool still works when
2895
+ * `features.enableForecasting` is off: DrawingManager checks freehand first and
2896
+ * falls through to this click-per-point path when the flag is not set.
2897
+ */
2246
2898
  readonly openEnded = true;
2247
2899
  points: DrawingPoint[];
2248
2900
  complete: boolean;
2249
2901
  style?: AnyDrawingStyle;
2902
+ /**
2903
+ * Last-seen value of `features.enableForecasting`, cached during `render`.
2904
+ *
2905
+ * `hitTest` takes no render context but must agree with what was drawn: a
2906
+ * freehand stroke has no dangling preview point, a click-per-point one does.
2907
+ * A drawing is always rendered before it can be clicked, so the cache is
2908
+ * populated by then; it defaults to the classic behaviour.
2909
+ */
2910
+ private freehandMode;
2250
2911
  constructor(first: DrawingPoint);
2251
- render(ctx: CanvasRenderingContext2D, scale: ScaleManager, viewport: Viewport, priceRange: PriceRange$1, chartW: number, chartH: number, isSelected: boolean, _pricePrecision?: number, theme?: ChartTheme): void;
2912
+ render(ctx: CanvasRenderingContext2D, scale: ScaleManager, viewport: Viewport, priceRange: PriceRange$1, chartW: number, chartH: number, isSelected: boolean, _pricePrecision?: number, theme?: ChartTheme, context?: DrawingRenderContext): void;
2252
2913
  hitTest(x: number, y: number, scale: ScaleManager, viewport: Viewport, priceRange: PriceRange$1, chartW: number, chartH: number): boolean;
2253
2914
  getHandle(x: number, y: number, scale: ScaleManager, viewport: Viewport, priceRange: PriceRange$1, chartW: number, chartH: number): DrawingHandle | null;
2254
2915
  }
@@ -2359,4 +3020,4 @@ declare class WebSocketAdapter implements IWebSocketAdapter {
2359
3020
  private emitStatus;
2360
3021
  }
2361
3022
 
2362
- export { AbcdPattern, AdvancedToolbar, AnchoredNote, AnchoredVP, type AnyDragArea, AnyLevel, Arc, ArrowDown, ArrowMarker, ArrowUp, BatPattern, Brush, ButterflyPattern, Callout, ChartConfig, ChartEngine, ChartEventMap, ChartGroup, ChartState, ChartTheme, Circle, CompactToolbar, CrabPattern, CrossLine, CyclicLines, CypherPattern, DatePriceRange, DateRange, DisjointChannel, DrawingHandle, DrawingManager, DrawingPoint, DrawingToolType, ElliottCombination, ElliottCorrective, ElliottImpulse, ElliottTriangle, ElliottWxy, Ellipse, FibChannel, FibCircles, FibExtension, FibFan, FibProjection, FibRetracement, FibSpiral, FibTimezone, FixedRangeVP, Flag, FlatChannel, GannBox, GannFan, GannSquare, GartleyPattern, GhostFeed, HeadShoulders, Highlighter, HorizontalLine, IDrawing, IIndicator, IWebSocketAdapter, IndicatorParams, InsidePitchfork, LayoutPopover, LayoutSyncState, ModifiedSchiff, OHLCVBar, ParallelChannel, Path, Pitchfork, Polyline, PriceLabel, PriceNote, PriceProjection, PriceRange, PriceRange$1 as PriceRangeType, Projection, Rectangle, RegressionTrend, RotatedRectangle, Ruler, SchiffPitchfork, SerializedDrawing, SeriesType, SharkPattern, SineLine, type SnapshotAction, SnapshotPopover, StreamStatus, Text, ThreeDrives, Tick, TimeCycles, Timeframe, type TradeLevelBracketDragArea, type TradeLevelDragArea, TradeLevelType, TrendAngle, TrendLine, Triangle, TrianglePattern, VerticalLine, Viewport, VolumeProfile, WebSocketAdapter, aggregateCandles };
3023
+ export { AbcdPattern, AccountSpec, AdvancedToolbar, AnchoredNote, AnchoredVP, type AnyDragArea, AnyLevel, Arc, ArrowDown, ArrowMarker, ArrowUp, type BadgeAnchor, type BadgeMetrics, BatPattern, type BracketPnlInput, Brush, type BulkLevelKind, ButterflyPattern, Callout, ChartConfig, ChartEngine, ChartEventMap, ChartGroup, ChartState, ChartTheme, Circle, CompactToolbar, CrabPattern, CrossLine, CyclicLines, CypherPattern, DatePriceRange, DateRange, DisjointChannel, DrawingHandle, DrawingManager, DrawingPoint, DrawingRenderContext, DrawingToolType, Duration, ElliottCombination, ElliottCorrective, ElliottImpulse, ElliottTriangle, ElliottWxy, Ellipse, FibChannel, FibCircles, FibExtension, FibFan, FibProjection, FibRetracement, FibSpiral, FibTimezone, FixedRangeVP, Flag, FlatChannel, GannBox, GannFan, GannSquare, GartleyPattern, GhostFeed, HeadShoulders, Highlighter, HorizontalLine, IDrawing, IIndicator, IWebSocketAdapter, IndicatorParams, InsidePitchfork, InstrumentSpec, LayoutPopover, LayoutSyncState, LongPosition, ModifiedSchiff, OHLCVBar, ParallelChannel, Path, Pitchfork, type PnlPillSummary, Polyline, PositionLevel, type PositionSizeInput, PositionTool, PriceLabel, PriceNote, PriceProjection, PriceRange, PriceRange$1 as PriceRangeType, Projection, Rectangle, RegressionTrend, ResolvedInstrument, RotatedRectangle, Ruler, SchiffPitchfork, SerializedDrawing, SeriesType, SharkPattern, ShortPosition, SineLine, type SnapshotAction, SnapshotPopover, StreamStatus, Text, ThreeDrives, Tick, TimeCycles, Timeframe, type TradeActionBarContext, type TradeLevelBracketDragArea, type TradeLevelDragArea, TradeLevelType, TrendAngle, TrendLine, Triangle, TrianglePattern, VerticalLine, Viewport, VolumeProfile, WebSocketAdapter, aggregateCandles, badgeMetrics, bracketPnl, formatMoney, formatPnlPillValue, positionQty, riskRewardRatio, summarisePositionLevels };