acttrader-charts 1.0.15 → 1.0.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.
package/README.md CHANGED
@@ -55,6 +55,8 @@ const chart = new ChartEngine({
55
55
  // No manual loadData() needed — the engine calls dataLoader on start and on timeframe/duration changes.
56
56
  ```
57
57
 
58
+ If the first fetch returns fewer than `minInitialBars` (default `10`), the engine automatically widens the lookback window and calls `dataLoader` again — up to the `maxLookbackMs` ceiling (default 365 days). This keeps the chart useful on weekends, holidays, or for instruments that just listed. If retries still yield zero bars, the engine shows a **"No data available"** overlay (customise the text via `labels.chart.noData`).
59
+
58
60
  ---
59
61
 
60
62
  ## Advanced Usage
@@ -160,13 +162,17 @@ chart.removeLevelByLabel('ORD-1');
160
162
  chart.setLevels([], 'label', 'price', 'pending'); // clear all of one type
161
163
  ```
162
164
 
165
+ **Visual differentiation:** pending orders and ES/EL entry working orders render as **dashed** lines tinted by side (`pendingBuyLine` green / `pendingSellLine` red). True open positions render as **solid** lines: green/red when `pnl` is set (sign-colored), otherwise `positionLine` (purple/indigo). The info-box border matches the line color. Each true open position also gets a small colored price tag on the right-side price axis showing the entry price — same visual language as the Bid/Ask tag — so you can read the entry price without hunting for the info box.
166
+
163
167
  **TFC events:**
164
168
 
165
169
  ```ts
166
170
  // Confirmed drag of a pending order entry
167
- chart.on('tradeLevelEdit', ({ label, type, changes, isFullscreen }) => {
171
+ chart.on('tradeLevelEdit', ({ label, type, data, changes, newLots, isFullscreen }) => {
168
172
  for (const c of changes) {
169
- if (c.field === 'MAIN') myApi.modifyOrder(label, { price: c.newPrice });
173
+ // `c.newLots` is present on the MAIN change when qty was edited this session.
174
+ // `data.lots` is also overridden with the new qty for convenience.
175
+ if (c.field === 'MAIN') myApi.modifyOrder(label, { price: c.newPrice, qty: c.newLots ?? (data as any).lots });
170
176
  if (c.field === 'SL') myApi.modifyOrder(label, { stopLoss: c.newPrice });
171
177
  if (c.field === 'TP') myApi.modifyOrder(label, { takeProfit: c.newPrice });
172
178
  }
@@ -224,6 +230,7 @@ When `enableTrading` is on and live BID/ASK data is streaming, hovering / activa
224
230
  | `showVolume` | | `true` | Show volume overlay |
225
231
  | `showUI` | | `true` | Render top / bottom / left bars. When `false`, the loading overlay is also suppressed (mobile wrappers provide their own) |
226
232
  | `showDrawingTools` | | `true` | Show drawing toolbar and pencil button |
233
+ | `showFullscreenButton` | | `true` | Show the fullscreen toggle button in the top bar. Set to `false` to hide it entirely. Mobile wrappers (Android / iOS) default this to `false` |
227
234
  | `timeframe` | | `"1D"` | Initial timeframe |
228
235
  | `duration` | | — | Initial active duration button |
229
236
  | `symbol` | | — | Symbol name shown in the top bar |
@@ -233,6 +240,8 @@ When `enableTrading` is on and live BID/ASK data is streaming, hovering / activa
233
240
  | `minLots` | | `1` | Default lot size in the trade popover |
234
241
  | `tickActivityMs` | | `30000` | ms the stream dot stays green after last tick |
235
242
  | `maxCandles` | | `200` | Max bars fetched per data-load request |
243
+ | `minInitialBars` | | `10` | If `dataLoader` returns fewer bars, the fetch window auto-widens and retries (handles weekends, market closures, and sparse symbols) |
244
+ | `maxLookbackMs` | | `31_536_000_000` | Hard ceiling on auto-widening lookback (ms). Retries stop once the window reaches this. Default: 365 days |
236
245
  | `prefetchThreshold` | | `80` | Bars from start of data at which historical fetch triggers (min 20) |
237
246
  | `mobileBarDivisor` | | `2` | Divide desktop visible bar count on touch devices (`2`, `3`, or `4`) |
238
247
  | `momentumScrollEnabled` | | `true` | Enable momentum (kinetic) scrolling — chart coasts after a fast flick |
@@ -487,8 +496,11 @@ chart.prependData(bars: OHLCVBar[]): this // prepend historical bars (infinite
487
496
  chart.correctBar(barTime: number, bar: OHLCVBar): void // replace a bar with authoritative data after close
488
497
  chart.setLoading(loading: boolean): this
489
498
 
490
- // Reset — clears all bars, the live price line, and any in-flight fetch.
491
- // Call before switching to a new symbol so no previous symbol data bleeds in.
499
+ // Reset — clears all bars, the live price line, any in-flight fetch,
500
+ // all user drawings, and all trade/position levels (including pending drafts).
501
+ // Call before switching to a new symbol so no previous symbol state bleeds in.
502
+ // If you only want to clear bars (e.g. refreshing the same symbol), call
503
+ // chart.loadData([]) directly to preserve drawings.
492
504
  chart.resetData(): this
493
505
  ```
494
506
 
@@ -530,12 +542,17 @@ chart.setDrawingTool(tool: DrawingToolType | null): this
530
542
  ```ts
531
543
  chart.setLevels(levels, labelField, priceField, type): this
532
544
  chart.removeLevelByLabel(label: string): this
533
- chart.cancelCurrentEdit(): this // cancel the active draft order or in-progress level edit; no-op when nothing is active
545
+ chart.updateLevelMainPrice(label: string, price: number): this // move an existing level's entry price; stages the edit so it survives subsequent setLevels refreshes until the server echoes the new price or cancelLevelEdit/cancelCurrentEdit is called
546
+ chart.updateLevelBracket(label: string, bracketType: 'sl' | 'tp', price: number | null): this // update or remove a SL/TP bracket on an existing level; same staging semantics as updateLevelMainPrice — pass null to remove
547
+ chart.cancelLevelEdit(label: string): this // drop any staged edit for this level and revert to last confirmed server state
548
+ chart.cancelCurrentEdit(): this // cancel the active draft order or any in-progress level edit; no-op when nothing is active
534
549
  chart.addLevelBracket(label: string, bracketType: 'sl' | 'tp'): this // auto-place a SL or TP bracket at a default offset; emits tradeLevelBracketActivated with the computed price
535
- chart.setDraftBracketPnl(bracketType: 'sl' | 'tp', pnlText: string | null): this // set estimated P&L text on a draft order bracket line; pass null to clear
550
+ chart.setDraftBracketPnl(bracketType: 'sl' | 'tp', pnlText: string | null): this // set estimated P&L text on the active bracket host — the draft order while drafting, or the currently selected existing pending order / position while modifying; pass null to clear
536
551
  chart.setTfcActive(enabled: boolean): this // toggle TFC on/off at runtime; hides/shows all trade levels, draft orders, and floating trade button; fires tfcToggle event
537
552
  ```
538
553
 
554
+ > **Staging semantics for `updateLevelMainPrice` / `updateLevelBracket`.** Calls to these methods register the change in the chart's pending-edit buffer (the same buffer chart-initiated drags use), so the edit stays visible even when the host app keeps pushing fresh server state via `setLevels` (e.g. per-tick PnL refreshes). When the server echoes back the new price in a later `setLevels` call, the staged edit is auto-released (mobile, i.e. `hideLevelConfirmCancel: true`). If a panel closes without submitting, call `cancelLevelEdit(label)` or `cancelCurrentEdit()` to drop the staged edit — otherwise it will keep overriding server state on the chart.
555
+
539
556
  ### Trade Button
540
557
 
541
558
  ```ts
@@ -300,6 +300,8 @@ interface DialogLabels {
300
300
  interface ChartMiscLabels {
301
301
  /** Overlay text shown while data is being fetched. */
302
302
  loading: string;
303
+ /** Overlay text shown when a completed fetch returned zero bars. */
304
+ noData: string;
303
305
  /** Tooltip on the "jump to live edge" button. */
304
306
  scrollToLatest: string;
305
307
  /** Collapse button text in the indicator overlay when more than 2 indicators
@@ -548,6 +550,8 @@ interface ChartConfig {
548
550
  showDrawingTools?: boolean;
549
551
  /** If false, the settings gear button in the top bar is hidden entirely. Default: true */
550
552
  showSettings?: boolean;
553
+ /** If false, the fullscreen toggle button in the top bar is hidden entirely. Default: true */
554
+ showFullscreenButton?: boolean;
551
555
  timeframe?: Timeframe;
552
556
  duration?: Duration;
553
557
  symbol?: string;
@@ -604,10 +608,12 @@ interface ChartConfig {
604
608
  label: string;
605
609
  type: TradeLevelType;
606
610
  data: unknown;
611
+ newLots?: number;
607
612
  changes: Array<{
608
613
  field: 'MAIN' | 'SL' | 'TP' | 'ADD_SL' | 'ADD_TP' | 'REMOVE_SL' | 'REMOVE_TP';
609
614
  newPrice: number;
610
615
  data: unknown;
616
+ newLots?: number;
611
617
  bracketOrderLabel?: string;
612
618
  }>;
613
619
  }) => void;
@@ -675,6 +681,20 @@ interface ChartConfig {
675
681
  * of bars. Default: 200.
676
682
  */
677
683
  maxCandles?: number;
684
+ /**
685
+ * Minimum bars expected from the initial fetch before giving up. If the
686
+ * `dataLoader` returns fewer bars than this, ChartEngine automatically
687
+ * widens the lookback window and retries — this keeps charts useful after
688
+ * weekends, holidays, or for instruments with sparse recent history.
689
+ * Default: 10.
690
+ */
691
+ minInitialBars?: number;
692
+ /**
693
+ * Hard ceiling (in milliseconds) on how far back auto-widening retries
694
+ * can reach when chasing enough bars. Retries stop once the window meets
695
+ * or exceeds this value. Default: 365 days.
696
+ */
697
+ maxLookbackMs?: number;
678
698
  /**
679
699
  * How close (in bars) the viewport must be to the start of loaded data
680
700
  * before a historical fetch is triggered. Higher values prefetch earlier.
@@ -1139,16 +1159,29 @@ type ChartEventMap = {
1139
1159
  tradeLevelEdit: {
1140
1160
  label: string;
1141
1161
  type: TradeLevelType;
1142
- /** Parent level's data. */
1162
+ /**
1163
+ * Parent level's data. When the user edited the lot size during this session,
1164
+ * the `lots` field of this object is overridden with the new value for convenience
1165
+ * (so `data.lots` always reflects the effective qty being submitted). The original
1166
+ * server fields (e.g. `Quantity`) are left untouched — use `newLots` when you need
1167
+ * to distinguish "edited" from "unchanged".
1168
+ */
1143
1169
  data: unknown;
1144
1170
  /** True when the chart is in fullscreen mode (native or CSS) at the time of the action. */
1145
1171
  isFullscreen: boolean;
1172
+ /** Present when the user changed the lot size via the QTY pill flyout during this edit session. */
1173
+ newLots?: number;
1146
1174
  changes: Array<{
1147
1175
  /** MAIN = entry price edit; SL/TP = bracket edit; ADD_SL/ADD_TP = new bracket placed; REMOVE_SL/REMOVE_TP = bracket removed. */
1148
1176
  field: 'MAIN' | 'SL' | 'TP' | 'ADD_SL' | 'ADD_TP' | 'REMOVE_SL' | 'REMOVE_TP';
1149
1177
  newPrice: number;
1150
- /** Bracket order's data when the bracket is a ToClose order; parent's data for inline brackets. */
1178
+ /**
1179
+ * Bracket order's data when the bracket is a ToClose order; parent's data for inline brackets.
1180
+ * On the MAIN change, `lots` is overridden with the new qty when the user edited it (same rule as the top-level `data`).
1181
+ */
1151
1182
  data: unknown;
1183
+ /** Present on the MAIN change when the user edited the lot size during this session. */
1184
+ newLots?: number;
1152
1185
  bracketOrderLabel?: string;
1153
1186
  }>;
1154
1187
  };
@@ -300,6 +300,8 @@ interface DialogLabels {
300
300
  interface ChartMiscLabels {
301
301
  /** Overlay text shown while data is being fetched. */
302
302
  loading: string;
303
+ /** Overlay text shown when a completed fetch returned zero bars. */
304
+ noData: string;
303
305
  /** Tooltip on the "jump to live edge" button. */
304
306
  scrollToLatest: string;
305
307
  /** Collapse button text in the indicator overlay when more than 2 indicators
@@ -548,6 +550,8 @@ interface ChartConfig {
548
550
  showDrawingTools?: boolean;
549
551
  /** If false, the settings gear button in the top bar is hidden entirely. Default: true */
550
552
  showSettings?: boolean;
553
+ /** If false, the fullscreen toggle button in the top bar is hidden entirely. Default: true */
554
+ showFullscreenButton?: boolean;
551
555
  timeframe?: Timeframe;
552
556
  duration?: Duration;
553
557
  symbol?: string;
@@ -604,10 +608,12 @@ interface ChartConfig {
604
608
  label: string;
605
609
  type: TradeLevelType;
606
610
  data: unknown;
611
+ newLots?: number;
607
612
  changes: Array<{
608
613
  field: 'MAIN' | 'SL' | 'TP' | 'ADD_SL' | 'ADD_TP' | 'REMOVE_SL' | 'REMOVE_TP';
609
614
  newPrice: number;
610
615
  data: unknown;
616
+ newLots?: number;
611
617
  bracketOrderLabel?: string;
612
618
  }>;
613
619
  }) => void;
@@ -675,6 +681,20 @@ interface ChartConfig {
675
681
  * of bars. Default: 200.
676
682
  */
677
683
  maxCandles?: number;
684
+ /**
685
+ * Minimum bars expected from the initial fetch before giving up. If the
686
+ * `dataLoader` returns fewer bars than this, ChartEngine automatically
687
+ * widens the lookback window and retries — this keeps charts useful after
688
+ * weekends, holidays, or for instruments with sparse recent history.
689
+ * Default: 10.
690
+ */
691
+ minInitialBars?: number;
692
+ /**
693
+ * Hard ceiling (in milliseconds) on how far back auto-widening retries
694
+ * can reach when chasing enough bars. Retries stop once the window meets
695
+ * or exceeds this value. Default: 365 days.
696
+ */
697
+ maxLookbackMs?: number;
678
698
  /**
679
699
  * How close (in bars) the viewport must be to the start of loaded data
680
700
  * before a historical fetch is triggered. Higher values prefetch earlier.
@@ -1139,16 +1159,29 @@ type ChartEventMap = {
1139
1159
  tradeLevelEdit: {
1140
1160
  label: string;
1141
1161
  type: TradeLevelType;
1142
- /** Parent level's data. */
1162
+ /**
1163
+ * Parent level's data. When the user edited the lot size during this session,
1164
+ * the `lots` field of this object is overridden with the new value for convenience
1165
+ * (so `data.lots` always reflects the effective qty being submitted). The original
1166
+ * server fields (e.g. `Quantity`) are left untouched — use `newLots` when you need
1167
+ * to distinguish "edited" from "unchanged".
1168
+ */
1143
1169
  data: unknown;
1144
1170
  /** True when the chart is in fullscreen mode (native or CSS) at the time of the action. */
1145
1171
  isFullscreen: boolean;
1172
+ /** Present when the user changed the lot size via the QTY pill flyout during this edit session. */
1173
+ newLots?: number;
1146
1174
  changes: Array<{
1147
1175
  /** MAIN = entry price edit; SL/TP = bracket edit; ADD_SL/ADD_TP = new bracket placed; REMOVE_SL/REMOVE_TP = bracket removed. */
1148
1176
  field: 'MAIN' | 'SL' | 'TP' | 'ADD_SL' | 'ADD_TP' | 'REMOVE_SL' | 'REMOVE_TP';
1149
1177
  newPrice: number;
1150
- /** Bracket order's data when the bracket is a ToClose order; parent's data for inline brackets. */
1178
+ /**
1179
+ * Bracket order's data when the bracket is a ToClose order; parent's data for inline brackets.
1180
+ * On the MAIN change, `lots` is overridden with the new qty when the user edited it (same rule as the top-level `data`).
1181
+ */
1151
1182
  data: unknown;
1183
+ /** Present on the MAIN change when the user edited the lot size during this session. */
1184
+ newLots?: number;
1152
1185
  bracketOrderLabel?: string;
1153
1186
  }>;
1154
1187
  };