acttrader-charts 1.3.0-beta.2 → 1.3.0-beta.20
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 +396 -15
- package/dist/{MACD-D1i-sEFR.d.cts → MACD-BM9VDMeg.d.cts} +465 -18
- package/dist/{MACD-D1i-sEFR.d.ts → MACD-BM9VDMeg.d.ts} +465 -18
- package/dist/index.cjs +3520 -396
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +639 -18
- package/dist/index.d.ts +639 -18
- package/dist/index.js +3505 -397
- 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
|
|
@@ -333,13 +346,15 @@ When `enableTrading` is on and live BID/ASK data is streaming, hovering / activa
|
|
|
333
346
|
|
|
334
347
|
### Horizontal (time-axis) order-line dragging
|
|
335
348
|
|
|
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.
|
|
349
|
+
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
350
|
|
|
338
351
|
```ts
|
|
339
352
|
const chart = new ChartEngine({
|
|
340
353
|
container,
|
|
341
354
|
features: { orderLineTimeDrag: true }, // host enables this only for the gated broker
|
|
342
355
|
orderLineDragSnap: true, // snap to nearest candle on release (default)
|
|
356
|
+
orderLineAnchorPersistence: true, // remember dropped positions in localStorage (default)
|
|
357
|
+
orderLineDefaultAnchor: 'center', // un-dragged badges start mid-chart (default: 'timestamp')
|
|
343
358
|
});
|
|
344
359
|
|
|
345
360
|
chart.setLevels(
|
|
@@ -354,13 +369,101 @@ chart.on('orderLineMoved', ({ label, fromTimestamp, toTimestamp, data }) => {
|
|
|
354
369
|
```
|
|
355
370
|
|
|
356
371
|
- **Bounds:** the badge is clamped to the visible viewport — it can't be dragged past the left/right edge.
|
|
372
|
+
- **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
373
|
- **Snap:** controlled by `orderLineDragSnap` (default `true`); set `false` to keep the exact released time.
|
|
358
374
|
- **Escape / touch-cancel:** reverts the badge to its starting candle without emitting `orderLineMoved`.
|
|
359
375
|
- **Stays put after release:** the chart holds the dropped position across `setLevels` refreshes until you echo the new `timestamp` back (auto-released on match).
|
|
376
|
+
- **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.
|
|
377
|
+
- **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
378
|
- A level **without** `timestamp` renders exactly as before (badge centered), so existing consumers are unaffected.
|
|
361
379
|
|
|
362
380
|
---
|
|
363
381
|
|
|
382
|
+
### Trade action bar — `features.tradeActionBar`
|
|
383
|
+
|
|
384
|
+
With the flag on, the floating trade button opens a segmented bar at the clicked
|
|
385
|
+
price instead of the two-row Buy/Sell popup:
|
|
386
|
+
|
|
387
|
+
```
|
|
388
|
+
Alert | Pending | SL | TP | TSL | ×
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
- No price readout — the axis already shows the price at that point.
|
|
392
|
+
- **Alert** calls `onAddAlert(price)`. The chip is only rendered when that callback is set.
|
|
393
|
+
- **Pending** opens a sub-menu just above the bar — an `Above · 1.17586` / `Below · …`
|
|
394
|
+
header, then the two orders that make sense at that price (`Buy Limit` / `Sell Stop`
|
|
395
|
+
below the market, `Sell Limit` / `Buy Stop` above), each with its `@ price`. Picking a
|
|
396
|
+
row creates the draft order exactly as before and closes both.
|
|
397
|
+
- **SL / TP / TSL** emit `bulkLevelsRequested` with `{ kind, price }` and close the
|
|
398
|
+
bar — the host opens its Bulk Levels dialog pre-focused on that kind. The chart
|
|
399
|
+
never calls a broker. The chips grey out, with a tooltip saying why, when the
|
|
400
|
+
symbol has no open positions or has buys and sells open at once — one stop or
|
|
401
|
+
target price cannot sit on the right side of the market for both directions.
|
|
402
|
+
- **×**, a click elsewhere, Escape, TFC off and `dismissAllUI()` all close it.
|
|
403
|
+
|
|
404
|
+
Chip labels and the × tooltip live under `labels.trade` (`actionAlert`, `actionPending`,
|
|
405
|
+
`actionSl`, `actionTp`, `actionTsl`, `actionCloseTitle`).
|
|
406
|
+
|
|
407
|
+
### On-chart P/L pill — `features.pnlPill`
|
|
408
|
+
|
|
409
|
+
A floating summary of the open positions on the chart, for the trader who wants
|
|
410
|
+
the total without opening the positions panel:
|
|
411
|
+
|
|
412
|
+
```
|
|
413
|
+
⋮⋮ 3 POS +72.65 USD | ◎ ✕
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
- **`3 POS`** — how many position levels are on the chart. Pending orders,
|
|
417
|
+
position-style entry orders (`entryPriceEditable`) and an unconfirmed draft
|
|
418
|
+
are not counted.
|
|
419
|
+
- **`+72.65 USD`** — the sum of the numeric `pnl` those levels carry, i.e. the
|
|
420
|
+
`pnlKey` column you pass to `setLevels`. The chart computes no money itself;
|
|
421
|
+
while no position has a `pnl` yet the pill shows `—`. Profit is drawn in the
|
|
422
|
+
buy colour, loss in the sell colour.
|
|
423
|
+
- **Click the body** (the grip, the count or the amount) to fan every position
|
|
424
|
+
badge out on the chart — clustered levels expand, and TFC is switched on if it
|
|
425
|
+
was off — with an accent ring on the pill while they are shown. Click again to
|
|
426
|
+
collapse.
|
|
427
|
+
- **◎** (the same target glyph as the Bulk Levels dialog header) emits
|
|
428
|
+
`bulkLevelsRequested` with `source: 'pill'` and a `null` price, so your Bulk Levels
|
|
429
|
+
dialog opens pre-scoped to every open position on the symbol. Tooltip:
|
|
430
|
+
"Set SL · TP · TSL on all N {symbol} positions". Greyed out while buys and sells are
|
|
431
|
+
both open ("Bulk levels need all N {symbol} positions in one direction").
|
|
432
|
+
- **✕** (amber) emits `pnlPillCloseAll` with every counted position. Your app
|
|
433
|
+
confirms and closes them; the chart never calls a broker.
|
|
434
|
+
|
|
435
|
+
The pill sits top-right of the plot, clear of the price axis. Drag it anywhere
|
|
436
|
+
inside the plot; double-click puts it back. It hides itself while the chart has
|
|
437
|
+
no positions and follows the chart theme. Every element sits on one shared row
|
|
438
|
+
height, and the grip and both buttons have fixed widths, so a long or negative
|
|
439
|
+
amount never shifts or squeezes them.
|
|
440
|
+
|
|
441
|
+
With the feature on, the header (advanced toolbar and default top bar) also gets a
|
|
442
|
+
**P/L** switch — wallet icon, `P/L`, the same 24px `rounded-sm` button tinted while the
|
|
443
|
+
pill is shown. It hides or shows the pill on the user's say-so, wins over the position
|
|
444
|
+
count, and emits `pnlPillToggle` so you can persist the choice; `pnlPill.visible` seeds
|
|
445
|
+
it and `setPnlPillVisible()` drives it from code.
|
|
446
|
+
|
|
447
|
+
```ts
|
|
448
|
+
const chart = new ChartEngine({
|
|
449
|
+
container,
|
|
450
|
+
features: { pnlPill: true },
|
|
451
|
+
pnlPill: { currency: 'USD' }, // → "+72.65 USD"; or format: (pnl) => myMoney(pnl)
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
chart.setLevels(positions, 'TradeID', 'Price', 'position', 'pnl', 'pnlText');
|
|
455
|
+
|
|
456
|
+
chart.on('pnlPillCloseAll', async ({ count, pnl, data }) => {
|
|
457
|
+
if (!(await confirmDialog(`Close all ${count} positions?`))) return;
|
|
458
|
+
for (const row of data) closeTrade(row);
|
|
459
|
+
});
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
Labels live under `labels.pnlPill`: `positionsAbbrev` (`'POS'`), `showPositionsTitle`,
|
|
463
|
+
`hidePositionsTitle`, `bulkLevelsTitle`, `closeAllTitle` — titles may
|
|
464
|
+
use `{symbol}` and `{count}`. The maths behind the pill is exported as
|
|
465
|
+
`summarisePositionLevels(levels, draftLabel?)` and `formatPnlPillValue(pnl, currency?)`.
|
|
466
|
+
|
|
364
467
|
## Default Configuration
|
|
365
468
|
|
|
366
469
|
### `ChartConfig`
|
|
@@ -375,9 +478,13 @@ chart.on('orderLineMoved', ({ label, fromTimestamp, toTimestamp, data }) => {
|
|
|
375
478
|
| `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
479
|
| `showDrawingTools` | | `true` | Show drawing toolbar and pencil button |
|
|
377
480
|
| `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 |
|
|
481
|
+
| `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 |
|
|
482
|
+
| `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
483
|
| `duration` | | — | Initial active duration button |
|
|
380
484
|
| `symbol` | | — | Symbol name shown in the top bar |
|
|
485
|
+
| `features` | | `{}` | Opt-in feature gates. See [Feature flags](#feature-flags) |
|
|
486
|
+
| `instrument` | | — | Contract specs for `symbol` — pip size, contract size, money conversion. See [`InstrumentSpec`](#instrumentspec) |
|
|
487
|
+
| `account` | | — | Account equity and per-trade risk used to size the position tools. See [`AccountSpec`](#accountspec) |
|
|
381
488
|
| `isins` | | — | Symbol list for the picker modal |
|
|
382
489
|
| `onIsinSelect` | | — | Called when user picks a symbol from the picker modal |
|
|
383
490
|
| `padding` | | `{top:8,right:0,bottom:0,left:0}` | Canvas padding (px) |
|
|
@@ -394,7 +501,7 @@ chart.on('orderLineMoved', ({ label, fromTimestamp, toTimestamp, data }) => {
|
|
|
394
501
|
| `momentumMaxVelocity` | | `6.0` | Max launch velocity (px/ms) — caps hard-flick speed |
|
|
395
502
|
| `targetCandleWidth` | | `10` | Target px width per candle for auto-calculating initial bar count |
|
|
396
503
|
| `durationTimeframeMap` | | *(see below)* | Override duration → timeframe pairings |
|
|
397
|
-
| `dataLoader` | | — | `(
|
|
504
|
+
| `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
505
|
| `compareDataLoader` | | — | `({ symbol, start, end, interval }) => Promise<OHLCVBar[]>` — fetches bars for a compare symbol. Set this to enable the library-owned Compare flow. |
|
|
399
506
|
| `initialCompares` | | — | Symbols to auto-add as compares once the initial primary range is loaded |
|
|
400
507
|
| `maxCompares` | | `8` | Maximum concurrent compare symbols. Adding beyond emits `compareError` |
|
|
@@ -403,6 +510,8 @@ chart.on('orderLineMoved', ({ label, fromTimestamp, toTimestamp, data }) => {
|
|
|
403
510
|
| `labels` | | `DEFAULT_LABELS` | Deep-partial string overrides for i18n/translation |
|
|
404
511
|
| `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
512
|
| `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)` |
|
|
513
|
+
| `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 |
|
|
514
|
+
| `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
515
|
| `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
516
|
| `showAskLine` | | unset | Show the Ask price line (dashed line + axis tag) independently. Unset: legacy `showBidAskLines` behavior. Toggle at runtime with `chart.setShowAskLine(show)` |
|
|
408
517
|
| `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 +521,23 @@ chart.on('orderLineMoved', ({ label, fromTimestamp, toTimestamp, data }) => {
|
|
|
412
521
|
| `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
522
|
| `maxSubPanes` | | `3` | Max simultaneous oscillator sub-panes |
|
|
414
523
|
| `tradeDisplayFilter` | | `"all"` | Which TFC levels are visible: `"all"` · `"positions"` · `"orders"` · `"none"` |
|
|
524
|
+
| `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
525
|
| `positionRenderStyle` | | auto | Force position render style: `"line"` or `"dot"` |
|
|
416
526
|
| `hideLevelConfirmCancel` | | `false` | Hide on-canvas ✓/✗ confirm-cancel buttons for TFC level edits |
|
|
417
527
|
| `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
528
|
| `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`. |
|
|
419
529
|
| `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)` |
|
|
530
|
+
| `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]` |
|
|
531
|
+
| `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
532
|
| `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
|
-
| `
|
|
533
|
+
| `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)` |
|
|
534
|
+
| `enableCrossHairHeader` | | `false` | Crosshair on/off switch in the header (advanced toolbar, default top bar and the compact per-pane strip): 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) |
|
|
535
|
+
| `enableCrosshairToggle` | | `false` | **Deprecated** — alias of `enableCrossHairHeader`; either flag renders the switch |
|
|
536
|
+
| `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 |
|
|
537
|
+
| `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
538
|
| `orderLineDragSnap` | | `true` | When horizontal dragging is enabled, snap the badge to the nearest candle on release. Set `false` to keep the exact released time |
|
|
539
|
+
| `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 |
|
|
540
|
+
| `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
541
|
| `levelClusteringEnabled` | | `true` | Enable trade-level fan-out clustering; overlapping levels group into expandable badges |
|
|
424
542
|
| `clusterThresholdDistance` | | `20` | Pixel proximity threshold for clustering (only when `levelClusteringEnabled` is `true`) |
|
|
425
543
|
| `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 +548,120 @@ chart.on('orderLineMoved', ({ label, fromTimestamp, toTimestamp, data }) => {
|
|
|
430
548
|
| `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
549
|
| `tradesThresholdForHorizontalLine` | | `2` | Level count above which render auto-switches to `"dot"` mode |
|
|
432
550
|
| `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
|
|
551
|
+
| `canvasColors` | | — | Per-theme colour overrides for the **canvas only** (persisted from the Settings dialog). 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
552
|
| `aggregateFrom` | | — | Fetch finer-grained data and aggregate client-side per timeframe |
|
|
435
553
|
| `onOrderSubmit` | | — | Called when user submits a trade via the floating button |
|
|
436
554
|
| `onLevelEdit` | | — | Called when user confirms a TFC level edit |
|
|
437
555
|
| `onLotsChange` | | — | Called when user changes qty on a draft order |
|
|
438
556
|
| `onLevelDragEnd` | | — | *(deprecated — use `onLevelEdit`)* |
|
|
439
557
|
|
|
558
|
+
### `InstrumentSpec`
|
|
559
|
+
|
|
560
|
+
The chart reads prices, never contract specs — so a tool that reports a distance
|
|
561
|
+
in **pips**, or converts one to money, has to be told how. Pass `instrument` at
|
|
562
|
+
construction and swap it with `setInstrument()` whenever the symbol changes.
|
|
563
|
+
|
|
564
|
+
```ts
|
|
565
|
+
const chart = new ChartEngine({
|
|
566
|
+
container,
|
|
567
|
+
symbol: 'EURUSD',
|
|
568
|
+
instrument: {
|
|
569
|
+
pipSize: 0.0001, // 0.01 for JPY crosses
|
|
570
|
+
contractSize: 100_000, // units per lot
|
|
571
|
+
valuePerPoint: 1, // account-currency value of one price unit per unit
|
|
572
|
+
currencySymbol: '$',
|
|
573
|
+
},
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
chart.setSymbol('USDJPY').setInstrument({ pipSize: 0.01, contractSize: 100_000 });
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
| Field | Default | Description |
|
|
580
|
+
|---|---|---|
|
|
581
|
+
| `pipSize` | inferred | Price distance counted as one pip |
|
|
582
|
+
| `contractSize` | `1` | Units per lot (`100` for XAUUSD, `100000` for most FX pairs) |
|
|
583
|
+
| `valuePerPoint` | `1` | Account-currency value of one price unit per contract unit |
|
|
584
|
+
| `currencySymbol` | `'$'` | Prefixed to money figures |
|
|
585
|
+
|
|
586
|
+
**Every field is optional and the whole block can be omitted** — tools fall back
|
|
587
|
+
to price-only readouts, so existing integrations keep working untouched.
|
|
588
|
+
|
|
589
|
+
When `pipSize` is absent it is inferred from how many decimals the feed quotes:
|
|
590
|
+
five-decimal (`1.08531`) and three-decimal (`151.234`) feeds carry fractional
|
|
591
|
+
pips, so the pip is the second-to-last digit; two- and four-decimal feeds quote
|
|
592
|
+
whole pips. **That convention is wrong for metals, indices and crypto** — pass
|
|
593
|
+
`pipSize` explicitly if pips matter.
|
|
594
|
+
|
|
595
|
+
The same fallbacks are exported for hosts that want to compute alongside the
|
|
596
|
+
chart:
|
|
597
|
+
|
|
598
|
+
```ts
|
|
599
|
+
import { resolveInstrument, inferPipSize, toPips, toMoney } from 'acttrader-charts';
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
> Per-position values still travel on the individual `TradeLevel`
|
|
603
|
+
> (`contractSize` / `valuePerPoint`). `InstrumentSpec` is the chart-wide default
|
|
604
|
+
> for tools that aren't attached to a position.
|
|
605
|
+
|
|
606
|
+
### Feature flags
|
|
607
|
+
|
|
608
|
+
Newer tools ship behind `ChartConfig.features` so each host opts in. **Every flag
|
|
609
|
+
defaults to `false`** — a chart that sets none behaves exactly as it did before
|
|
610
|
+
the flag existed, including its toolbar contents.
|
|
611
|
+
|
|
612
|
+
```ts
|
|
613
|
+
const chart = new ChartEngine({
|
|
614
|
+
container,
|
|
615
|
+
features: { enableForecasting: true },
|
|
616
|
+
});
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
| Flag | Default | What it changes |
|
|
620
|
+
|---|---|---|
|
|
621
|
+
| `enableForecasting` | `false` | The reworked drawing tools, as one switch — see below |
|
|
622
|
+
| `orderLineTimeDrag` | `false` | Horizontal (time-axis) order-line dragging for positions and pending orders — see [Horizontal order-line dragging](#horizontal-time-axis-order-line-dragging) |
|
|
623
|
+
| `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) |
|
|
624
|
+
| `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) |
|
|
625
|
+
|
|
626
|
+
`enableForecasting` turns on three things together:
|
|
627
|
+
|
|
628
|
+
- **Long Position** / **Short Position** in a new **Forecasting** group in the
|
|
629
|
+
drawing toolbar. With the flag off the group is not rendered at all.
|
|
630
|
+
- **Brush** and **Highlighter** draw freehand — press, drag, release. Off, they
|
|
631
|
+
keep the click-per-point gesture ended by a double-click.
|
|
632
|
+
- **Ruler** reports percent, pips, calendar duration and volume. Off, it reports
|
|
633
|
+
the bar count and raw price delta as before.
|
|
634
|
+
|
|
635
|
+
They share one flag because they ship and roll back together: a broker either has
|
|
636
|
+
the reworked drawing tools or it doesn't.
|
|
637
|
+
|
|
638
|
+
Related config, independent of the flag: the position tools show quantity and
|
|
639
|
+
money only when [`account`](#accountspec) is supplied, and pips on both the
|
|
640
|
+
position tools and the ruler come from [`instrument`](#instrumentspec) — without
|
|
641
|
+
it, pip size is inferred from the feed's decimal count.
|
|
642
|
+
|
|
643
|
+
### `AccountSpec`
|
|
644
|
+
|
|
645
|
+
What the **Long / Short position** tools size themselves against.
|
|
646
|
+
|
|
647
|
+
TradingView asks the user to type these because it has no broker connection. We
|
|
648
|
+
do have one — so pass real equity and keep it current.
|
|
649
|
+
|
|
650
|
+
```ts
|
|
651
|
+
chart.setAccount({ size: 10_000, riskPercent: 1 });
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
| Field | Default | Description |
|
|
655
|
+
|---|---|---|
|
|
656
|
+
| `size` | — | Account equity in the account currency |
|
|
657
|
+
| `riskPercent` | `1` | Percent of the account risked per trade |
|
|
658
|
+
|
|
659
|
+
Omit the block and the position tools still draw — price, percent, pips and
|
|
660
|
+
risk/reward all render; only the quantity and money amounts are left out.
|
|
661
|
+
|
|
662
|
+
> A sketch drawn against a stale balance reports the wrong quantity rather than
|
|
663
|
+
> failing visibly, so push `setAccount()` whenever equity moves.
|
|
664
|
+
|
|
440
665
|
### `OHLCVBar`
|
|
441
666
|
|
|
442
667
|
| Field | Required | Type | Description |
|
|
@@ -477,7 +702,7 @@ import { DEFAULT_UI_CONFIG } from 'acttrader-charts';
|
|
|
477
702
|
|
|
478
703
|
| Component key | Configurable properties |
|
|
479
704
|
|---|---|
|
|
480
|
-
| `drawingToolbar` | `iconBtnSize`, `iconFontSize`, `iconFontFamily`, `flyoutLabelFontSize`, `flyoutLabelFontFamily`, `shortcutFontSize`, `soonBadgeFontSize`, `flyoutHeadingFontSize`, `flyoutHeadingLetterSpacing`, `scrollBtnHeight`, `scrollBtnFontSize`, `barPadding`, `btnGap` |
|
|
705
|
+
| `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
706
|
| `topBar` | `height`, `dropBtnFontSize`, `dropBtnFontFamily`, `drawBtnSize`, `drawBtnIconFontSize`, `iconBtnSize`, `mobileIconBtnSize`, `mobileDrawBtnIconSize`, `flyoutRowFontSize`, `flyoutRowFontFamily`, `flyoutCategoryFontSize`, `flyoutCategoryLetterSpacing`, `flyoutCheckFontSize`, `streamDotSize` |
|
|
482
707
|
| `bottomBar` | `height`, `btnFontSize`, `btnFontFamily` |
|
|
483
708
|
| `priceAxis` | `fontSize`, `fontFamily` |
|
|
@@ -555,6 +780,11 @@ new ChartEngine({
|
|
|
555
780
|
| `topBar.series` | `area` | `'Area'` | |
|
|
556
781
|
| `topBar` | `indicatorsBtn` | `'Indicators'` | Indicators button (no active) |
|
|
557
782
|
| `topBar` | `indicatorsBtnActive` | `'Indicators ({count})'` | `{count}` replaced at runtime |
|
|
783
|
+
| `topBar` | `disableCrosshairTitle` | `'Disable crosshair'` | Header crosshair switch tooltip while on (`enableCrossHairHeader`) |
|
|
784
|
+
| `topBar` | `enableCrosshairTitle` | `'Enable crosshair'` | Header crosshair switch tooltip while off |
|
|
785
|
+
| `topBar` | `pnlPillBtn` | `'P/L'` | Header P/L pill switch label (`features.pnlPill`) |
|
|
786
|
+
| `topBar` | `hidePnlPillTitle` | `'Hide P/L pill for {symbol}'` | P/L switch tooltip while the pill is shown |
|
|
787
|
+
| `topBar` | `showPnlPillTitle` | `'Show P/L pill for {symbol}'` | P/L switch tooltip while the pill is hidden |
|
|
558
788
|
| `topBar` | `toggleDrawingTitle` | `'Toggle Drawing Tools'` | Toolbar toggle tooltip |
|
|
559
789
|
| `topBar` | `toggleFullscreenTitle` | `'Toggle Fullscreen'` | Fullscreen button tooltip |
|
|
560
790
|
| `topBar` | `exitFullscreenTitle` | `'Exit Fullscreen'` | |
|
|
@@ -597,6 +827,16 @@ new ChartEngine({
|
|
|
597
827
|
| `trade` | `market` | `'Market'` | |
|
|
598
828
|
| `trade` | `qty` | `'Qty'` | Draft order qty input label |
|
|
599
829
|
| `trade` | `placeOrderTitle` | `'Place order at this price'` | Trade button tooltip |
|
|
830
|
+
| `trade` | `actionAlert` … `actionTsl` | `'Alert'`, `'Pending'`, `'SL'`, `'TP'`, `'TSL'` | Trade action bar chips (`features.tradeActionBar`) |
|
|
831
|
+
| `trade` | `actionCloseTitle` | `'Close'` | Trade action bar × tooltip |
|
|
832
|
+
| `trade` | `directionAbove`, `directionBelow` | `'Above'`, `'Below'` | Pending sub-menu header — side of the market the clicked price is on |
|
|
833
|
+
| `trade` | `actionBulkNoPositionsTitle`, `actionBulkMixedTitle` | `'No open {symbol} positions'`, `'Bulk levels need all {symbol} positions in one direction'` | SL / TP / TSL chips greyed out — why |
|
|
834
|
+
| `pnlPill` | `positionsAbbrev` | `'POS'` | On-chart P/L pill — word after the count (`3 POS`), shown uppercase |
|
|
835
|
+
| `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 |
|
|
836
|
+
| `pnlPill` | `hidePositionsTitle` | `'Hide positions on chart · drag handle to move · double-click to reset'` | Pill tooltip while fanned out |
|
|
837
|
+
| `pnlPill` | `bulkLevelsTitle` | `'Set SL · TP · TSL on all {count} {symbol} positions'` | ◎ tooltip — opens the host's Bulk Levels dialog |
|
|
838
|
+
| `pnlPill` | `bulkLevelsMixedTitle` | `'Bulk levels need all {count} {symbol} positions in one direction'` | ◎ tooltip while greyed out for mixed sides |
|
|
839
|
+
| `pnlPill` | `closeAllTitle` | `'Close all {symbol} positions'` | Amber ✕ tooltip; `{symbol}` and `{count}` are filled in |
|
|
600
840
|
| `dialogs.settings` | `title` | `'Chart Settings'` | Dialog title |
|
|
601
841
|
| `dialogs.settings` | `tfcSection` | `'Trade from Charts'` | Section heading |
|
|
602
842
|
| `dialogs.settings` | `showLabel` | `'Show'` | Radio group label |
|
|
@@ -637,8 +877,8 @@ chart.setTheme('light');
|
|
|
637
877
|
|---|---|---|
|
|
638
878
|
| `background` | `string` | Canvas background color |
|
|
639
879
|
| `grid` | `string` | Grid line color |
|
|
640
|
-
| `axisText` | `string` | Axis tick label color |
|
|
641
|
-
| `axisBorder` | `string` | Axis border / separator color |
|
|
880
|
+
| `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 |
|
|
881
|
+
| `axisBorder` | `string` | Axis border / separator color. Also the default border colour for the chart chrome (dialog frames, toolbar dividers) — same split as `axisText` |
|
|
642
882
|
| `crosshair` | `string` | Crosshair line color |
|
|
643
883
|
| `tooltip` | `{background, text, border}` | Floating label colors |
|
|
644
884
|
| `candle` | `{up, down, wickUp, wickDown}` | Candlestick colors |
|
|
@@ -673,7 +913,7 @@ chart.resetData(): this
|
|
|
673
913
|
|
|
674
914
|
**Symbol switch pattern:**
|
|
675
915
|
```ts
|
|
676
|
-
chart.setSymbol('GBPUSD').resetData();
|
|
916
|
+
chart.setSymbol('GBPUSD').setInstrument({ pipSize: 0.0001 }).resetData();
|
|
677
917
|
// … fetch new bars …
|
|
678
918
|
chart.loadData(newBars);
|
|
679
919
|
```
|
|
@@ -682,8 +922,15 @@ chart.loadData(newBars);
|
|
|
682
922
|
|
|
683
923
|
```ts
|
|
684
924
|
chart.setSeries(series: SeriesType): this
|
|
925
|
+
chart.setInstrument(spec: InstrumentSpec | undefined): this // contract specs for pips / money readouts
|
|
926
|
+
chart.getInstrument(): ResolvedInstrument // specs with fallbacks applied
|
|
927
|
+
chart.setAccount(account: AccountSpec | undefined): this // equity + risk for the position tools
|
|
928
|
+
chart.getAccount(): AccountSpec | undefined
|
|
685
929
|
chart.setTheme('dark' | 'light'): this
|
|
686
930
|
chart.setTimeframe(tf: Timeframe): this // change timeframe and reload data
|
|
931
|
+
chart.setDuration(d: Duration, timeframe?: Timeframe): this // select a duration; pairs the timeframe and reloads
|
|
932
|
+
chart.getDuration(): Duration | null // currently selected duration
|
|
933
|
+
chart.setBracketLabelMode(mode, currencySymbol?): this // SL/TP pills: 'price' (default) or 'amount'
|
|
687
934
|
chart.setThemeOverrides(overrides: ThemeOverrides): this // update per-theme color overrides at runtime
|
|
688
935
|
chart.setTimezone(tz: string): this // change display timezone at runtime
|
|
689
936
|
chart.setVolume(show: boolean): this
|
|
@@ -722,6 +969,7 @@ chart.cancelCurrentEdit(): this // cancel the active draft order or any in-pro
|
|
|
722
969
|
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
970
|
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
971
|
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
|
|
972
|
+
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
973
|
```
|
|
726
974
|
|
|
727
975
|
> **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 +978,33 @@ chart.setTfcActive(enabled: boolean): this // toggle TFC on/off at runtime; hi
|
|
|
730
978
|
|
|
731
979
|
```ts
|
|
732
980
|
chart.setOrderLots(lots: number): void // update default qty at runtime
|
|
981
|
+
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
|
|
733
982
|
```
|
|
734
983
|
|
|
984
|
+
The button follows the crosshair's horizontal line, so it is only ever shown while the
|
|
985
|
+
crosshair is (`crosshairEnabled`, default `true`). Hosts with a crosshair on/off control
|
|
986
|
+
should call `setCrosshairEnabled()` rather than painting the crosshair transparent — the
|
|
987
|
+
latter leaves the button riding an invisible line.
|
|
988
|
+
|
|
989
|
+
### Header crosshair switch — `enableCrossHairHeader`
|
|
990
|
+
|
|
991
|
+
```ts
|
|
992
|
+
const chart = new ChartEngine({
|
|
993
|
+
container,
|
|
994
|
+
enableCrossHairHeader: true, // crosshair icon in the header; the crosshair itself starts on
|
|
995
|
+
});
|
|
996
|
+
chart.on('crosshairToggle', ({ enabled }) => saveUserPref('crosshair', enabled));
|
|
997
|
+
```
|
|
998
|
+
|
|
999
|
+
With the flag on, a crosshair icon sits in the header's right cluster (advanced toolbar and
|
|
1000
|
+
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
|
|
1001
|
+
the icon hides the crosshair — and the floating trade button that rides on it — and the icon
|
|
1002
|
+
drops to its plain state; clicking again brings both back. Each click goes through
|
|
1003
|
+
`setCrosshairEnabled()` and emits `crosshairToggle`, so a host can persist the choice and seed
|
|
1004
|
+
it on the next load with `crosshairEnabled: false`. `enableCrosshairToggle` is the deprecated
|
|
1005
|
+
name of the same flag and still works. Native wrappers get the same switch via their
|
|
1006
|
+
`enableCrossHairHeader` init option, the `onCrosshairToggle` callback and `setCrosshairEnabled()`.
|
|
1007
|
+
|
|
735
1008
|
### Viewport
|
|
736
1009
|
|
|
737
1010
|
```ts
|
|
@@ -888,6 +1161,37 @@ Sub-pane heights are user-resizable by dragging the separator between panes.
|
|
|
888
1161
|
| `flatChannel` | Flat Channel | 2 clicks |
|
|
889
1162
|
| `disjointChannel` | Disjoint Channel | 3 clicks |
|
|
890
1163
|
|
|
1164
|
+
### SL/TP bracket labels — price or amount
|
|
1165
|
+
|
|
1166
|
+
By default an SL/TP pill reads `SL 4159.00`. With `bracketLabelMode: 'amount'`
|
|
1167
|
+
it reads `SL -$290.80` — the money the position gains or loses if that bracket
|
|
1168
|
+
is hit, currency symbol in front.
|
|
1169
|
+
|
|
1170
|
+
The chart has no access to contract specs or the account currency, so each level
|
|
1171
|
+
supplies what the maths needs:
|
|
1172
|
+
|
|
1173
|
+
```ts
|
|
1174
|
+
new ChartEngine({ container, bracketLabelMode: 'amount', currencySymbol: '$' });
|
|
1175
|
+
|
|
1176
|
+
chart.setLevels([{
|
|
1177
|
+
label: 'POS-1', price: 4173.54, side: 'buy', lots: 0.20,
|
|
1178
|
+
stopLossPrice: 4159.00, takeProfitPrice: 4183.00,
|
|
1179
|
+
contractSize: 100, // units per lot (100 for XAUUSD, 100000 for most FX)
|
|
1180
|
+
valuePerPoint: 1, // account-currency value of one price unit
|
|
1181
|
+
currencySymbol: '$', // optional per-level override
|
|
1182
|
+
}], 'label', 'price', 'position');
|
|
1183
|
+
// pills render: SL -$290.80 TP +$189.20
|
|
1184
|
+
```
|
|
1185
|
+
|
|
1186
|
+
```
|
|
1187
|
+
amount = (bracket − entry) × direction × lots × contractSize × valuePerPoint
|
|
1188
|
+
```
|
|
1189
|
+
|
|
1190
|
+
`valuePerPoint` is where a quote → account currency conversion goes. A level
|
|
1191
|
+
missing `lots` or `contractSize` keeps showing its price, so a partial rollout
|
|
1192
|
+
degrades level by level rather than rendering `NaN`. Toggle at runtime with
|
|
1193
|
+
`chart.setBracketLabelMode('amount')`.
|
|
1194
|
+
|
|
891
1195
|
### Fibonacci & Gann
|
|
892
1196
|
|
|
893
1197
|
`fibRetracement` · `fibExtension` · `fibChannel` · `fibTimezone` · `fibCircles` · `fibSpiral` · `fibFan` · `fibProjection` · `gannFan` · `gannSquare` · `gannBox`
|
|
@@ -920,10 +1224,78 @@ In a **per-drawing** style (`SerializedDrawing.style.levels`), a level may also
|
|
|
920
1224
|
|
|
921
1225
|
`rectangle` · `rotatedRectangle` · `ellipse` · `triangle` · `circle` · `arc` · `polyline` · `path` · `brush` · `highlighter`
|
|
922
1226
|
|
|
1227
|
+
**`brush` and `highlighter` draw freehand** when `features.enableForecasting` is on
|
|
1228
|
+
(see [Feature flags](#feature-flags)): press, drag, release — the pointer
|
|
1229
|
+
path is sampled continuously and the finished stroke is thinned to the points
|
|
1230
|
+
that carry its shape. (`polyline` and `path` remain click-per-point, ended with a
|
|
1231
|
+
double-click.)
|
|
1232
|
+
|
|
923
1233
|
### Measurements & Annotations
|
|
924
1234
|
|
|
925
1235
|
`priceRange` · `dateRange` · `datePriceRange` · `ruler` · `priceLabel` · `priceNote` · `priceProjection` · `projection` · `arrowUp` · `arrowDown` · `arrowMarker` · `text` · `callout` · `anchoredNote` · `flag` · `sineLine` · `regressionTrend` · `ghostFeed`
|
|
926
1236
|
|
|
1237
|
+
**`ruler`**, with `features.enableForecasting` on (see [Feature flags](#feature-flags)),
|
|
1238
|
+
draws a direction-tinted box over the span it measures and reports:
|
|
1239
|
+
|
|
1240
|
+
```
|
|
1241
|
+
0.00455 (0.80%) 45.5
|
|
1242
|
+
43 bars, 9d 4h
|
|
1243
|
+
Vol 102.27K
|
|
1244
|
+
```
|
|
1245
|
+
|
|
1246
|
+
Price delta, percent, and **pips**; bar count and calendar duration; volume
|
|
1247
|
+
traded across the span.
|
|
1248
|
+
|
|
1249
|
+
A pip figure is always shown — but it is only *correct* when the host supplies
|
|
1250
|
+
[`instrument.pipSize`](#instrumentspec). Without it the chart infers pip size
|
|
1251
|
+
from how many decimals the feed quotes, which follows the usual FX convention
|
|
1252
|
+
and is wrong for metals, indices and crypto.
|
|
1253
|
+
|
|
1254
|
+
Duration needs bar timestamps, and the volume line is omitted entirely when the
|
|
1255
|
+
bars carry no volume, rather than showing a `0` that would read as "no volume
|
|
1256
|
+
traded".
|
|
1257
|
+
|
|
1258
|
+
The box takes its colour from `candle.up` / `candle.down` so a measurement never
|
|
1259
|
+
disagrees with the candles underneath it; picking a colour in the style popover
|
|
1260
|
+
overrides that. All four corners resize.
|
|
1261
|
+
|
|
1262
|
+
With the flag off it reports the bar count and raw price delta, as it always has.
|
|
1263
|
+
|
|
1264
|
+
### Forecasting
|
|
1265
|
+
|
|
1266
|
+
`longPosition` · `shortPosition`
|
|
1267
|
+
|
|
1268
|
+
> Requires `features.enableForecasting` — see [Feature flags](#feature-flags).
|
|
1269
|
+
> With the flag off the group is not rendered, on the desktop toolbar or in the
|
|
1270
|
+
> mobile tools sheet.
|
|
1271
|
+
>
|
|
1272
|
+
> `ghostFeed` and `projection` stay in **Advanced Tools** and are ungated — they
|
|
1273
|
+
> predate this group.
|
|
1274
|
+
|
|
1275
|
+
**`longPosition` / `shortPosition`** sketch a trade that hasn't been placed: a
|
|
1276
|
+
green profit zone from entry to target, a red risk zone from entry to stop, and
|
|
1277
|
+
live readouts.
|
|
1278
|
+
|
|
1279
|
+
```
|
|
1280
|
+
Target: 0.00313 (0.549%) 31.3, Amount: 5622.13
|
|
1281
|
+
Open PnL: 0.00146, Qty: 11323
|
|
1282
|
+
Risk/reward ratio: 1.84
|
|
1283
|
+
Stop: 0.00170 (0.298%) 17.0, Amount: 2887.5
|
|
1284
|
+
```
|
|
1285
|
+
|
|
1286
|
+
Two clicks place it — entry, then target — and the stop lands at a 2:1
|
|
1287
|
+
reward:risk for you to drag. All three prices have handles.
|
|
1288
|
+
|
|
1289
|
+
Quantity is sized so that hitting the stop costs exactly `riskPercent` of the
|
|
1290
|
+
account, which needs [`account`](#accountspec); the money amounts and pips also
|
|
1291
|
+
need [`instrument`](#instrumentspec). Without them the tool still draws and still
|
|
1292
|
+
reports price, percent and risk/reward.
|
|
1293
|
+
|
|
1294
|
+
> **This is a drawing, not an order.** Trade-From-Chart (`setLevels`) is what
|
|
1295
|
+
> puts real broker orders on the chart — see [Trade From Chart
|
|
1296
|
+
> (TFC)](#trade-from-chart-tfc--setlevels). Nothing on a position tool reaches
|
|
1297
|
+
> the broker.
|
|
1298
|
+
|
|
927
1299
|
### Volume Profile
|
|
928
1300
|
|
|
929
1301
|
`volumeProfile` · `anchoredVP` · `fixedRangeVP`
|
|
@@ -932,6 +1304,10 @@ In a **per-drawing** style (`SerializedDrawing.style.levels`), a level may also
|
|
|
932
1304
|
|
|
933
1305
|
`cyclicLines` · `timeCycles`
|
|
934
1306
|
|
|
1307
|
+
> `ghostFeed` and `projection` remain here and are ungated. TradingView files
|
|
1308
|
+
> them under Forecasting, but moving them would have reshuffled the toolbar for
|
|
1309
|
+
> hosts that never enabled the new group.
|
|
1310
|
+
|
|
935
1311
|
### Harmonic & Elliott Patterns
|
|
936
1312
|
|
|
937
1313
|
`abcdPattern` · `headShoulders` · `trianglePattern` · `batPattern` · `butterflyPattern` · `crabPattern` · `gartleyPattern` · `cypherPattern` · `sharkPattern` · `threeDrives` · `elliottImpulse` · `elliottCorrective` · `elliottTriangle` · `elliottCombination` · `elliottWxy`
|
|
@@ -952,8 +1328,9 @@ chart.on('pan', ({ viewport }) => {});
|
|
|
952
1328
|
chart.on('timeframeChange',({ timeframe }) => {});
|
|
953
1329
|
chart.on('durationChange', ({ duration, timeframe }) => {});
|
|
954
1330
|
chart.on('seriesChange', ({ series }) => {});
|
|
1331
|
+
chart.on('priceSourceChange', ({ source }) => {}); // header BID/ASK/LTP dropdown pick
|
|
955
1332
|
chart.on('streamStatus', ({ status }) => {}); // 'connected' | 'reconnecting' | 'disconnected'
|
|
956
|
-
chart.on('dataLoaded', ({ timeframe, interval, start, end }) => {});
|
|
1333
|
+
chart.on('dataLoaded', ({ timeframe, interval, start, end, priceSource }) => {});
|
|
957
1334
|
chart.on('newBar', ({ completedBar, openingBar, intervalMs }) => {});
|
|
958
1335
|
chart.on('indicatorAdded', ({ instanceId, shortName, params }) => {}); // a study instance was added — keep instanceId to remove it later
|
|
959
1336
|
chart.on('indicatorRemoved', ({ instanceId, shortName }) => {});
|
|
@@ -976,6 +1353,10 @@ chart.on('tradeLevelEditCancelled', ({ label, type, isFullscreen }) => {}); // E
|
|
|
976
1353
|
chart.on('draftInitiated', ({ side, price, orderType, isFullscreen }) => {}); // new draft order shown — open buy/sell form
|
|
977
1354
|
chart.on('draftCancelled', ({ label, isFullscreen }) => {}); // draft order dismissed without confirming
|
|
978
1355
|
chart.on('tfcToggle', ({ enabled }) => {}); // TFC toggled on or off via top bar button or setTfcActive()
|
|
1356
|
+
chart.on('crosshairToggle', ({ enabled }) => {}); // crosshair switched on or off — header switch (enableCrossHairHeader) or setCrosshairEnabled(); persist it here
|
|
1357
|
+
chart.on('pnlPillCloseAll', ({ count, pnl, labels, data, isFullscreen }) => {}); // the on-chart P/L pill's ✕ — close these positions (features.pnlPill)
|
|
1358
|
+
chart.on('pnlPillToggle', ({ visible }) => {}); // the header's P/L switch showed or hid the pill — persist it here (features.pnlPill)
|
|
1359
|
+
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
1360
|
chart.on('tradeLevelDragEnd', ({ label, type, newPrice, data }) => {}); // deprecated — use tradeLevelEdit
|
|
980
1361
|
chart.on('tradeLevelBracketDrag', ({ label, bracketType, newPrice, data }) => {}); // deprecated
|
|
981
1362
|
|
|
@@ -984,8 +1365,8 @@ chart.on('tradeLevelBracketDrag', ({ label, bracketType, newPrice, data }) => {}
|
|
|
984
1365
|
chart.on('orderLineMoveStart', ({ label, fromTimestamp, fromBarIndex, isFullscreen }) => {});
|
|
985
1366
|
chart.on('orderLineMoving', ({ label, toTimestamp, toBarIndex, isFullscreen }) => {}); // fires on every move
|
|
986
1367
|
chart.on('orderLineMoved', ({ label, fromTimestamp, toTimestamp, fromBarIndex, toBarIndex, data, isFullscreen }) => {
|
|
987
|
-
//
|
|
988
|
-
//
|
|
1368
|
+
// The chart already remembers the drop in localStorage (orderLineAnchorPersistence, default on).
|
|
1369
|
+
// Hook here only if you also want to persist the new time anchor server-side.
|
|
989
1370
|
});
|
|
990
1371
|
|
|
991
1372
|
// Fires after addLevelBracket() auto-places a bracket, delivering the computed price back to the caller
|