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/README.md +405 -16
- package/dist/{MACD-D1i-sEFR.d.cts → MACD-B6SoR6iH.d.cts} +486 -18
- package/dist/{MACD-D1i-sEFR.d.ts → MACD-B6SoR6iH.d.ts} +486 -18
- package/dist/index.cjs +3792 -448
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +679 -18
- package/dist/index.d.ts +679 -18
- package/dist/index.js +3702 -374
- package/dist/index.js.map +1 -1
- package/dist/indicators/index.d.cts +2 -2
- package/dist/indicators/index.d.ts +2 -2
- package/dist/webview/chart.html +90 -82
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -58,12 +58,15 @@ chart.on('durationChange', ({ duration, timeframe }) => {
|
|
|
58
58
|
```ts
|
|
59
59
|
const chart = new ChartEngine({
|
|
60
60
|
container,
|
|
61
|
-
dataLoader: async ({ start, end, interval }) => {
|
|
62
|
-
|
|
61
|
+
dataLoader: async ({ start, end, interval, priceSource }) => {
|
|
62
|
+
// `priceSource` ('bid' | 'ask' | 'ltp') is the candle source the chart is showing —
|
|
63
|
+
// pass it to your history API so the bars match (dealing feeds: LTP vs BID candles).
|
|
64
|
+
const res = await fetch(`/api/bars?from=${start.getTime()}&to=${end.getTime()}&tf=${interval}&source=${priceSource}`);
|
|
63
65
|
return res.json(); // OHLCVBar[]
|
|
64
66
|
},
|
|
65
67
|
});
|
|
66
|
-
// No manual loadData() needed — the engine calls dataLoader on start
|
|
68
|
+
// No manual loadData() needed — the engine calls dataLoader on start, on timeframe/duration
|
|
69
|
+
// changes, and (unless `reloadOnPriceSourceChange: false`) whenever the price source changes.
|
|
67
70
|
```
|
|
68
71
|
|
|
69
72
|
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`).
|
|
@@ -152,6 +155,9 @@ const dealingChart = new ChartEngine({
|
|
|
152
155
|
container,
|
|
153
156
|
// "Show chart by" toggle: 'ltp' | 'ask' | 'bid' (candle close/high/low source)
|
|
154
157
|
tickClosePriceSource: 'ltp',
|
|
158
|
+
// Header dropdown so the user can switch the source on the chart itself
|
|
159
|
+
// (rendered next to the timeframe dropdown; omit to hide):
|
|
160
|
+
priceSourceSelector: ['ltp', 'bid'],
|
|
155
161
|
// Chart line checkboxes — each price line is independent:
|
|
156
162
|
showAskLine: true, // Show AskLine
|
|
157
163
|
showBidLine: true, // Show BidLine
|
|
@@ -161,8 +167,15 @@ const dealingChart = new ChartEngine({
|
|
|
161
167
|
dealingChart.setShowAskLine(false); // hide the ask line
|
|
162
168
|
dealingChart.setShowBidLine(true); // show the bid line
|
|
163
169
|
dealingChart.setShowLtpPrice(true); // show the LTP line
|
|
170
|
+
// Switch the candle source programmatically (e.g. from a trade-settings page);
|
|
171
|
+
// the header dropdown label follows automatically:
|
|
172
|
+
dealingChart.setTickClosePriceSource('bid');
|
|
173
|
+
// A user pick from the dropdown emits `priceSourceChange` — persist it:
|
|
174
|
+
dealingChart.on('priceSourceChange', ({ source }) => saveTradeSetting(source));
|
|
164
175
|
```
|
|
165
176
|
|
|
177
|
+
**History follows the source.** Switching the source (dropdown pick or `setTickClosePriceSource()`) does not just change how live ticks extend the last candle — the engine re-runs your `dataLoader` with `params.priceSource` set to the new value and replaces the loaded bars, keeping the visible time window. Forward `priceSource` to your charting API (e.g. as a query parameter) so it returns LTP-built or BID-built candles accordingly; without it the API keeps serving the old source and the chart cannot change. Set `reloadOnPriceSourceChange: false` to opt out of the re-fetch.
|
|
178
|
+
|
|
166
179
|
### Compare symbols
|
|
167
180
|
|
|
168
181
|
Overlay one or more comparison instruments on the main chart, normalized to
|
|
@@ -249,6 +262,12 @@ chart.setLevels(
|
|
|
249
262
|
);
|
|
250
263
|
```
|
|
251
264
|
|
|
265
|
+
**Trailing stop:** a trade with a trail always has a working stop, so keep passing its price as `stopLossPrice` and set `stopLossTrailing: true`. The line renders as usual but its pill reads **TSL** instead of SL, and it is read-only on the chart — no drag handle, no × — because the trail is a pip distance edited from the host's own panel. The level's TP stays fully editable.
|
|
266
|
+
|
|
267
|
+
```ts
|
|
268
|
+
{ label: 'POS-2', price: 1.0850, side: 'buy', lots: 1, stopLossPrice: 1.0800, stopLossTrailing: true }
|
|
269
|
+
```
|
|
270
|
+
|
|
252
271
|
**Bracket orders via `ToClose`** — link a separate SL/TP pending order to a parent level:
|
|
253
272
|
|
|
254
273
|
```ts
|
|
@@ -333,13 +352,15 @@ When `enableTrading` is on and live BID/ASK data is streaming, hovering / activa
|
|
|
333
352
|
|
|
334
353
|
### Horizontal (time-axis) order-line dragging
|
|
335
354
|
|
|
336
|
-
Opt-in via `features.orderLineTimeDrag` (off by default; enable it per broker from the host app — e.g. only for Hankotrade users). When enabled, any level flagged `timeDraggable: true` can have its **info-box badge** dragged left/right to re-anchor the order to a different candle.
|
|
355
|
+
Opt-in via `features.orderLineTimeDrag` (off by default; enable it per broker from the host app — e.g. only for Hankotrade users). When enabled, any level flagged `timeDraggable: true` — open positions **and pending orders** alike — can have its **info-box badge** dragged left/right to re-anchor the order to a different candle. During a horizontal drag the **price line stays locked** — only the time anchor moves. A fixed-price position's badge grabs horizontally at once (`grab` / `grabbing` cursor). A pending order (or entry-editable position) keeps its price drag: its badge shows a `move` cursor and the **first movement decides the axis** — sideways moves the time anchor, up/down moves the entry price exactly as before, and the full-width line still drags vertically.
|
|
337
356
|
|
|
338
357
|
```ts
|
|
339
358
|
const chart = new ChartEngine({
|
|
340
359
|
container,
|
|
341
360
|
features: { orderLineTimeDrag: true }, // host enables this only for the gated broker
|
|
342
361
|
orderLineDragSnap: true, // snap to nearest candle on release (default)
|
|
362
|
+
orderLineAnchorPersistence: true, // remember dropped positions in localStorage (default)
|
|
363
|
+
orderLineDefaultAnchor: 'center', // un-dragged badges start mid-chart (default: 'timestamp')
|
|
343
364
|
});
|
|
344
365
|
|
|
345
366
|
chart.setLevels(
|
|
@@ -354,13 +375,101 @@ chart.on('orderLineMoved', ({ label, fromTimestamp, toTimestamp, data }) => {
|
|
|
354
375
|
```
|
|
355
376
|
|
|
356
377
|
- **Bounds:** the badge is clamped to the visible viewport — it can't be dragged past the left/right edge.
|
|
378
|
+
- **Pending orders:** flag them `timeDraggable: true` too. The pill follows the drag direction — a sideways pull re-anchors the order in time (price locked, `orderLineMoved` on release), a vertical pull moves the entry price and its SL/TP brackets as usual (`tradeLevelDrag` / `tradeLevelEdit`). The axis is fixed after about 6px of movement and does not change mid-drag. Touch follows the same rule, and a tap that never moves still opens the edit form.
|
|
357
379
|
- **Snap:** controlled by `orderLineDragSnap` (default `true`); set `false` to keep the exact released time.
|
|
358
380
|
- **Escape / touch-cancel:** reverts the badge to its starting candle without emitting `orderLineMoved`.
|
|
359
381
|
- **Stays put after release:** the chart holds the dropped position across `setLevels` refreshes until you echo the new `timestamp` back (auto-released on match).
|
|
382
|
+
- **Survives reloads:** with `orderLineAnchorPersistence` (default `true`) the dropped candle time is saved in `localStorage` under `finchart.orderLineTimeAnchors`, keyed by the level's label. After a page reload the badge returns to that candle even if your `setLevels()` payload still carries the order's original `timestamp` — the persisted anchor wins until the badge is dragged again. Capped at the 500 most recently dragged labels; set `false` if you persist anchors server-side instead.
|
|
383
|
+
- **New orders at the center:** with `orderLineDefaultAnchor: 'center'` a `timeDraggable` level that has never been dragged ignores its `timestamp` and renders mid-chart — so a freshly filled market order arrives in the middle of the chart rather than pinned to the latest candle at the right edge. Levels that are not `timeDraggable` still honor `timestamp`. Default `'timestamp'` keeps the previous behavior.
|
|
360
384
|
- A level **without** `timestamp` renders exactly as before (badge centered), so existing consumers are unaffected.
|
|
361
385
|
|
|
362
386
|
---
|
|
363
387
|
|
|
388
|
+
### Trade action bar — `features.tradeActionBar`
|
|
389
|
+
|
|
390
|
+
With the flag on, the floating trade button opens a segmented bar at the clicked
|
|
391
|
+
price instead of the two-row Buy/Sell popup:
|
|
392
|
+
|
|
393
|
+
```
|
|
394
|
+
Alert | Pending | SL | TP | TSL | ×
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
- No price readout — the axis already shows the price at that point.
|
|
398
|
+
- **Alert** calls `onAddAlert(price)`. The chip is only rendered when that callback is set. The same callback also backs the **Alert** entry of the advanced header's ⋯ menu (`headerOverflowMenu`), which passes the current market price (live ask, else bid, else last close) so the host opens the same form.
|
|
399
|
+
- **Pending** opens a sub-menu just above the bar — an `Above · 1.17586` / `Below · …`
|
|
400
|
+
header, then the two orders that make sense at that price (`Buy Limit` / `Sell Stop`
|
|
401
|
+
below the market, `Sell Limit` / `Buy Stop` above), each with its `@ price`. Picking a
|
|
402
|
+
row creates the draft order exactly as before and closes both.
|
|
403
|
+
- **SL / TP / TSL** emit `bulkLevelsRequested` with `{ kind, price }` and close the
|
|
404
|
+
bar — the host opens its Bulk Levels dialog pre-focused on that kind. The chart
|
|
405
|
+
never calls a broker. The chips grey out, with a tooltip saying why, when the
|
|
406
|
+
symbol has no open positions or has buys and sells open at once — one stop or
|
|
407
|
+
target price cannot sit on the right side of the market for both directions.
|
|
408
|
+
- **×**, a click elsewhere, Escape, TFC off and `dismissAllUI()` all close it.
|
|
409
|
+
|
|
410
|
+
Chip labels and the × tooltip live under `labels.trade` (`actionAlert`, `actionPending`,
|
|
411
|
+
`actionSl`, `actionTp`, `actionTsl`, `actionCloseTitle`).
|
|
412
|
+
|
|
413
|
+
### On-chart P/L pill — `features.pnlPill`
|
|
414
|
+
|
|
415
|
+
A floating summary of the open positions on the chart, for the trader who wants
|
|
416
|
+
the total without opening the positions panel:
|
|
417
|
+
|
|
418
|
+
```
|
|
419
|
+
⋮⋮ 3 POS +72.65 USD | ◎ ✕
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
- **`3 POS`** — how many position levels are on the chart. Pending orders,
|
|
423
|
+
position-style entry orders (`entryPriceEditable`) and an unconfirmed draft
|
|
424
|
+
are not counted.
|
|
425
|
+
- **`+72.65 USD`** — the sum of the numeric `pnl` those levels carry, i.e. the
|
|
426
|
+
`pnlKey` column you pass to `setLevels`. The chart computes no money itself;
|
|
427
|
+
while no position has a `pnl` yet the pill shows `—`. Profit is drawn in the
|
|
428
|
+
buy colour, loss in the sell colour.
|
|
429
|
+
- **Click the body** (the grip, the count or the amount) to fan every position
|
|
430
|
+
badge out on the chart — clustered levels expand, and TFC is switched on if it
|
|
431
|
+
was off — with an accent ring on the pill while they are shown. Click again to
|
|
432
|
+
collapse.
|
|
433
|
+
- **◎** (the same target glyph as the Bulk Levels dialog header) emits
|
|
434
|
+
`bulkLevelsRequested` with `source: 'pill'` and a `null` price, so your Bulk Levels
|
|
435
|
+
dialog opens pre-scoped to every open position on the symbol. Tooltip:
|
|
436
|
+
"Set SL · TP · TSL on all N {symbol} positions". Greyed out while buys and sells are
|
|
437
|
+
both open ("Bulk levels need all N {symbol} positions in one direction").
|
|
438
|
+
- **✕** (amber) emits `pnlPillCloseAll` with every counted position. Your app
|
|
439
|
+
confirms and closes them; the chart never calls a broker.
|
|
440
|
+
|
|
441
|
+
The pill sits top-right of the plot, clear of the price axis. Drag it anywhere
|
|
442
|
+
inside the plot; double-click puts it back. It hides itself while the chart has
|
|
443
|
+
no positions and follows the chart theme. Every element sits on one shared row
|
|
444
|
+
height, and the grip and both buttons have fixed widths, so a long or negative
|
|
445
|
+
amount never shifts or squeezes them.
|
|
446
|
+
|
|
447
|
+
With the feature on, the header (advanced toolbar and default top bar) also gets a
|
|
448
|
+
**P/L** switch — wallet icon, `P/L`, the same 24px `rounded-sm` button tinted while the
|
|
449
|
+
pill is shown. It hides or shows the pill on the user's say-so, wins over the position
|
|
450
|
+
count, and emits `pnlPillToggle` so you can persist the choice; `pnlPill.visible` seeds
|
|
451
|
+
it and `setPnlPillVisible()` drives it from code.
|
|
452
|
+
|
|
453
|
+
```ts
|
|
454
|
+
const chart = new ChartEngine({
|
|
455
|
+
container,
|
|
456
|
+
features: { pnlPill: true },
|
|
457
|
+
pnlPill: { currency: 'USD' }, // → "+72.65 USD"; or format: (pnl) => myMoney(pnl)
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
chart.setLevels(positions, 'TradeID', 'Price', 'position', 'pnl', 'pnlText');
|
|
461
|
+
|
|
462
|
+
chart.on('pnlPillCloseAll', async ({ count, pnl, data }) => {
|
|
463
|
+
if (!(await confirmDialog(`Close all ${count} positions?`))) return;
|
|
464
|
+
for (const row of data) closeTrade(row);
|
|
465
|
+
});
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
Labels live under `labels.pnlPill`: `positionsAbbrev` (`'POS'`), `showPositionsTitle`,
|
|
469
|
+
`hidePositionsTitle`, `bulkLevelsTitle`, `closeAllTitle` — titles may
|
|
470
|
+
use `{symbol}` and `{count}`. The maths behind the pill is exported as
|
|
471
|
+
`summarisePositionLevels(levels, draftLabel?)` and `formatPnlPillValue(pnl, currency?)`.
|
|
472
|
+
|
|
364
473
|
## Default Configuration
|
|
365
474
|
|
|
366
475
|
### `ChartConfig`
|
|
@@ -375,9 +484,13 @@ chart.on('orderLineMoved', ({ label, fromTimestamp, toTimestamp, data }) => {
|
|
|
375
484
|
| `hideHeader` | | `false` | Hide only the chart header (TopBar / AdvancedToolbar / CompactToolbar, per `headerLayout`). Bottom bar, left drawing tools, and on-canvas overlays remain on their own flags. Drive the chart from your own UI via `setTimeframe(tf)`, `setSeries(type)`, `addIndicatorByName(name)`, `removeIndicator(name)` |
|
|
376
485
|
| `showDrawingTools` | | `true` | Show drawing toolbar and pencil button |
|
|
377
486
|
| `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` |
|
|
378
|
-
| `timeframe` | | `"1D"` | Initial timeframe |
|
|
487
|
+
| `timeframe` | | `"1D"` | Initial timeframe. Values: `1m` `3m` `4m` `5m` `10m` `15m` `30m` `1h` `2h` `4h` `8h` `1D` `1W` `1M`; `3m` / `4m` / `10m` / `30m` are requested from `dataLoader` as `3min` / `4min` / `10min` / `30min`. If your API lacks them, point `aggregateFrom` at a finer interval it does serve, e.g. `{ '3m': '1m', '4m': '1m', '10m': '5m', '30m': '15m' }` — the chart merges the candles |
|
|
488
|
+
| `timeframes` | | classic set | Timeframes offered in the header, in order: the advanced row's pills, the classic dropdown, and the rest of the reference header's ⌄ menu after its six fixed pills. Omit to keep `1m 5m 15m 1h 4h 1D 1W` (row) / `… 30m 2h 8h 1M` (dropdown, ⌄), so the newer minute timeframes only appear for hosts that list them |
|
|
379
489
|
| `duration` | | — | Initial active duration button |
|
|
380
490
|
| `symbol` | | — | Symbol name shown in the top bar |
|
|
491
|
+
| `features` | | `{}` | Opt-in feature gates. See [Feature flags](#feature-flags) |
|
|
492
|
+
| `instrument` | | — | Contract specs for `symbol` — pip size, contract size, money conversion. See [`InstrumentSpec`](#instrumentspec) |
|
|
493
|
+
| `account` | | — | Account equity and per-trade risk used to size the position tools. See [`AccountSpec`](#accountspec) |
|
|
381
494
|
| `isins` | | — | Symbol list for the picker modal |
|
|
382
495
|
| `onIsinSelect` | | — | Called when user picks a symbol from the picker modal |
|
|
383
496
|
| `padding` | | `{top:8,right:0,bottom:0,left:0}` | Canvas padding (px) |
|
|
@@ -394,7 +507,7 @@ chart.on('orderLineMoved', ({ label, fromTimestamp, toTimestamp, data }) => {
|
|
|
394
507
|
| `momentumMaxVelocity` | | `6.0` | Max launch velocity (px/ms) — caps hard-flick speed |
|
|
395
508
|
| `targetCandleWidth` | | `10` | Target px width per candle for auto-calculating initial bar count |
|
|
396
509
|
| `durationTimeframeMap` | | *(see below)* | Override duration → timeframe pairings |
|
|
397
|
-
| `dataLoader` | | — | `(
|
|
510
|
+
| `dataLoader` | | — | `({ start, end, interval, priceSource }) => Promise<OHLCVBar[]>` auto-called on load, timeframe/duration change, and price-source change. `priceSource` is the candle source to fetch (`'bid'`, `'ask'` or `'ltp'`) |
|
|
398
511
|
| `compareDataLoader` | | — | `({ symbol, start, end, interval }) => Promise<OHLCVBar[]>` — fetches bars for a compare symbol. Set this to enable the library-owned Compare flow. |
|
|
399
512
|
| `initialCompares` | | — | Symbols to auto-add as compares once the initial primary range is loaded |
|
|
400
513
|
| `maxCompares` | | `8` | Maximum concurrent compare symbols. Adding beyond emits `compareError` |
|
|
@@ -403,6 +516,8 @@ chart.on('orderLineMoved', ({ label, fromTimestamp, toTimestamp, data }) => {
|
|
|
403
516
|
| `labels` | | `DEFAULT_LABELS` | Deep-partial string overrides for i18n/translation |
|
|
404
517
|
| `tickClosePriceSource` | | `"bid"` | Which price drives live tick close/high/low: `"bid"`, `"ask"`, or `"ltp"` (build candles from the last traded price — exchange/dealing feeds; ticks without a valid LTP fall back to the bid) |
|
|
405
518
|
| `showLtpPrice` | | unset | Show the LTP marker (dashed price line + axis tag). Unset: shown only in `"ltp"` mode. `true`: always shown when the feed supplies an LTP. `false`: hidden even in `"ltp"` mode (candles still build from the LTP). Toggle at runtime with `chart.setShowLtpPrice(show)` |
|
|
519
|
+
| `priceSourceSelector` | | unset | Show a price-source dropdown in the chart header listing the given sources, e.g. `["ltp", "bid"]` (dealing feeds). A user pick switches the candle source, re-fetches history via `dataLoader` (see `reloadOnPriceSourceChange`) and emits `priceSourceChange`; sync it programmatically with `chart.setTickClosePriceSource(source)`. Hidden when unset/empty. Default header layout only |
|
|
520
|
+
| `reloadOnPriceSourceChange` | | `true` | Re-run `dataLoader` with the new `params.priceSource` whenever the candle price source changes, so historical candles are rebuilt from the selected price (LTP vs BID) and not only the live one. The visible time window is preserved. Set `false` to keep only live ticks following the new source |
|
|
406
521
|
| `showBidAskLines` | | `false` | **Deprecated** — show both bid and ask as dashed lines during a live stream. Prefer the per-line checks `showAskLine` / `showBidLine`, which override it when set |
|
|
407
522
|
| `showAskLine` | | unset | Show the Ask price line (dashed line + axis tag) independently. Unset: legacy `showBidAskLines` behavior. Toggle at runtime with `chart.setShowAskLine(show)` |
|
|
408
523
|
| `showBidLine` | | unset | Show the Bid price line (dashed line + axis tag) independently. Unset: legacy `showBidAskLines` behavior. Toggle at runtime with `chart.setShowBidLine(show)` |
|
|
@@ -412,14 +527,24 @@ chart.on('orderLineMoved', ({ label, fromTimestamp, toTimestamp, data }) => {
|
|
|
412
527
|
| `showPriceAxisCountdown` | | `false` | Show candle countdown timer on the right price axis, just below the live price tag. Honours `candleCountdownTimeframes`. Toggleable from the Settings dialog (Appearance tab). |
|
|
413
528
|
| `maxSubPanes` | | `3` | Max simultaneous oscillator sub-panes |
|
|
414
529
|
| `tradeDisplayFilter` | | `"all"` | Which TFC levels are visible: `"all"` · `"positions"` · `"orders"` · `"none"` |
|
|
530
|
+
| `pnlPill` | | — | `{ currency?, format?, visible? }` for the on-chart P/L pill — currency code appended to the amount, or your own formatter. Only read when `features.pnlPill` is on |
|
|
415
531
|
| `positionRenderStyle` | | auto | Force position render style: `"line"` or `"dot"` |
|
|
416
532
|
| `hideLevelConfirmCancel` | | `false` | Hide on-canvas ✓/✗ confirm-cancel buttons for TFC level edits |
|
|
417
533
|
| `deselectActiveOnOutsideClick` | | `false` | When `true`, clicking/tapping anywhere outside a selected trade level dismisses it (reverting any pending edits, mirroring ✗ Cancel). Default `false` keeps the level active so incidental clicks — price-axis resize, taps outside the QTY input — don't drop an in-progress edit. The level can still be dismissed via ✓/✗, tapping the level again, or `setLevels()` removing it |
|
|
418
534
|
| `showTradeLevelsAlways` | | `false` | Always render SL/TP bracket lines + price pills, even when the parent level is not hovered or selected. The close (×) button stays hover-only so the chart isn't cluttered. Toggleable from the Settings dialog (Trading tab). Persisted in `localStorage`. |
|
|
535
|
+
| `revealNewBrackets` | | `false` | When a level gains a **new** SL/TP — placed from the host's order panel, so it arrives via `setLevels()`, `updateLevelBracket()` or `updateDraftOrderBracket()` — whose price sits outside the visible price range, widen (compress) the price axis so the new line comes into view together with the candles already on screen. Animated, and the trader can drag or reset the axis afterwards as usual. A bracket dragged on the chart is visible by construction and never triggers it. Inert in compare (percent) mode |
|
|
419
536
|
| `tradeLevelButtonScale` | | `1` | Multiplier for trade-level Confirm/Cancel/Edit/Close button radii and gaps. Scales visuals **and** hit/drag areas together — raise it on touch devices for larger tap targets. Clamped to `[1, 3]`. Also settable at runtime via `chart.setTradeLevelButtonScale(scale)` |
|
|
537
|
+
| `tradeLevelBadgeScale` | | `1` | Shrinks the on-chart position / order tag — font, padding, grip, ✎ × buttons and their hit areas together. `0.6` is about half the default area. Clamped `[0.5, 1]` |
|
|
538
|
+
| `tradeLevelBadgeAnchor` | | `"auto"` | `"left"` homes the tag at the plot's left edge like an axis label, whatever the open price or time anchor, so it never covers candles until the trader drags it. With `features.orderLineTimeDrag` the tag stays draggable along the time axis and the drop is remembered as before |
|
|
420
539
|
| `tfcEnabled` | | `true` | Enable the TFC toggle button in the top bar. When `false`, TFC is completely disabled — the toggle button is hidden and all trade levels, draft orders, and the floating trade button are suppressed |
|
|
421
|
-
| `
|
|
540
|
+
| `crosshairEnabled` | | `true` | Draw the mouse crosshair. `false` hides it **and** the floating "place order at this price" button that rides on its horizontal line, ignores mirrored `setCrosshair()` calls and keeps the mobile long-press crosshair from arming. Trade levels, drawings and TFC are unaffected. Runtime: `chart.setCrosshairEnabled(enabled)` |
|
|
541
|
+
| `enableCrossHairHeader` | | `false` | Crosshair on/off switch in the header (advanced toolbar, default top bar, the compact per-pane strip and the mobile header): the reference `rounded-sm` button. The crosshair is on by default, so the icon starts tinted; a click hides the crosshair (and the trade button riding on it) and drops the icon to its plain state, the next click brings both back. Calls `setCrosshairEnabled()` and emits `crosshairToggle` so the host can persist the choice — see [Header crosshair switch](#header-crosshair-switch--enablecrosshairheader) |
|
|
542
|
+
| `enableCrosshairToggle` | | `false` | **Deprecated** — alias of `enableCrossHairHeader`; either flag renders the switch |
|
|
543
|
+
| `headerOverflowMenu` | | `false` | Advanced header only: the reference Hanko header. A 32px bar with six lowercase timeframe pills (`1m 5m 15m 1h 4h 1d`) and a ⌄ for the rest, then crosshair · P/L · Layout (grid icon) · ⋯ · fullscreen at 24px. Draw, Chart type, Indicators, Compare, Snapshot and Chart settings fold into the ⋯ menu; each keeps its behaviour. With `onAddAlert` set the menu also offers **Alert** (own group, before Snapshot) at the current market price |
|
|
544
|
+
| `features.orderLineTimeDrag` | | `false` | Enable **horizontal (time-axis) order-line dragging**. A level with `timeDraggable: true` (position or pending order) can have its info-box badge dragged left/right to re-anchor the order to a different candle — **price stays locked** while dragging sideways. Pending orders keep their vertical price drag: the badge's first movement picks the axis. Emits `orderLineMoved` on release. Off by default; intended to be enabled per broker (e.g. Hankotrade) by the host app |
|
|
422
545
|
| `orderLineDragSnap` | | `true` | When horizontal dragging is enabled, snap the badge to the nearest candle on release. Set `false` to keep the exact released time |
|
|
546
|
+
| `orderLineAnchorPersistence` | | `true` | When horizontal dragging is enabled, remember where each badge was dropped in `localStorage` (key `finchart.orderLineTimeAnchors`, keyed by level label) so it comes back to the same candle after a reload. A persisted anchor overrides the level's `timestamp` until the badge is dragged again. Set `false` to persist anchors yourself |
|
|
547
|
+
| `orderLineDefaultAnchor` | | `'timestamp'` | Where an un-dragged `timeDraggable` badge sits: `'timestamp'` = over the candle at the level's `timestamp`; `'center'` = the chart's horizontal center (ignores `timestamp`), so a new market order lands mid-chart. Non-draggable levels always honor `timestamp` |
|
|
423
548
|
| `levelClusteringEnabled` | | `true` | Enable trade-level fan-out clustering; overlapping levels group into expandable badges |
|
|
424
549
|
| `clusterThresholdDistance` | | `20` | Pixel proximity threshold for clustering (only when `levelClusteringEnabled` is `true`) |
|
|
425
550
|
| `hideSymbolAndTick` | | `false` | Hide the symbol name and tick-activity (streaming) dot in the top-left overlay. Does **not** affect the OHLC(V) strip — use `hideOHLCV` for that |
|
|
@@ -430,13 +555,120 @@ chart.on('orderLineMoved', ({ label, fromTimestamp, toTimestamp, data }) => {
|
|
|
430
555
|
| `quantityFieldConfig` | | — | Constraints for the draft order QTY field (only relevant when `showQuantityField` is `true`). Object: `{ minLots?: number, maxLots?: number }`. `minLots` also sets the initial quantity and the step value for the flyout input (default: `1`). `maxLots` caps the input (default: `100`) |
|
|
431
556
|
| `tradesThresholdForHorizontalLine` | | `2` | Level count above which render auto-switches to `"dot"` mode |
|
|
432
557
|
| `timezone` | | `"UTC"` | IANA timezone string for time-axis and crosshair labels. `"UTC"` (default), `"local"` (browser/device timezone), or any IANA string (`"America/New_York"`, `"Europe/London"`, etc.) |
|
|
433
|
-
| `canvasColors` | | — | Per-theme
|
|
558
|
+
| `canvasColors` | | — | Per-theme colour overrides for the **canvas only** (persisted from the Settings dialog; runtime: `chart.setCanvasColors()`). The surface picks — `background`, `grid`, `axisText`, `axisBorder`, `crosshair` — repaint the chart canvas and leave the chart chrome (top/bottom/left bars, dialogs, popovers) on the theme from `themeOverrides`. Series picks (`candleUp`/`candleDown`/`wickUp`/`wickDown`/`borderUp`/`borderDown`/`volumeUp`/`volumeDown`) apply everywhere, so legends and indicator pills keep tracking them |
|
|
434
559
|
| `aggregateFrom` | | — | Fetch finer-grained data and aggregate client-side per timeframe |
|
|
435
560
|
| `onOrderSubmit` | | — | Called when user submits a trade via the floating button |
|
|
436
561
|
| `onLevelEdit` | | — | Called when user confirms a TFC level edit |
|
|
437
562
|
| `onLotsChange` | | — | Called when user changes qty on a draft order |
|
|
438
563
|
| `onLevelDragEnd` | | — | *(deprecated — use `onLevelEdit`)* |
|
|
439
564
|
|
|
565
|
+
### `InstrumentSpec`
|
|
566
|
+
|
|
567
|
+
The chart reads prices, never contract specs — so a tool that reports a distance
|
|
568
|
+
in **pips**, or converts one to money, has to be told how. Pass `instrument` at
|
|
569
|
+
construction and swap it with `setInstrument()` whenever the symbol changes.
|
|
570
|
+
|
|
571
|
+
```ts
|
|
572
|
+
const chart = new ChartEngine({
|
|
573
|
+
container,
|
|
574
|
+
symbol: 'EURUSD',
|
|
575
|
+
instrument: {
|
|
576
|
+
pipSize: 0.0001, // 0.01 for JPY crosses
|
|
577
|
+
contractSize: 100_000, // units per lot
|
|
578
|
+
valuePerPoint: 1, // account-currency value of one price unit per unit
|
|
579
|
+
currencySymbol: '$',
|
|
580
|
+
},
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
chart.setSymbol('USDJPY').setInstrument({ pipSize: 0.01, contractSize: 100_000 });
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
| Field | Default | Description |
|
|
587
|
+
|---|---|---|
|
|
588
|
+
| `pipSize` | inferred | Price distance counted as one pip |
|
|
589
|
+
| `contractSize` | `1` | Units per lot (`100` for XAUUSD, `100000` for most FX pairs) |
|
|
590
|
+
| `valuePerPoint` | `1` | Account-currency value of one price unit per contract unit |
|
|
591
|
+
| `currencySymbol` | `'$'` | Prefixed to money figures |
|
|
592
|
+
|
|
593
|
+
**Every field is optional and the whole block can be omitted** — tools fall back
|
|
594
|
+
to price-only readouts, so existing integrations keep working untouched.
|
|
595
|
+
|
|
596
|
+
When `pipSize` is absent it is inferred from how many decimals the feed quotes:
|
|
597
|
+
five-decimal (`1.08531`) and three-decimal (`151.234`) feeds carry fractional
|
|
598
|
+
pips, so the pip is the second-to-last digit; two- and four-decimal feeds quote
|
|
599
|
+
whole pips. **That convention is wrong for metals, indices and crypto** — pass
|
|
600
|
+
`pipSize` explicitly if pips matter.
|
|
601
|
+
|
|
602
|
+
The same fallbacks are exported for hosts that want to compute alongside the
|
|
603
|
+
chart:
|
|
604
|
+
|
|
605
|
+
```ts
|
|
606
|
+
import { resolveInstrument, inferPipSize, toPips, toMoney } from 'acttrader-charts';
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
> Per-position values still travel on the individual `TradeLevel`
|
|
610
|
+
> (`contractSize` / `valuePerPoint`). `InstrumentSpec` is the chart-wide default
|
|
611
|
+
> for tools that aren't attached to a position.
|
|
612
|
+
|
|
613
|
+
### Feature flags
|
|
614
|
+
|
|
615
|
+
Newer tools ship behind `ChartConfig.features` so each host opts in. **Every flag
|
|
616
|
+
defaults to `false`** — a chart that sets none behaves exactly as it did before
|
|
617
|
+
the flag existed, including its toolbar contents.
|
|
618
|
+
|
|
619
|
+
```ts
|
|
620
|
+
const chart = new ChartEngine({
|
|
621
|
+
container,
|
|
622
|
+
features: { enableForecasting: true },
|
|
623
|
+
});
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
| Flag | Default | What it changes |
|
|
627
|
+
|---|---|---|
|
|
628
|
+
| `enableForecasting` | `false` | The reworked drawing tools, as one switch — see below |
|
|
629
|
+
| `orderLineTimeDrag` | `false` | Horizontal (time-axis) order-line dragging for positions and pending orders — see [Horizontal order-line dragging](#horizontal-time-axis-order-line-dragging) |
|
|
630
|
+
| `pnlPill` | `false` | Floating on-chart P/L pill — count and summed P/L of the positions on the chart — see [On-chart P/L pill](#on-chart-pl-pill--featurespnlpill) |
|
|
631
|
+
| `tradeActionBar` | `false` | The trade button opens a segmented `Alert · Pending · SL · TP · TSL · ×` bar at the clicked price instead of the two-row Buy/Sell popup — see [Trade action bar](#trade-action-bar--featurestradeactionbar) |
|
|
632
|
+
|
|
633
|
+
`enableForecasting` turns on three things together:
|
|
634
|
+
|
|
635
|
+
- **Long Position** / **Short Position** in a new **Forecasting** group in the
|
|
636
|
+
drawing toolbar. With the flag off the group is not rendered at all.
|
|
637
|
+
- **Brush** and **Highlighter** draw freehand — press, drag, release. Off, they
|
|
638
|
+
keep the click-per-point gesture ended by a double-click.
|
|
639
|
+
- **Ruler** reports percent, pips, calendar duration and volume. Off, it reports
|
|
640
|
+
the bar count and raw price delta as before.
|
|
641
|
+
|
|
642
|
+
They share one flag because they ship and roll back together: a broker either has
|
|
643
|
+
the reworked drawing tools or it doesn't.
|
|
644
|
+
|
|
645
|
+
Related config, independent of the flag: the position tools show quantity and
|
|
646
|
+
money only when [`account`](#accountspec) is supplied, and pips on both the
|
|
647
|
+
position tools and the ruler come from [`instrument`](#instrumentspec) — without
|
|
648
|
+
it, pip size is inferred from the feed's decimal count.
|
|
649
|
+
|
|
650
|
+
### `AccountSpec`
|
|
651
|
+
|
|
652
|
+
What the **Long / Short position** tools size themselves against.
|
|
653
|
+
|
|
654
|
+
TradingView asks the user to type these because it has no broker connection. We
|
|
655
|
+
do have one — so pass real equity and keep it current.
|
|
656
|
+
|
|
657
|
+
```ts
|
|
658
|
+
chart.setAccount({ size: 10_000, riskPercent: 1 });
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
| Field | Default | Description |
|
|
662
|
+
|---|---|---|
|
|
663
|
+
| `size` | — | Account equity in the account currency |
|
|
664
|
+
| `riskPercent` | `1` | Percent of the account risked per trade |
|
|
665
|
+
|
|
666
|
+
Omit the block and the position tools still draw — price, percent, pips and
|
|
667
|
+
risk/reward all render; only the quantity and money amounts are left out.
|
|
668
|
+
|
|
669
|
+
> A sketch drawn against a stale balance reports the wrong quantity rather than
|
|
670
|
+
> failing visibly, so push `setAccount()` whenever equity moves.
|
|
671
|
+
|
|
440
672
|
### `OHLCVBar`
|
|
441
673
|
|
|
442
674
|
| Field | Required | Type | Description |
|
|
@@ -477,7 +709,7 @@ import { DEFAULT_UI_CONFIG } from 'acttrader-charts';
|
|
|
477
709
|
|
|
478
710
|
| Component key | Configurable properties |
|
|
479
711
|
|---|---|
|
|
480
|
-
| `drawingToolbar` | `iconBtnSize`, `iconFontSize`, `iconFontFamily`, `flyoutLabelFontSize`, `flyoutLabelFontFamily`, `shortcutFontSize`, `soonBadgeFontSize`, `flyoutHeadingFontSize`, `flyoutHeadingLetterSpacing`, `scrollBtnHeight`, `scrollBtnFontSize`, `barPadding`, `btnGap` |
|
|
712
|
+
| `drawingToolbar` | `iconBtnSize`, `iconFontSize`, `iconFontFamily`, `flyoutLabelFontSize`, `flyoutLabelFontFamily`, `shortcutFontSize`, `soonBadgeFontSize`, `flyoutHeadingFontSize`, `flyoutHeadingLetterSpacing`, `scrollBtnHeight`, `scrollBtnFontSize`, `barPadding`, `btnGap`, `modernIcons`, `actionsButtonIcon` (`'default'` \| `'lock'` — lock glyph on the Actions button, for hosts that relabel Lock All as "Save Charts") |
|
|
481
713
|
| `topBar` | `height`, `dropBtnFontSize`, `dropBtnFontFamily`, `drawBtnSize`, `drawBtnIconFontSize`, `iconBtnSize`, `mobileIconBtnSize`, `mobileDrawBtnIconSize`, `flyoutRowFontSize`, `flyoutRowFontFamily`, `flyoutCategoryFontSize`, `flyoutCategoryLetterSpacing`, `flyoutCheckFontSize`, `streamDotSize` |
|
|
482
714
|
| `bottomBar` | `height`, `btnFontSize`, `btnFontFamily` |
|
|
483
715
|
| `priceAxis` | `fontSize`, `fontFamily` |
|
|
@@ -555,6 +787,11 @@ new ChartEngine({
|
|
|
555
787
|
| `topBar.series` | `area` | `'Area'` | |
|
|
556
788
|
| `topBar` | `indicatorsBtn` | `'Indicators'` | Indicators button (no active) |
|
|
557
789
|
| `topBar` | `indicatorsBtnActive` | `'Indicators ({count})'` | `{count}` replaced at runtime |
|
|
790
|
+
| `topBar` | `disableCrosshairTitle` | `'Disable crosshair'` | Header crosshair switch tooltip while on (`enableCrossHairHeader`) |
|
|
791
|
+
| `topBar` | `enableCrosshairTitle` | `'Enable crosshair'` | Header crosshair switch tooltip while off |
|
|
792
|
+
| `topBar` | `pnlPillBtn` | `'P/L'` | Header P/L pill switch label (`features.pnlPill`) |
|
|
793
|
+
| `topBar` | `hidePnlPillTitle` | `'Hide P/L pill for {symbol}'` | P/L switch tooltip while the pill is shown |
|
|
794
|
+
| `topBar` | `showPnlPillTitle` | `'Show P/L pill for {symbol}'` | P/L switch tooltip while the pill is hidden |
|
|
558
795
|
| `topBar` | `toggleDrawingTitle` | `'Toggle Drawing Tools'` | Toolbar toggle tooltip |
|
|
559
796
|
| `topBar` | `toggleFullscreenTitle` | `'Toggle Fullscreen'` | Fullscreen button tooltip |
|
|
560
797
|
| `topBar` | `exitFullscreenTitle` | `'Exit Fullscreen'` | |
|
|
@@ -597,6 +834,16 @@ new ChartEngine({
|
|
|
597
834
|
| `trade` | `market` | `'Market'` | |
|
|
598
835
|
| `trade` | `qty` | `'Qty'` | Draft order qty input label |
|
|
599
836
|
| `trade` | `placeOrderTitle` | `'Place order at this price'` | Trade button tooltip |
|
|
837
|
+
| `trade` | `actionAlert` … `actionTsl` | `'Alert'`, `'Pending'`, `'SL'`, `'TP'`, `'TSL'` | Trade action bar chips (`features.tradeActionBar`) |
|
|
838
|
+
| `trade` | `actionCloseTitle` | `'Close'` | Trade action bar × tooltip |
|
|
839
|
+
| `trade` | `directionAbove`, `directionBelow` | `'Above'`, `'Below'` | Pending sub-menu header — side of the market the clicked price is on |
|
|
840
|
+
| `trade` | `actionBulkNoPositionsTitle`, `actionBulkMixedTitle` | `'No open {symbol} positions'`, `'Bulk levels need all {symbol} positions in one direction'` | SL / TP / TSL chips greyed out — why |
|
|
841
|
+
| `pnlPill` | `positionsAbbrev` | `'POS'` | On-chart P/L pill — word after the count (`3 POS`), shown uppercase |
|
|
842
|
+
| `pnlPill` | `showPositionsTitle` | `'Show positions on chart · drag handle to move · double-click to reset'` | Pill tooltip while collapsed — a body click fans the positions out |
|
|
843
|
+
| `pnlPill` | `hidePositionsTitle` | `'Hide positions on chart · drag handle to move · double-click to reset'` | Pill tooltip while fanned out |
|
|
844
|
+
| `pnlPill` | `bulkLevelsTitle` | `'Set SL · TP · TSL on all {count} {symbol} positions'` | ◎ tooltip — opens the host's Bulk Levels dialog |
|
|
845
|
+
| `pnlPill` | `bulkLevelsMixedTitle` | `'Bulk levels need all {count} {symbol} positions in one direction'` | ◎ tooltip while greyed out for mixed sides |
|
|
846
|
+
| `pnlPill` | `closeAllTitle` | `'Close all {symbol} positions'` | Amber ✕ tooltip; `{symbol}` and `{count}` are filled in |
|
|
600
847
|
| `dialogs.settings` | `title` | `'Chart Settings'` | Dialog title |
|
|
601
848
|
| `dialogs.settings` | `tfcSection` | `'Trade from Charts'` | Section heading |
|
|
602
849
|
| `dialogs.settings` | `showLabel` | `'Show'` | Radio group label |
|
|
@@ -637,8 +884,8 @@ chart.setTheme('light');
|
|
|
637
884
|
|---|---|---|
|
|
638
885
|
| `background` | `string` | Canvas background color |
|
|
639
886
|
| `grid` | `string` | Grid line color |
|
|
640
|
-
| `axisText` | `string` | Axis tick label color |
|
|
641
|
-
| `axisBorder` | `string` | Axis border / separator color |
|
|
887
|
+
| `axisText` | `string` | Axis tick label color. Also the default label colour for the chart chrome (dialog text, toolbar labels) — set it via `themeOverrides` to restyle both, or via the Settings dialog to restyle the canvas axis alone |
|
|
888
|
+
| `axisBorder` | `string` | Axis border / separator color. Also the default border colour for the chart chrome (dialog frames, toolbar dividers) — same split as `axisText` |
|
|
642
889
|
| `crosshair` | `string` | Crosshair line color |
|
|
643
890
|
| `tooltip` | `{background, text, border}` | Floating label colors |
|
|
644
891
|
| `candle` | `{up, down, wickUp, wickDown}` | Candlestick colors |
|
|
@@ -673,7 +920,7 @@ chart.resetData(): this
|
|
|
673
920
|
|
|
674
921
|
**Symbol switch pattern:**
|
|
675
922
|
```ts
|
|
676
|
-
chart.setSymbol('GBPUSD').resetData();
|
|
923
|
+
chart.setSymbol('GBPUSD').setInstrument({ pipSize: 0.0001 }).resetData();
|
|
677
924
|
// … fetch new bars …
|
|
678
925
|
chart.loadData(newBars);
|
|
679
926
|
```
|
|
@@ -682,9 +929,17 @@ chart.loadData(newBars);
|
|
|
682
929
|
|
|
683
930
|
```ts
|
|
684
931
|
chart.setSeries(series: SeriesType): this
|
|
932
|
+
chart.setInstrument(spec: InstrumentSpec | undefined): this // contract specs for pips / money readouts
|
|
933
|
+
chart.getInstrument(): ResolvedInstrument // specs with fallbacks applied
|
|
934
|
+
chart.setAccount(account: AccountSpec | undefined): this // equity + risk for the position tools
|
|
935
|
+
chart.getAccount(): AccountSpec | undefined
|
|
685
936
|
chart.setTheme('dark' | 'light'): this
|
|
686
937
|
chart.setTimeframe(tf: Timeframe): this // change timeframe and reload data
|
|
687
|
-
chart.
|
|
938
|
+
chart.setDuration(d: Duration, timeframe?: Timeframe): this // select a duration; pairs the timeframe and reloads
|
|
939
|
+
chart.getDuration(): Duration | null // currently selected duration
|
|
940
|
+
chart.setBracketLabelMode(mode, currencySymbol?): this // SL/TP pills: 'price' (default) or 'amount'
|
|
941
|
+
chart.setThemeOverrides(overrides: ThemeOverrides): this // update per-theme color overrides at runtime — canvas AND chrome
|
|
942
|
+
chart.setCanvasColors(colors: ChartConfig['canvasColors'] | null): this // recolour the canvas only at runtime (same picks as the Settings dialog); null clears
|
|
688
943
|
chart.setTimezone(tz: string): this // change display timezone at runtime
|
|
689
944
|
chart.setVolume(show: boolean): this
|
|
690
945
|
```
|
|
@@ -722,6 +977,7 @@ chart.cancelCurrentEdit(): this // cancel the active draft order or any in-pro
|
|
|
722
977
|
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
|
|
723
978
|
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
|
|
724
979
|
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
|
|
980
|
+
chart.setPnlPillVisible(visible: boolean): this // show/hide the on-chart P/L pill (features.pnlPill) — what the header's P/L switch does; emits pnlPillToggle
|
|
725
981
|
```
|
|
726
982
|
|
|
727
983
|
> **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.
|
|
@@ -730,8 +986,33 @@ chart.setTfcActive(enabled: boolean): this // toggle TFC on/off at runtime; hi
|
|
|
730
986
|
|
|
731
987
|
```ts
|
|
732
988
|
chart.setOrderLots(lots: number): void // update default qty at runtime
|
|
989
|
+
chart.setCrosshairEnabled(enabled: boolean): this // off hides the crosshair AND this button — it rides on the crosshair line, so it has no place once the line is gone
|
|
990
|
+
```
|
|
991
|
+
|
|
992
|
+
The button follows the crosshair's horizontal line, so it is only ever shown while the
|
|
993
|
+
crosshair is (`crosshairEnabled`, default `true`). Hosts with a crosshair on/off control
|
|
994
|
+
should call `setCrosshairEnabled()` rather than painting the crosshair transparent — the
|
|
995
|
+
latter leaves the button riding an invisible line.
|
|
996
|
+
|
|
997
|
+
### Header crosshair switch — `enableCrossHairHeader`
|
|
998
|
+
|
|
999
|
+
```ts
|
|
1000
|
+
const chart = new ChartEngine({
|
|
1001
|
+
container,
|
|
1002
|
+
enableCrossHairHeader: true, // crosshair icon in the header; the crosshair itself starts on
|
|
1003
|
+
});
|
|
1004
|
+
chart.on('crosshairToggle', ({ enabled }) => saveUserPref('crosshair', enabled));
|
|
733
1005
|
```
|
|
734
1006
|
|
|
1007
|
+
With the flag on, a crosshair icon sits in the header's right cluster (advanced toolbar and
|
|
1008
|
+
default top bar) or at the end of the compact per-pane strip (`headerLayout: 'compact'`). The chart crosshair is shown as usual on load and the icon is tinted. Clicking
|
|
1009
|
+
the icon hides the crosshair — and the floating trade button that rides on it — and the icon
|
|
1010
|
+
drops to its plain state; clicking again brings both back. Each click goes through
|
|
1011
|
+
`setCrosshairEnabled()` and emits `crosshairToggle`, so a host can persist the choice and seed
|
|
1012
|
+
it on the next load with `crosshairEnabled: false`. `enableCrosshairToggle` is the deprecated
|
|
1013
|
+
name of the same flag and still works. Native wrappers get the same switch via their
|
|
1014
|
+
`enableCrossHairHeader` init option, the `onCrosshairToggle` callback and `setCrosshairEnabled()`.
|
|
1015
|
+
|
|
735
1016
|
### Viewport
|
|
736
1017
|
|
|
737
1018
|
```ts
|
|
@@ -888,6 +1169,37 @@ Sub-pane heights are user-resizable by dragging the separator between panes.
|
|
|
888
1169
|
| `flatChannel` | Flat Channel | 2 clicks |
|
|
889
1170
|
| `disjointChannel` | Disjoint Channel | 3 clicks |
|
|
890
1171
|
|
|
1172
|
+
### SL/TP bracket labels — price or amount
|
|
1173
|
+
|
|
1174
|
+
By default an SL/TP pill reads `SL 4159.00`. With `bracketLabelMode: 'amount'`
|
|
1175
|
+
it reads `SL -$290.80` — the money the position gains or loses if that bracket
|
|
1176
|
+
is hit, currency symbol in front.
|
|
1177
|
+
|
|
1178
|
+
The chart has no access to contract specs or the account currency, so each level
|
|
1179
|
+
supplies what the maths needs:
|
|
1180
|
+
|
|
1181
|
+
```ts
|
|
1182
|
+
new ChartEngine({ container, bracketLabelMode: 'amount', currencySymbol: '$' });
|
|
1183
|
+
|
|
1184
|
+
chart.setLevels([{
|
|
1185
|
+
label: 'POS-1', price: 4173.54, side: 'buy', lots: 0.20,
|
|
1186
|
+
stopLossPrice: 4159.00, takeProfitPrice: 4183.00,
|
|
1187
|
+
contractSize: 100, // units per lot (100 for XAUUSD, 100000 for most FX)
|
|
1188
|
+
valuePerPoint: 1, // account-currency value of one price unit
|
|
1189
|
+
currencySymbol: '$', // optional per-level override
|
|
1190
|
+
}], 'label', 'price', 'position');
|
|
1191
|
+
// pills render: SL -$290.80 TP +$189.20
|
|
1192
|
+
```
|
|
1193
|
+
|
|
1194
|
+
```
|
|
1195
|
+
amount = (bracket − entry) × direction × lots × contractSize × valuePerPoint
|
|
1196
|
+
```
|
|
1197
|
+
|
|
1198
|
+
`valuePerPoint` is where a quote → account currency conversion goes. A level
|
|
1199
|
+
missing `lots` or `contractSize` keeps showing its price, so a partial rollout
|
|
1200
|
+
degrades level by level rather than rendering `NaN`. Toggle at runtime with
|
|
1201
|
+
`chart.setBracketLabelMode('amount')`.
|
|
1202
|
+
|
|
891
1203
|
### Fibonacci & Gann
|
|
892
1204
|
|
|
893
1205
|
`fibRetracement` · `fibExtension` · `fibChannel` · `fibTimezone` · `fibCircles` · `fibSpiral` · `fibFan` · `fibProjection` · `gannFan` · `gannSquare` · `gannBox`
|
|
@@ -920,10 +1232,78 @@ In a **per-drawing** style (`SerializedDrawing.style.levels`), a level may also
|
|
|
920
1232
|
|
|
921
1233
|
`rectangle` · `rotatedRectangle` · `ellipse` · `triangle` · `circle` · `arc` · `polyline` · `path` · `brush` · `highlighter`
|
|
922
1234
|
|
|
1235
|
+
**`brush` and `highlighter` draw freehand** when `features.enableForecasting` is on
|
|
1236
|
+
(see [Feature flags](#feature-flags)): press, drag, release — the pointer
|
|
1237
|
+
path is sampled continuously and the finished stroke is thinned to the points
|
|
1238
|
+
that carry its shape. (`polyline` and `path` remain click-per-point, ended with a
|
|
1239
|
+
double-click.)
|
|
1240
|
+
|
|
923
1241
|
### Measurements & Annotations
|
|
924
1242
|
|
|
925
1243
|
`priceRange` · `dateRange` · `datePriceRange` · `ruler` · `priceLabel` · `priceNote` · `priceProjection` · `projection` · `arrowUp` · `arrowDown` · `arrowMarker` · `text` · `callout` · `anchoredNote` · `flag` · `sineLine` · `regressionTrend` · `ghostFeed`
|
|
926
1244
|
|
|
1245
|
+
**`ruler`**, with `features.enableForecasting` on (see [Feature flags](#feature-flags)),
|
|
1246
|
+
draws a direction-tinted box over the span it measures and reports:
|
|
1247
|
+
|
|
1248
|
+
```
|
|
1249
|
+
0.00455 (0.80%) 45.5
|
|
1250
|
+
43 bars, 9d 4h
|
|
1251
|
+
Vol 102.27K
|
|
1252
|
+
```
|
|
1253
|
+
|
|
1254
|
+
Price delta, percent, and **pips**; bar count and calendar duration; volume
|
|
1255
|
+
traded across the span.
|
|
1256
|
+
|
|
1257
|
+
A pip figure is always shown — but it is only *correct* when the host supplies
|
|
1258
|
+
[`instrument.pipSize`](#instrumentspec). Without it the chart infers pip size
|
|
1259
|
+
from how many decimals the feed quotes, which follows the usual FX convention
|
|
1260
|
+
and is wrong for metals, indices and crypto.
|
|
1261
|
+
|
|
1262
|
+
Duration needs bar timestamps, and the volume line is omitted entirely when the
|
|
1263
|
+
bars carry no volume, rather than showing a `0` that would read as "no volume
|
|
1264
|
+
traded".
|
|
1265
|
+
|
|
1266
|
+
The box takes its colour from `candle.up` / `candle.down` so a measurement never
|
|
1267
|
+
disagrees with the candles underneath it; picking a colour in the style popover
|
|
1268
|
+
overrides that. All four corners resize.
|
|
1269
|
+
|
|
1270
|
+
With the flag off it reports the bar count and raw price delta, as it always has.
|
|
1271
|
+
|
|
1272
|
+
### Forecasting
|
|
1273
|
+
|
|
1274
|
+
`longPosition` · `shortPosition`
|
|
1275
|
+
|
|
1276
|
+
> Requires `features.enableForecasting` — see [Feature flags](#feature-flags).
|
|
1277
|
+
> With the flag off the group is not rendered, on the desktop toolbar or in the
|
|
1278
|
+
> mobile tools sheet.
|
|
1279
|
+
>
|
|
1280
|
+
> `ghostFeed` and `projection` stay in **Advanced Tools** and are ungated — they
|
|
1281
|
+
> predate this group.
|
|
1282
|
+
|
|
1283
|
+
**`longPosition` / `shortPosition`** sketch a trade that hasn't been placed: a
|
|
1284
|
+
green profit zone from entry to target, a red risk zone from entry to stop, and
|
|
1285
|
+
live readouts.
|
|
1286
|
+
|
|
1287
|
+
```
|
|
1288
|
+
Target: 0.00313 (0.549%) 31.3, Amount: 5622.13
|
|
1289
|
+
Open PnL: 0.00146, Qty: 11323
|
|
1290
|
+
Risk/reward ratio: 1.84
|
|
1291
|
+
Stop: 0.00170 (0.298%) 17.0, Amount: 2887.5
|
|
1292
|
+
```
|
|
1293
|
+
|
|
1294
|
+
Two clicks place it — entry, then target — and the stop lands at a 2:1
|
|
1295
|
+
reward:risk for you to drag. All three prices have handles.
|
|
1296
|
+
|
|
1297
|
+
Quantity is sized so that hitting the stop costs exactly `riskPercent` of the
|
|
1298
|
+
account, which needs [`account`](#accountspec); the money amounts and pips also
|
|
1299
|
+
need [`instrument`](#instrumentspec). Without them the tool still draws and still
|
|
1300
|
+
reports price, percent and risk/reward.
|
|
1301
|
+
|
|
1302
|
+
> **This is a drawing, not an order.** Trade-From-Chart (`setLevels`) is what
|
|
1303
|
+
> puts real broker orders on the chart — see [Trade From Chart
|
|
1304
|
+
> (TFC)](#trade-from-chart-tfc--setlevels). Nothing on a position tool reaches
|
|
1305
|
+
> the broker.
|
|
1306
|
+
|
|
927
1307
|
### Volume Profile
|
|
928
1308
|
|
|
929
1309
|
`volumeProfile` · `anchoredVP` · `fixedRangeVP`
|
|
@@ -932,6 +1312,10 @@ In a **per-drawing** style (`SerializedDrawing.style.levels`), a level may also
|
|
|
932
1312
|
|
|
933
1313
|
`cyclicLines` · `timeCycles`
|
|
934
1314
|
|
|
1315
|
+
> `ghostFeed` and `projection` remain here and are ungated. TradingView files
|
|
1316
|
+
> them under Forecasting, but moving them would have reshuffled the toolbar for
|
|
1317
|
+
> hosts that never enabled the new group.
|
|
1318
|
+
|
|
935
1319
|
### Harmonic & Elliott Patterns
|
|
936
1320
|
|
|
937
1321
|
`abcdPattern` · `headShoulders` · `trianglePattern` · `batPattern` · `butterflyPattern` · `crabPattern` · `gartleyPattern` · `cypherPattern` · `sharkPattern` · `threeDrives` · `elliottImpulse` · `elliottCorrective` · `elliottTriangle` · `elliottCombination` · `elliottWxy`
|
|
@@ -952,8 +1336,9 @@ chart.on('pan', ({ viewport }) => {});
|
|
|
952
1336
|
chart.on('timeframeChange',({ timeframe }) => {});
|
|
953
1337
|
chart.on('durationChange', ({ duration, timeframe }) => {});
|
|
954
1338
|
chart.on('seriesChange', ({ series }) => {});
|
|
1339
|
+
chart.on('priceSourceChange', ({ source }) => {}); // header BID/ASK/LTP dropdown pick
|
|
955
1340
|
chart.on('streamStatus', ({ status }) => {}); // 'connected' | 'reconnecting' | 'disconnected'
|
|
956
|
-
chart.on('dataLoaded', ({ timeframe, interval, start, end }) => {});
|
|
1341
|
+
chart.on('dataLoaded', ({ timeframe, interval, start, end, priceSource }) => {});
|
|
957
1342
|
chart.on('newBar', ({ completedBar, openingBar, intervalMs }) => {});
|
|
958
1343
|
chart.on('indicatorAdded', ({ instanceId, shortName, params }) => {}); // a study instance was added — keep instanceId to remove it later
|
|
959
1344
|
chart.on('indicatorRemoved', ({ instanceId, shortName }) => {});
|
|
@@ -976,6 +1361,10 @@ chart.on('tradeLevelEditCancelled', ({ label, type, isFullscreen }) => {}); // E
|
|
|
976
1361
|
chart.on('draftInitiated', ({ side, price, orderType, isFullscreen }) => {}); // new draft order shown — open buy/sell form
|
|
977
1362
|
chart.on('draftCancelled', ({ label, isFullscreen }) => {}); // draft order dismissed without confirming
|
|
978
1363
|
chart.on('tfcToggle', ({ enabled }) => {}); // TFC toggled on or off via top bar button or setTfcActive()
|
|
1364
|
+
chart.on('crosshairToggle', ({ enabled }) => {}); // crosshair switched on or off — header switch (enableCrossHairHeader) or setCrosshairEnabled(); persist it here
|
|
1365
|
+
chart.on('pnlPillCloseAll', ({ count, pnl, labels, data, isFullscreen }) => {}); // the on-chart P/L pill's ✕ — close these positions (features.pnlPill)
|
|
1366
|
+
chart.on('pnlPillToggle', ({ visible }) => {}); // the header's P/L switch showed or hid the pill — persist it here (features.pnlPill)
|
|
1367
|
+
chart.on('bulkLevelsRequested', ({ kind, price, source, isFullscreen }) => {}); // action-bar chip (price) or the P/L pill's ◎ (price null) — open your Bulk Levels dialog for `kind`
|
|
979
1368
|
chart.on('tradeLevelDragEnd', ({ label, type, newPrice, data }) => {}); // deprecated — use tradeLevelEdit
|
|
980
1369
|
chart.on('tradeLevelBracketDrag', ({ label, bracketType, newPrice, data }) => {}); // deprecated
|
|
981
1370
|
|
|
@@ -984,8 +1373,8 @@ chart.on('tradeLevelBracketDrag', ({ label, bracketType, newPrice, data }) => {}
|
|
|
984
1373
|
chart.on('orderLineMoveStart', ({ label, fromTimestamp, fromBarIndex, isFullscreen }) => {});
|
|
985
1374
|
chart.on('orderLineMoving', ({ label, toTimestamp, toBarIndex, isFullscreen }) => {}); // fires on every move
|
|
986
1375
|
chart.on('orderLineMoved', ({ label, fromTimestamp, toTimestamp, fromBarIndex, toBarIndex, data, isFullscreen }) => {
|
|
987
|
-
//
|
|
988
|
-
//
|
|
1376
|
+
// The chart already remembers the drop in localStorage (orderLineAnchorPersistence, default on).
|
|
1377
|
+
// Hook here only if you also want to persist the new time anchor server-side.
|
|
989
1378
|
});
|
|
990
1379
|
|
|
991
1380
|
// Fires after addLevelBracket() auto-places a bracket, delivering the computed price back to the caller
|