acttrader-charts 1.3.0-beta.17 → 1.3.0-beta.19
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 +7 -2
- package/dist/{MACD-OBa3OUGO.d.cts → MACD-DDqzsv_V.d.cts} +6 -0
- package/dist/{MACD-OBa3OUGO.d.ts → MACD-DDqzsv_V.d.ts} +6 -0
- package/dist/index.cjs +63 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -2
- package/dist/index.d.ts +13 -2
- package/dist/index.js +63 -8
- 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 +16 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -395,7 +395,9 @@ Alert | Pending | SL | TP | TSL | ×
|
|
|
395
395
|
row creates the draft order exactly as before and closes both.
|
|
396
396
|
- **SL / TP / TSL** emit `bulkLevelsRequested` with `{ kind, price }` and close the
|
|
397
397
|
bar — the host opens its Bulk Levels dialog pre-focused on that kind. The chart
|
|
398
|
-
never calls a broker.
|
|
398
|
+
never calls a broker. The chips grey out, with a tooltip saying why, when the
|
|
399
|
+
symbol has no open positions or has buys and sells open at once — one stop or
|
|
400
|
+
target price cannot sit on the right side of the market for both directions.
|
|
399
401
|
- **×**, a click elsewhere, Escape, TFC off and `dismissAllUI()` all close it.
|
|
400
402
|
|
|
401
403
|
Chip labels and the × tooltip live under `labels.trade` (`actionAlert`, `actionPending`,
|
|
@@ -424,7 +426,8 @@ the total without opening the positions panel:
|
|
|
424
426
|
- **◎** (the same target glyph as the Bulk Levels dialog header) emits
|
|
425
427
|
`bulkLevelsRequested` with `source: 'pill'` and a `null` price, so your Bulk Levels
|
|
426
428
|
dialog opens pre-scoped to every open position on the symbol. Tooltip:
|
|
427
|
-
"Set SL · TP · TSL on all N {symbol} positions".
|
|
429
|
+
"Set SL · TP · TSL on all N {symbol} positions". Greyed out while buys and sells are
|
|
430
|
+
both open ("Bulk levels need all N {symbol} positions in one direction").
|
|
428
431
|
- **✕** (amber) emits `pnlPillCloseAll` with every counted position. Your app
|
|
429
432
|
confirms and closes them; the chart never calls a broker.
|
|
430
433
|
|
|
@@ -825,10 +828,12 @@ new ChartEngine({
|
|
|
825
828
|
| `trade` | `actionAlert` … `actionTsl` | `'Alert'`, `'Pending'`, `'SL'`, `'TP'`, `'TSL'` | Trade action bar chips (`features.tradeActionBar`) |
|
|
826
829
|
| `trade` | `actionCloseTitle` | `'Close'` | Trade action bar × tooltip |
|
|
827
830
|
| `trade` | `directionAbove`, `directionBelow` | `'Above'`, `'Below'` | Pending sub-menu header — side of the market the clicked price is on |
|
|
831
|
+
| `trade` | `actionBulkNoPositionsTitle`, `actionBulkMixedTitle` | `'No open {symbol} positions'`, `'Bulk levels need all {symbol} positions in one direction'` | SL / TP / TSL chips greyed out — why |
|
|
828
832
|
| `pnlPill` | `positionsAbbrev` | `'POS'` | On-chart P/L pill — word after the count (`3 POS`), shown uppercase |
|
|
829
833
|
| `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 |
|
|
830
834
|
| `pnlPill` | `hidePositionsTitle` | `'Hide positions on chart · drag handle to move · double-click to reset'` | Pill tooltip while fanned out |
|
|
831
835
|
| `pnlPill` | `bulkLevelsTitle` | `'Set SL · TP · TSL on all {count} {symbol} positions'` | ◎ tooltip — opens the host's Bulk Levels dialog |
|
|
836
|
+
| `pnlPill` | `bulkLevelsMixedTitle` | `'Bulk levels need all {count} {symbol} positions in one direction'` | ◎ tooltip while greyed out for mixed sides |
|
|
832
837
|
| `pnlPill` | `closeAllTitle` | `'Close all {symbol} positions'` | Amber ✕ tooltip; `{symbol}` and `{count}` are filled in |
|
|
833
838
|
| `dialogs.settings` | `title` | `'Chart Settings'` | Dialog title |
|
|
834
839
|
| `dialogs.settings` | `tfcSection` | `'Trade from Charts'` | Section heading |
|
|
@@ -345,6 +345,10 @@ interface TradeLabels {
|
|
|
345
345
|
/** Pending sub-menu header: which side of the market the clicked price is on. */
|
|
346
346
|
directionAbove: string;
|
|
347
347
|
directionBelow: string;
|
|
348
|
+
/** SL / TP / TSL chips greyed out: no open positions on the symbol. `{symbol}` is filled in. */
|
|
349
|
+
actionBulkNoPositionsTitle: string;
|
|
350
|
+
/** SL / TP / TSL chips greyed out: buys and sells both open — one price cannot suit both. */
|
|
351
|
+
actionBulkMixedTitle: string;
|
|
348
352
|
}
|
|
349
353
|
interface DialogLabels {
|
|
350
354
|
/** Chart Settings dialog. */
|
|
@@ -402,6 +406,8 @@ interface PnlPillLabels {
|
|
|
402
406
|
hidePositionsTitle: string;
|
|
403
407
|
/** ◎ tooltip — it opens the host's Bulk Levels dialog. `{count}` and `{symbol}` are filled in. */
|
|
404
408
|
bulkLevelsTitle: string;
|
|
409
|
+
/** ◎ tooltip while greyed out because buys and sells are both open. */
|
|
410
|
+
bulkLevelsMixedTitle: string;
|
|
405
411
|
/** Amber ✕ tooltip. */
|
|
406
412
|
closeAllTitle: string;
|
|
407
413
|
}
|
|
@@ -345,6 +345,10 @@ interface TradeLabels {
|
|
|
345
345
|
/** Pending sub-menu header: which side of the market the clicked price is on. */
|
|
346
346
|
directionAbove: string;
|
|
347
347
|
directionBelow: string;
|
|
348
|
+
/** SL / TP / TSL chips greyed out: no open positions on the symbol. `{symbol}` is filled in. */
|
|
349
|
+
actionBulkNoPositionsTitle: string;
|
|
350
|
+
/** SL / TP / TSL chips greyed out: buys and sells both open — one price cannot suit both. */
|
|
351
|
+
actionBulkMixedTitle: string;
|
|
348
352
|
}
|
|
349
353
|
interface DialogLabels {
|
|
350
354
|
/** Chart Settings dialog. */
|
|
@@ -402,6 +406,8 @@ interface PnlPillLabels {
|
|
|
402
406
|
hidePositionsTitle: string;
|
|
403
407
|
/** ◎ tooltip — it opens the host's Bulk Levels dialog. `{count}` and `{symbol}` are filled in. */
|
|
404
408
|
bulkLevelsTitle: string;
|
|
409
|
+
/** ◎ tooltip while greyed out because buys and sells are both open. */
|
|
410
|
+
bulkLevelsMixedTitle: string;
|
|
405
411
|
/** Amber ✕ tooltip. */
|
|
406
412
|
closeAllTitle: string;
|
|
407
413
|
}
|
package/dist/index.cjs
CHANGED
|
@@ -336,6 +336,7 @@ function resolveCssVarsInTheme(theme, container) {
|
|
|
336
336
|
drawing: resolveDrawingThemeVars(theme.drawing, resolve)
|
|
337
337
|
};
|
|
338
338
|
container.removeChild(tmp);
|
|
339
|
+
tmp.remove();
|
|
339
340
|
return result;
|
|
340
341
|
}
|
|
341
342
|
function resolveDrawingThemeVars(drawing, resolve) {
|
|
@@ -744,6 +745,7 @@ var DEFAULT_LABELS = {
|
|
|
744
745
|
showPositionsTitle: "Show positions on chart \xB7 drag handle to move \xB7 double-click to reset",
|
|
745
746
|
hidePositionsTitle: "Hide positions on chart \xB7 drag handle to move \xB7 double-click to reset",
|
|
746
747
|
bulkLevelsTitle: "Set SL \xB7 TP \xB7 TSL on all {count} {symbol} positions",
|
|
748
|
+
bulkLevelsMixedTitle: "Bulk levels need all {count} {symbol} positions in one direction",
|
|
747
749
|
closeAllTitle: "Close all {symbol} positions"
|
|
748
750
|
},
|
|
749
751
|
topBar: {
|
|
@@ -932,7 +934,9 @@ var DEFAULT_LABELS = {
|
|
|
932
934
|
actionTsl: "TSL",
|
|
933
935
|
actionCloseTitle: "Close",
|
|
934
936
|
directionAbove: "Above",
|
|
935
|
-
directionBelow: "Below"
|
|
937
|
+
directionBelow: "Below",
|
|
938
|
+
actionBulkNoPositionsTitle: "No open {symbol} positions",
|
|
939
|
+
actionBulkMixedTitle: "Bulk levels need all {symbol} positions in one direction"
|
|
936
940
|
},
|
|
937
941
|
dialogs: {
|
|
938
942
|
settings: {
|
|
@@ -4426,7 +4430,8 @@ function withAlpha4(color, alpha) {
|
|
|
4426
4430
|
}
|
|
4427
4431
|
const rgb = /^rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)/i.exec(c);
|
|
4428
4432
|
if (rgb) return `rgba(${rgb[1]}, ${rgb[2]}, ${rgb[3]}, ${alpha})`;
|
|
4429
|
-
|
|
4433
|
+
const pct = Math.round(Math.min(1, Math.max(0, alpha)) * 100);
|
|
4434
|
+
return `color-mix(in srgb, ${c} ${pct}%, transparent)`;
|
|
4430
4435
|
}
|
|
4431
4436
|
|
|
4432
4437
|
// src/ui/keyboardAware.ts
|
|
@@ -5426,6 +5431,14 @@ var TopBar = class {
|
|
|
5426
5431
|
};
|
|
5427
5432
|
|
|
5428
5433
|
// src/ui/AdvancedToolbar.ts
|
|
5434
|
+
function resolveToolbarTheme(theme) {
|
|
5435
|
+
if (typeof document === "undefined" || !document.body) return theme;
|
|
5436
|
+
try {
|
|
5437
|
+
return resolveCssVarsInTheme(theme, document.body);
|
|
5438
|
+
} catch {
|
|
5439
|
+
return theme;
|
|
5440
|
+
}
|
|
5441
|
+
}
|
|
5429
5442
|
var TF_PILLS = ["1m", "5m", "15m", "1h", "4h", "1D", "1W"];
|
|
5430
5443
|
var TF_PILLS_COMPACT = ["1m", "5m", "15m", "1h", "4h", "1D"];
|
|
5431
5444
|
var TF_MORE = ["30m", "2h", "8h", "1W", "1M"];
|
|
@@ -5584,6 +5597,7 @@ var AdvancedToolbar = class {
|
|
|
5584
5597
|
this.priceSourceOptions = [];
|
|
5585
5598
|
this.currentPriceSource = "bid";
|
|
5586
5599
|
this.openFlyout = null;
|
|
5600
|
+
theme = resolveToolbarTheme(theme);
|
|
5587
5601
|
this.theme = theme;
|
|
5588
5602
|
this.overflowMenu = overflowMenu;
|
|
5589
5603
|
this.showSnapshotItem = showSnapshotBtn;
|
|
@@ -5923,6 +5937,7 @@ var AdvancedToolbar = class {
|
|
|
5923
5937
|
this.volHasData = hasVol;
|
|
5924
5938
|
}
|
|
5925
5939
|
setTheme(theme) {
|
|
5940
|
+
theme = resolveToolbarTheme(theme);
|
|
5926
5941
|
this.theme = theme;
|
|
5927
5942
|
this.applyBarStyles(theme);
|
|
5928
5943
|
this.applyFlyoutStyles(theme);
|
|
@@ -18266,18 +18281,22 @@ function summarisePositionLevels(levels, draftLabel = null) {
|
|
|
18266
18281
|
const positions = [];
|
|
18267
18282
|
let pnl = 0;
|
|
18268
18283
|
let hasPnl2 = false;
|
|
18284
|
+
let buys = 0;
|
|
18285
|
+
let sells = 0;
|
|
18269
18286
|
for (const level of levels) {
|
|
18270
18287
|
if (level.type !== "position") continue;
|
|
18271
18288
|
const position = level;
|
|
18272
18289
|
if (position.entryPriceEditable) continue;
|
|
18273
18290
|
if (draftLabel !== null && position.label === draftLabel) continue;
|
|
18274
18291
|
positions.push(position);
|
|
18292
|
+
if (position.side === "buy") buys++;
|
|
18293
|
+
else if (position.side === "sell") sells++;
|
|
18275
18294
|
if (typeof position.pnl === "number" && Number.isFinite(position.pnl)) {
|
|
18276
18295
|
pnl += position.pnl;
|
|
18277
18296
|
hasPnl2 = true;
|
|
18278
18297
|
}
|
|
18279
18298
|
}
|
|
18280
|
-
return { count: positions.length, pnl, hasPnl: hasPnl2, positions };
|
|
18299
|
+
return { count: positions.length, pnl, hasPnl: hasPnl2, positions, buys, sells, mixed: buys > 0 && sells > 0 };
|
|
18281
18300
|
}
|
|
18282
18301
|
function formatPnlPillValue(pnl, currency) {
|
|
18283
18302
|
const amount = Math.abs(pnl).toFixed(2);
|
|
@@ -18310,6 +18329,8 @@ var PnlPill = class {
|
|
|
18310
18329
|
this.lastSummary = null;
|
|
18311
18330
|
this.count = 0;
|
|
18312
18331
|
this.symbol = "";
|
|
18332
|
+
/** Buys and sells both open — ◎ is greyed out, one price cannot suit both. */
|
|
18333
|
+
this.mixed = false;
|
|
18313
18334
|
this.pnlTone = "none";
|
|
18314
18335
|
/** Set once the user has dragged; null means the default top-right spot. */
|
|
18315
18336
|
this.pos = null;
|
|
@@ -18484,6 +18505,7 @@ var PnlPill = class {
|
|
|
18484
18505
|
update(summary, symbol = "") {
|
|
18485
18506
|
this.count = summary.count;
|
|
18486
18507
|
this.symbol = symbol;
|
|
18508
|
+
this.mixed = summary.mixed;
|
|
18487
18509
|
this.lastSummary = summary;
|
|
18488
18510
|
if (summary.count === 0 || this.userHidden) {
|
|
18489
18511
|
this.el.style.display = "none";
|
|
@@ -18581,9 +18603,12 @@ var PnlPill = class {
|
|
|
18581
18603
|
}
|
|
18582
18604
|
applyTitles() {
|
|
18583
18605
|
this.el.title = this.fill(this.expanded ? this.labels.hidePositionsTitle : this.labels.showPositionsTitle);
|
|
18584
|
-
const bulk = this.fill(this.labels.bulkLevelsTitle);
|
|
18606
|
+
const bulk = this.fill(this.mixed ? this.labels.bulkLevelsMixedTitle : this.labels.bulkLevelsTitle);
|
|
18585
18607
|
this.toggleBtn.title = bulk;
|
|
18586
18608
|
this.toggleBtn.setAttribute("aria-label", bulk);
|
|
18609
|
+
this.toggleBtn.disabled = this.mixed;
|
|
18610
|
+
this.toggleBtn.setAttribute("aria-disabled", String(this.mixed));
|
|
18611
|
+
this.applyToggleColor();
|
|
18587
18612
|
const closeAll = this.fill(this.labels.closeAllTitle);
|
|
18588
18613
|
this.closeBtn.title = closeAll;
|
|
18589
18614
|
this.closeBtn.setAttribute("aria-label", closeAll);
|
|
@@ -18619,9 +18644,10 @@ var PnlPill = class {
|
|
|
18619
18644
|
const t = this.theme;
|
|
18620
18645
|
Object.assign(this.toggleBtn.style, {
|
|
18621
18646
|
color: t.ui.accent,
|
|
18622
|
-
background: withAlpha4(t.ui.accent, this.toggleHover ? 0.2 : 0.12),
|
|
18647
|
+
background: withAlpha4(t.ui.accent, this.toggleHover && !this.mixed ? 0.2 : 0.12),
|
|
18623
18648
|
border: "1px solid transparent",
|
|
18624
|
-
opacity: "1"
|
|
18649
|
+
opacity: this.mixed ? "0.4" : "1",
|
|
18650
|
+
cursor: this.mixed ? "not-allowed" : "pointer"
|
|
18625
18651
|
});
|
|
18626
18652
|
}
|
|
18627
18653
|
/* border-yellow-500/60, hover border-yellow-400 + bg-yellow-500/15; the ✕ is yellow-500. */
|
|
@@ -18839,6 +18865,7 @@ var TradeActionBar = class {
|
|
|
18839
18865
|
const wasVisible = this.isVisible;
|
|
18840
18866
|
this.price = price;
|
|
18841
18867
|
if (ctx) this.ctx = ctx;
|
|
18868
|
+
this.applyBulkAvailability();
|
|
18842
18869
|
this.closeSubmenu();
|
|
18843
18870
|
this.el.style.display = "block";
|
|
18844
18871
|
this.el.style.right = `${right}px`;
|
|
@@ -18897,11 +18924,34 @@ var TradeActionBar = class {
|
|
|
18897
18924
|
this.applyCloseColor();
|
|
18898
18925
|
}
|
|
18899
18926
|
/* Alert amber · Pending foreground/85 → primary on hover · SL/TSL negative · TP positive. */
|
|
18927
|
+
/*
|
|
18928
|
+
* SL / TP / TSL act on every open position of the symbol. With none there is
|
|
18929
|
+
* nothing to act on; with buys AND sells open, one stop or target price would
|
|
18930
|
+
* have to sit below the market for one side and above it for the other, so the
|
|
18931
|
+
* host's dialog could never accept it. Both states grey the chips out and say why.
|
|
18932
|
+
*/
|
|
18933
|
+
applyBulkAvailability() {
|
|
18934
|
+
const count = this.ctx.positionCount;
|
|
18935
|
+
const mixed = this.ctx.mixedSides === true;
|
|
18936
|
+
const unavailable = count === 0 || mixed;
|
|
18937
|
+
const symbol = this.ctx.symbol ?? "";
|
|
18938
|
+
const title = !unavailable ? "" : (count === 0 ? this.labels.actionBulkNoPositionsTitle : this.labels.actionBulkMixedTitle).replace(/\{symbol\}/g, symbol);
|
|
18939
|
+
for (const kind of ["sl", "tp", "tsl"]) {
|
|
18940
|
+
const btn = this.chips.get(kind);
|
|
18941
|
+
if (!btn) continue;
|
|
18942
|
+
btn.disabled = unavailable;
|
|
18943
|
+
btn.setAttribute("aria-disabled", String(unavailable));
|
|
18944
|
+
btn.title = title;
|
|
18945
|
+
btn.style.opacity = unavailable ? "0.4" : "1";
|
|
18946
|
+
btn.style.cursor = unavailable ? "not-allowed" : "pointer";
|
|
18947
|
+
this.applyChipColor(kind);
|
|
18948
|
+
}
|
|
18949
|
+
}
|
|
18900
18950
|
applyChipColor(kind) {
|
|
18901
18951
|
const btn = this.chips.get(kind);
|
|
18902
18952
|
if (!btn) return;
|
|
18903
18953
|
const t = this.theme;
|
|
18904
|
-
const hover = this.hovered === kind;
|
|
18954
|
+
const hover = this.hovered === kind && !btn.disabled;
|
|
18905
18955
|
let color;
|
|
18906
18956
|
let hoverBg;
|
|
18907
18957
|
switch (kind) {
|
|
@@ -26897,10 +26947,14 @@ var _ChartEngine = class _ChartEngine {
|
|
|
26897
26947
|
const { price, y } = this._tradeButtonClickData;
|
|
26898
26948
|
if (this.tradeActionBar) {
|
|
26899
26949
|
this.tradePopover.hide();
|
|
26950
|
+
const positions = summarisePositionLevels(this.levels, this.draftOrderLabel);
|
|
26900
26951
|
this.tradeActionBar.show(price, y, btnRight, {
|
|
26901
26952
|
pricePrecision: this.dataStore.getPricePrecision(),
|
|
26902
26953
|
buyOrderType: this._orderTypeForPrice("buy", price),
|
|
26903
|
-
sellOrderType: this._orderTypeForPrice("sell", price)
|
|
26954
|
+
sellOrderType: this._orderTypeForPrice("sell", price),
|
|
26955
|
+
symbol: this.symbol,
|
|
26956
|
+
positionCount: positions.count,
|
|
26957
|
+
mixedSides: positions.mixed
|
|
26904
26958
|
});
|
|
26905
26959
|
return;
|
|
26906
26960
|
}
|
|
@@ -27816,6 +27870,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
27816
27870
|
onToggleExpand: () => this.togglePnlPillExpanded(),
|
|
27817
27871
|
// ◎ hands every open position on the symbol to the host's Bulk Levels dialog.
|
|
27818
27872
|
onBulkLevels: () => {
|
|
27873
|
+
if (summarisePositionLevels(this.levels, this.draftOrderLabel).mixed) return;
|
|
27819
27874
|
this.emitter.emit("bulkLevelsRequested", {
|
|
27820
27875
|
kind: "sl",
|
|
27821
27876
|
price: null,
|