acttrader-charts 1.0.21 → 1.1.0-beta.6
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 +223 -5
- package/dist/{MACD-DGFPNwy9.d.cts → MACD-7Y0rnDBQ.d.cts} +363 -6
- package/dist/{MACD-DGFPNwy9.d.ts → MACD-7Y0rnDBQ.d.ts} +363 -6
- package/dist/index.cjs +6892 -1463
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +680 -77
- package/dist/index.d.ts +680 -77
- package/dist/index.js +6876 -1462
- 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 +93 -67
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ActCharts
|
|
2
2
|
|
|
3
3
|
A performant, zero-dependency stock charting library built on Canvas 2D.
|
|
4
4
|
Dual ESM/CJS output, TypeScript-first.
|
|
@@ -84,6 +84,35 @@ chart.removeIndicator('SMA');
|
|
|
84
84
|
|
|
85
85
|
Indicator pills appear inline above the chart (up to 2 visible; beyond that collapses into a dropdown).
|
|
86
86
|
|
|
87
|
+
#### Multiple instances of the same study
|
|
88
|
+
|
|
89
|
+
Parameterized studies support **multiple simultaneous instances** — e.g. EMA-20, EMA-50 and
|
|
90
|
+
EMA-200, or RSI-14 alongside RSI-7. Each instance gets an auto-cycled color so they're visually
|
|
91
|
+
distinct, and oscillators (RSI, MACD, …) each render in their own stacked sub-pane.
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
chart.addIndicatorByName('EMA'); // EMA-20 (default)
|
|
95
|
+
chart.addIndicatorByName('EMA'); // a 2nd EMA, different color
|
|
96
|
+
// or build instances directly with different params:
|
|
97
|
+
chart.addIndicator(new EMA(50, '#22c55e'));
|
|
98
|
+
chart.addIndicator(new EMA(200, '#ef4444'));
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Selecting an already-active study in the Studies dropdown **adds another instance**. Remove a
|
|
102
|
+
specific instance via its pill **×** or the settings dialog's Remove button.
|
|
103
|
+
|
|
104
|
+
Each instance has a unique `instanceId` (e.g. `"EMA#3"`). Listen for it via the `indicatorAdded`
|
|
105
|
+
event and pass it to `removeIndicator` to target one instance:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
chart.on('indicatorAdded', ({ instanceId, shortName, params }) => { /* track it */ });
|
|
109
|
+
chart.removeIndicator('EMA#3'); // remove that one instance
|
|
110
|
+
chart.removeIndicator('EMA'); // (back-compat) remove ALL EMA instances
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
A handful of studies remain **single-instance** (re-selecting toggles them off): `VOL`, `OBV`,
|
|
114
|
+
`A/D`, `AO`, `VWAP`, `Ichimoku`, `PSAR`, `Pivot`, and Heikin-Ashi.
|
|
115
|
+
|
|
87
116
|
### Drawing Tools
|
|
88
117
|
|
|
89
118
|
```ts
|
|
@@ -115,6 +144,50 @@ To push ticks directly without an adapter (e.g. from your own WebSocket handler)
|
|
|
115
144
|
chart.pushTick({ time: Date.now(), bid: 1.2055, ask: 1.2057, volume: 120 });
|
|
116
145
|
```
|
|
117
146
|
|
|
147
|
+
### Compare symbols
|
|
148
|
+
|
|
149
|
+
Overlay one or more comparison instruments on the main chart, normalized to
|
|
150
|
+
percent change from the leftmost visible bar — TradingView-style. Compares are
|
|
151
|
+
historical-only (no live streaming) and persist across primary symbol /
|
|
152
|
+
timeframe switches; the library refetches them automatically against the new
|
|
153
|
+
primary range whenever the chart's range changes.
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
const chart = new ChartEngine({
|
|
157
|
+
container: document.getElementById('chart')!,
|
|
158
|
+
symbol: 'AAPL',
|
|
159
|
+
isins: ['AAPL', 'MSFT', 'GOOG', 'SPY', 'NVDA'],
|
|
160
|
+
headerLayout: 'advanced', // Compare button lives in this toolbar
|
|
161
|
+
initialCompares: ['SPY'], // optional — added on first dataLoaded
|
|
162
|
+
maxCompares: 8, // optional — default 8
|
|
163
|
+
compareDataLoader: async ({ symbol, start, end, interval }) => {
|
|
164
|
+
const res = await fetch(
|
|
165
|
+
`/api/bars?symbol=${symbol}&interval=${interval}` +
|
|
166
|
+
`&start=${start.toISOString()}&end=${end.toISOString()}`
|
|
167
|
+
);
|
|
168
|
+
return res.json(); // OHLCVBar[]
|
|
169
|
+
},
|
|
170
|
+
dataLoader: async (params) => fetchBars('AAPL', params),
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Programmatic API
|
|
174
|
+
await chart.addCompare('MSFT');
|
|
175
|
+
chart.removeCompare('MSFT');
|
|
176
|
+
chart.clearCompares();
|
|
177
|
+
chart.getCompares(); // [{ symbol, color, status }]
|
|
178
|
+
|
|
179
|
+
// Events
|
|
180
|
+
chart.on('compareAdded', ({ symbol, color }) => console.log('+', symbol));
|
|
181
|
+
chart.on('compareRemoved', ({ symbol }) => console.log('-', symbol));
|
|
182
|
+
chart.on('compareError', ({ symbol, message }) => console.warn(symbol, message));
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
When any compare is active the Y-axis switches to percent (`+12.34%` /
|
|
186
|
+
`-5.67%`); removing every compare returns it to absolute-price labels.
|
|
187
|
+
The Compare button in the advanced toolbar opens an in-chart picker filtered
|
|
188
|
+
by `isins` (with the primary symbol and active compares hidden). To keep the
|
|
189
|
+
old consumer-owned flow, omit `compareDataLoader` and wire `onCompareClick`.
|
|
190
|
+
|
|
118
191
|
### Trade From Chart (TFC) — `setLevels`
|
|
119
192
|
|
|
120
193
|
TFC renders draggable price levels for open positions and pending orders.
|
|
@@ -252,6 +325,7 @@ When `enableTrading` is on and live BID/ASK data is streaming, hovering / activa
|
|
|
252
325
|
| `series` | | `"candlestick"` | Initial chart type |
|
|
253
326
|
| `showVolume` | | `true` | Show volume overlay |
|
|
254
327
|
| `showUI` | | `true` | Render top / bottom / left bars. When `false`, the loading overlay is also suppressed (mobile wrappers provide their own) |
|
|
328
|
+
| `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)` |
|
|
255
329
|
| `showDrawingTools` | | `true` | Show drawing toolbar and pencil button |
|
|
256
330
|
| `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` |
|
|
257
331
|
| `timeframe` | | `"1D"` | Initial timeframe |
|
|
@@ -274,23 +348,30 @@ When `enableTrading` is on and live BID/ASK data is streaming, hovering / activa
|
|
|
274
348
|
| `targetCandleWidth` | | `10` | Target px width per candle for auto-calculating initial bar count |
|
|
275
349
|
| `durationTimeframeMap` | | *(see below)* | Override duration → timeframe pairings |
|
|
276
350
|
| `dataLoader` | | — | `(params) => Promise<OHLCVBar[]>` auto-called on load / change |
|
|
351
|
+
| `compareDataLoader` | | — | `({ symbol, start, end, interval }) => Promise<OHLCVBar[]>` — fetches bars for a compare symbol. Set this to enable the library-owned Compare flow. |
|
|
352
|
+
| `initialCompares` | | — | Symbols to auto-add as compares once the initial primary range is loaded |
|
|
353
|
+
| `maxCompares` | | `8` | Maximum concurrent compare symbols. Adding beyond emits `compareError` |
|
|
277
354
|
| `themeOverrides` | | — | Deep-partial color overrides applied on top of the built-in dark/light themes |
|
|
278
355
|
| `uiConfig` | | `DEFAULT_UI_CONFIG` | Deep-partial size / font overrides per component |
|
|
279
356
|
| `labels` | | `DEFAULT_LABELS` | Deep-partial string overrides for i18n/translation |
|
|
280
357
|
| `tickClosePriceSource` | | `"bid"` | Which quote side drives live tick close/high/low (`"bid"` or `"ask"`) |
|
|
281
358
|
| `showBidAskLines` | | `false` | Show both bid and ask as dashed lines during a live stream |
|
|
282
359
|
| `showActLogo` | | `false` | Show the ACT watermark logo in the bottom-left corner |
|
|
283
|
-
| `showCandleCountdown` | | `true` | Show countdown timer on the live candle |
|
|
360
|
+
| `showCandleCountdown` | | `true` | Show countdown timer on the live candle (time axis) |
|
|
284
361
|
| `candleCountdownTimeframes` | | intraday only | Timeframes where the countdown appears; `'all'` or `Timeframe[]` |
|
|
362
|
+
| `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). |
|
|
285
363
|
| `maxSubPanes` | | `3` | Max simultaneous oscillator sub-panes |
|
|
286
364
|
| `tradeDisplayFilter` | | `"all"` | Which TFC levels are visible: `"all"` · `"positions"` · `"orders"` · `"none"` |
|
|
287
365
|
| `positionRenderStyle` | | auto | Force position render style: `"line"` or `"dot"` |
|
|
288
366
|
| `hideLevelConfirmCancel` | | `false` | Hide on-canvas ✓/✗ confirm-cancel buttons for TFC level edits |
|
|
367
|
+
| `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 |
|
|
368
|
+
| `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`. |
|
|
289
369
|
| `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)` |
|
|
290
370
|
| `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 |
|
|
291
371
|
| `levelClusteringEnabled` | | `true` | Enable trade-level fan-out clustering; overlapping levels group into expandable badges |
|
|
292
372
|
| `clusterThresholdDistance` | | `20` | Pixel proximity threshold for clustering (only when `levelClusteringEnabled` is `true`) |
|
|
293
|
-
| `hideSymbolAndTick` | | `false` | Hide the symbol name
|
|
373
|
+
| `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 |
|
|
374
|
+
| `hideOHLCV` | | `false` | Hide the OHLC(V) data strip (`O: H: L: C: V:`) in the top-left overlay. Independent of `hideSymbolAndTick` — set both to `true` to hide the entire overlay |
|
|
294
375
|
| `showBottomBar` | | `false` | Show the bottom duration-selector bar |
|
|
295
376
|
| `hideQtyButton` | | `false` | Hide the floating Qty input overlay on draft orders |
|
|
296
377
|
| `showQuantityField` | | `false` | Render an editable QTY pill at the left of the draft order info box. Clicking the pill opens a flyout input to edit the quantity before submitting |
|
|
@@ -551,10 +632,16 @@ chart.setVolume(show: boolean): this
|
|
|
551
632
|
### Indicators
|
|
552
633
|
|
|
553
634
|
```ts
|
|
554
|
-
chart.addIndicator(indicator: IIndicator): this
|
|
555
|
-
chart.
|
|
635
|
+
chart.addIndicator(indicator: IIndicator, initialParams?: IndicatorParams): this
|
|
636
|
+
chart.addIndicatorByName(shortName: string, params?: IndicatorParams): void
|
|
637
|
+
chart.removeIndicator(idOrShortName: string): this
|
|
556
638
|
```
|
|
557
639
|
|
|
640
|
+
- `addIndicator` / `addIndicatorByName` — add a study. Multi-instance studies add a new instance
|
|
641
|
+
per call (auto-cycled color); single-instance studies toggle. `params` overrides period/color/source.
|
|
642
|
+
- `removeIndicator` — pass an `instanceId` (e.g. `"EMA#3"`, from the `indicatorAdded` event) to remove
|
|
643
|
+
that one instance, or a `shortName` (e.g. `"EMA"`) to remove **all** instances of that type.
|
|
644
|
+
|
|
558
645
|
### Drawing Tools
|
|
559
646
|
|
|
560
647
|
```ts
|
|
@@ -782,6 +869,8 @@ chart.on('seriesChange', ({ series }) => {});
|
|
|
782
869
|
chart.on('streamStatus', ({ status }) => {}); // 'connected' | 'reconnecting' | 'disconnected'
|
|
783
870
|
chart.on('dataLoaded', ({ timeframe, interval, start, end }) => {});
|
|
784
871
|
chart.on('newBar', ({ completedBar, openingBar, intervalMs }) => {});
|
|
872
|
+
chart.on('indicatorAdded', ({ instanceId, shortName, params }) => {}); // a study instance was added — keep instanceId to remove it later
|
|
873
|
+
chart.on('indicatorRemoved', ({ instanceId, shortName }) => {});
|
|
785
874
|
chart.on('stateChange', ({ symbol, state }) => {
|
|
786
875
|
// Fires after every user-driven config change (timeframe, series, theme,
|
|
787
876
|
// indicators, drawings, volume). `state` is a full serializable snapshot.
|
|
@@ -813,6 +902,135 @@ chart.on('tradeLevelBracketActivated', ({ label, bracketType, price, isFullscree
|
|
|
813
902
|
|
|
814
903
|
---
|
|
815
904
|
|
|
905
|
+
## Multi-Pane Layouts
|
|
906
|
+
|
|
907
|
+
The library ships everything you need to put multiple synchronised `ChartEngine`s on one screen — preset definitions, a layout-picker popover, cross-pane sync, and a composite snapshot helper. The library *emits intent*; the host renders the grid.
|
|
908
|
+
|
|
909
|
+
### Enabling the layout & snapshot UI
|
|
910
|
+
|
|
911
|
+
Pass two flags to `ChartConfig` (and optionally pick a header variant):
|
|
912
|
+
|
|
913
|
+
```ts
|
|
914
|
+
const chart = new ChartEngine({
|
|
915
|
+
container,
|
|
916
|
+
headerLayout: 'advanced', // 'simple' (default) | 'advanced' | 'compact'
|
|
917
|
+
enableMultipleLayouts: true, // shows Layout button + 26 preset picker
|
|
918
|
+
enableSnapshot: true, // shows Snapshot button + Download/Copy popover
|
|
919
|
+
layoutSync: { symbol: false, interval: true }, // optional — seed the popover's sync toggles
|
|
920
|
+
});
|
|
921
|
+
|
|
922
|
+
chart.on('layoutChange', ({ presetId, preset, sync }) => {
|
|
923
|
+
// Mount / teardown panes to match preset.count
|
|
924
|
+
// Apply sync flags to your ChartGroup
|
|
925
|
+
});
|
|
926
|
+
|
|
927
|
+
chart.on('snapshot', ({ dataUrl, action }) => {
|
|
928
|
+
// Native wrappers can intercept here to save via platform APIs
|
|
929
|
+
});
|
|
930
|
+
```
|
|
931
|
+
|
|
932
|
+
### Seeding & restoring the popover's sync toggles
|
|
933
|
+
|
|
934
|
+
The layout popover's five sync toggles (`symbol` / `interval` / `crosshair` / `time` / `dateRange`) default to `DEFAULT_LAYOUT_SYNC`. To open the popover in a different state — e.g. restoring a user's saved preference — pass a **partial** `layoutSync` at construction (omitted keys keep their `DEFAULT_LAYOUT_SYNC` value):
|
|
935
|
+
|
|
936
|
+
```ts
|
|
937
|
+
const chart = new ChartEngine({
|
|
938
|
+
container,
|
|
939
|
+
enableMultipleLayouts: true,
|
|
940
|
+
layoutSync: savedSync ?? { symbol: false, interval: false, crosshair: false, time: false, dateRange: false },
|
|
941
|
+
});
|
|
942
|
+
```
|
|
943
|
+
|
|
944
|
+
To change the toggles on an **already-mounted** chart (e.g. mirroring a host-owned settings UI), call `setLayoutSync()`:
|
|
945
|
+
|
|
946
|
+
```ts
|
|
947
|
+
chart.setLayoutSync({ crosshair: true }); // partial — other toggles unchanged
|
|
948
|
+
const current = chart.getLayoutSync(); // read the current state
|
|
949
|
+
```
|
|
950
|
+
|
|
951
|
+
`setLayoutSync()` updates the popover's toggles in place and — unlike a user click inside the popover — does **not** emit a `layoutChange` event, so a host can call it in response to its own state without re-entrancy. It no-ops when the resolved state is unchanged.
|
|
952
|
+
|
|
953
|
+
### `ChartGroup` — cross-pane sync
|
|
954
|
+
|
|
955
|
+
`ChartGroup` listens to events on the *active* engine and mirrors them to the others:
|
|
956
|
+
|
|
957
|
+
| Sync flag | Source event | Action on other panes
|
|
958
|
+
| ------------ | ------------------ | ----------------------------------------
|
|
959
|
+
| `interval` | `timeframeChange` | `setTimeframe` (skip if equal)
|
|
960
|
+
| `crosshair` | `crosshair` | `timeToBarIndex` → `setCrosshair`
|
|
961
|
+
| `time` | `pan` | `setViewport({ startIndex, endIndex })`
|
|
962
|
+
| `dateRange` | `zoom` | `setViewport({ startIndex, endIndex })`
|
|
963
|
+
| `symbol` | push (`broadcastSymbol`) | `setSymbol` (host still fetches data)
|
|
964
|
+
|
|
965
|
+
```ts
|
|
966
|
+
import { ChartGroup, DEFAULT_LAYOUT_SYNC } from 'acttrader-charts';
|
|
967
|
+
|
|
968
|
+
const group = new ChartGroup({ sync: DEFAULT_LAYOUT_SYNC });
|
|
969
|
+
group.add('p1', engineA);
|
|
970
|
+
group.add('p2', engineB);
|
|
971
|
+
group.setActive('p1');
|
|
972
|
+
|
|
973
|
+
// react to user picking a preset
|
|
974
|
+
chart.on('layoutChange', ({ sync }) => group.setSync(sync));
|
|
975
|
+
|
|
976
|
+
// user picked a new symbol on the active pane
|
|
977
|
+
group.broadcastSymbol('AAPL', 'p1');
|
|
978
|
+
```
|
|
979
|
+
|
|
980
|
+
`ChartEngine.setViewport` / `setCrosshair` deliberately do **not** re-emit `pan` / `zoom` / `crosshair`, so the mirror is safe from feedback loops.
|
|
981
|
+
|
|
982
|
+
### Custom layout presets
|
|
983
|
+
|
|
984
|
+
Register an extra preset alongside the 26 built-ins; it appears in the picker automatically:
|
|
985
|
+
|
|
986
|
+
```ts
|
|
987
|
+
import { registerCustomPreset } from 'acttrader-charts';
|
|
988
|
+
|
|
989
|
+
registerCustomPreset({
|
|
990
|
+
id: 'my-3-wide-left',
|
|
991
|
+
count: 3,
|
|
992
|
+
cols: '2fr 1fr',
|
|
993
|
+
rows: '1fr 1fr',
|
|
994
|
+
areas: '"a b" "a c"',
|
|
995
|
+
areaOrder: ['a', 'b', 'c'],
|
|
996
|
+
icon: '<svg>…</svg>',
|
|
997
|
+
});
|
|
998
|
+
```
|
|
999
|
+
|
|
1000
|
+
Persistence is the host's job — serialise the preset to your store, call `registerCustomPreset` again on app boot.
|
|
1001
|
+
|
|
1002
|
+
### Composite snapshot of a multi-pane grid
|
|
1003
|
+
|
|
1004
|
+
`SnapshotPopover` captures the single active chart. For a whole-grid PNG, hand the cell rectangles to the group:
|
|
1005
|
+
|
|
1006
|
+
```ts
|
|
1007
|
+
const containerRect = gridEl.getBoundingClientRect();
|
|
1008
|
+
const rects = paneIds.map(id => {
|
|
1009
|
+
const cell = cellEls.get(id)!.getBoundingClientRect();
|
|
1010
|
+
return {
|
|
1011
|
+
paneId: id,
|
|
1012
|
+
x: cell.left - containerRect.left,
|
|
1013
|
+
y: cell.top - containerRect.top,
|
|
1014
|
+
w: cell.width,
|
|
1015
|
+
h: cell.height,
|
|
1016
|
+
};
|
|
1017
|
+
});
|
|
1018
|
+
|
|
1019
|
+
const dataUrl = group.toCompositeDataUrl({
|
|
1020
|
+
width: containerRect.width,
|
|
1021
|
+
height: containerRect.height,
|
|
1022
|
+
rects,
|
|
1023
|
+
});
|
|
1024
|
+
```
|
|
1025
|
+
|
|
1026
|
+
The composite excludes trade levels (positions, SL/TP) — same rule as `chart.toDataUrl()`.
|
|
1027
|
+
|
|
1028
|
+
### Native wrapper parity
|
|
1029
|
+
|
|
1030
|
+
`enableMultipleLayouts`, `enableSnapshot`, `headerLayout`, the `layoutChange` event, and the `snapshot` event are all exposed by [charts-android](../charts-android) and [charts-ios](../charts-ios) — see their READMEs for native usage.
|
|
1031
|
+
|
|
1032
|
+
---
|
|
1033
|
+
|
|
816
1034
|
## Custom Indicator
|
|
817
1035
|
|
|
818
1036
|
Implement `IIndicator` and pass it to `addIndicator()`:
|