acttrader-charts 1.0.7 → 1.0.9
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 +21 -0
- package/dist/{MACD-CHD5mG7r.d.cts → MACD-CjC_rccv.d.cts} +9 -0
- package/dist/{MACD-CHD5mG7r.d.ts → MACD-CjC_rccv.d.ts} +9 -0
- package/dist/index.cjs +159 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +32 -3
- package/dist/index.d.ts +32 -3
- package/dist/index.js +159 -36
- 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 +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -194,6 +194,18 @@ chart.on('tradeLevelDrag', ({ label, newPrice, bracketType, isFullscreen }) => {
|
|
|
194
194
|
| `buy` | must stay ≤ order price | must stay ≥ order price |
|
|
195
195
|
| `sell` | must stay ≥ order price | must stay ≤ order price |
|
|
196
196
|
|
|
197
|
+
**Mobile mode — `hideLevelConfirmCancel: true`:**
|
|
198
|
+
|
|
199
|
+
When the on-canvas ✓/✗ buttons are hidden (mobile native UI), the chart adapts its interaction model:
|
|
200
|
+
|
|
201
|
+
- **Tap a trade level line** → `tradeLevelEditOpen` fires immediately (the whole line acts as the edit button; no pencil icon required).
|
|
202
|
+
- **Tap empty canvas area while a level is selected** → edit is dismissed and any pending drag changes are reverted (equivalent to pressing ✗).
|
|
203
|
+
- **Drag a SL/TP bracket and release** → `tradeLevelEdit` fires automatically without needing a ✓ button tap.
|
|
204
|
+
|
|
205
|
+
**Market orders — BID/ASK spread zone:**
|
|
206
|
+
|
|
207
|
+
When `enableTrading` is on and live BID/ASK data is streaming, hovering / activating the trade crosshair button while the cursor price is inside the spread shows **"Buy Market"** and **"Sell Market"** rows instead of the standard Limit/Stop rows. Tapping either row calls `showMarketDraft()` internally and emits `draftInitiated` with `orderType: 'market'`.
|
|
208
|
+
|
|
197
209
|
---
|
|
198
210
|
|
|
199
211
|
## Default Configuration
|
|
@@ -483,6 +495,9 @@ chart.setDrawingTool(tool: DrawingToolType | null): this
|
|
|
483
495
|
```ts
|
|
484
496
|
chart.setLevels(levels, labelField, priceField, type): this
|
|
485
497
|
chart.removeLevelByLabel(label: string): this
|
|
498
|
+
chart.cancelCurrentEdit(): this // cancel the active draft order or in-progress level edit; no-op when nothing is active
|
|
499
|
+
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
|
|
500
|
+
chart.setDraftBracketPnl(bracketType: 'sl' | 'tp', pnlText: string | null): this // set estimated P&L text on a draft order bracket line; pass null to clear
|
|
486
501
|
```
|
|
487
502
|
|
|
488
503
|
### Trade Button
|
|
@@ -661,6 +676,12 @@ chart.on('draftInitiated', ({ side, price, orderType, isFullscreen }) => {})
|
|
|
661
676
|
chart.on('draftCancelled', ({ label, isFullscreen }) => {}); // draft order dismissed without confirming
|
|
662
677
|
chart.on('tradeLevelDragEnd', ({ label, type, newPrice, data }) => {}); // deprecated — use tradeLevelEdit
|
|
663
678
|
chart.on('tradeLevelBracketDrag', ({ label, bracketType, newPrice, data }) => {}); // deprecated
|
|
679
|
+
|
|
680
|
+
// Fires after addLevelBracket() auto-places a bracket, delivering the computed price back to the caller
|
|
681
|
+
chart.on('tradeLevelBracketActivated', ({ label, bracketType, price, isFullscreen }) => {
|
|
682
|
+
// Use price to pre-populate the SL/TP input field in your native form
|
|
683
|
+
updateBracketPriceInput(bracketType, price);
|
|
684
|
+
});
|
|
664
685
|
```
|
|
665
686
|
|
|
666
687
|
---
|
|
@@ -518,6 +518,8 @@ interface ChartConfig {
|
|
|
518
518
|
showUI?: boolean;
|
|
519
519
|
/** If false, the drawing tools toolbar and pencil button are hidden entirely. Default: true */
|
|
520
520
|
showDrawingTools?: boolean;
|
|
521
|
+
/** If false, the settings gear button in the top bar is hidden entirely. Default: true */
|
|
522
|
+
showSettings?: boolean;
|
|
521
523
|
timeframe?: Timeframe;
|
|
522
524
|
duration?: Duration;
|
|
523
525
|
symbol?: string;
|
|
@@ -986,6 +988,13 @@ type ChartEventMap = {
|
|
|
986
988
|
openingBar: OHLCVBar;
|
|
987
989
|
intervalMs: number;
|
|
988
990
|
};
|
|
991
|
+
/** Emitted after addLevelBracket() auto-places a SL or TP bracket so consumers can populate their form's price field. */
|
|
992
|
+
tradeLevelBracketActivated: {
|
|
993
|
+
label: string;
|
|
994
|
+
bracketType: 'sl' | 'tp';
|
|
995
|
+
price: number;
|
|
996
|
+
isFullscreen: boolean;
|
|
997
|
+
};
|
|
989
998
|
/** Emitted when the user confirms all edits to a level — replaces separate tradeLevelDragEnd / tradeLevelBracketDrag events. */
|
|
990
999
|
tradeLevelEdit: {
|
|
991
1000
|
label: string;
|
|
@@ -518,6 +518,8 @@ interface ChartConfig {
|
|
|
518
518
|
showUI?: boolean;
|
|
519
519
|
/** If false, the drawing tools toolbar and pencil button are hidden entirely. Default: true */
|
|
520
520
|
showDrawingTools?: boolean;
|
|
521
|
+
/** If false, the settings gear button in the top bar is hidden entirely. Default: true */
|
|
522
|
+
showSettings?: boolean;
|
|
521
523
|
timeframe?: Timeframe;
|
|
522
524
|
duration?: Duration;
|
|
523
525
|
symbol?: string;
|
|
@@ -986,6 +988,13 @@ type ChartEventMap = {
|
|
|
986
988
|
openingBar: OHLCVBar;
|
|
987
989
|
intervalMs: number;
|
|
988
990
|
};
|
|
991
|
+
/** Emitted after addLevelBracket() auto-places a SL or TP bracket so consumers can populate their form's price field. */
|
|
992
|
+
tradeLevelBracketActivated: {
|
|
993
|
+
label: string;
|
|
994
|
+
bracketType: 'sl' | 'tp';
|
|
995
|
+
price: number;
|
|
996
|
+
isFullscreen: boolean;
|
|
997
|
+
};
|
|
989
998
|
/** Emitted when the user confirms all edits to a level — replaces separate tradeLevelDragEnd / tradeLevelBracketDrag events. */
|
|
990
999
|
tradeLevelEdit: {
|
|
991
1000
|
label: string;
|
package/dist/index.cjs
CHANGED
|
@@ -1607,7 +1607,7 @@ function isDraggable(level) {
|
|
|
1607
1607
|
return !!level.entryPriceEditable;
|
|
1608
1608
|
return false;
|
|
1609
1609
|
}
|
|
1610
|
-
function renderTradeLevels(ctx, levels, scale, priceRange, theme, chartW, priceH, precision, hoveredLabel = null, draggingLabel = null, dragNewPrice = null, selectedLabel = null, pendingLabels = /* @__PURE__ */ new Set(), positionStyle = "line", hideConfirmCancel = false) {
|
|
1610
|
+
function renderTradeLevels(ctx, levels, scale, priceRange, theme, chartW, priceH, precision, hoveredLabel = null, draggingLabel = null, dragNewPrice = null, selectedLabel = null, pendingLabels = /* @__PURE__ */ new Set(), positionStyle = "line", hideConfirmCancel = false, draftOrderLabel = null, draftBracketPnl = {}) {
|
|
1611
1611
|
if (levels.length === 0)
|
|
1612
1612
|
return {
|
|
1613
1613
|
hitAreas: [],
|
|
@@ -1704,7 +1704,8 @@ function renderTradeLevels(ctx, levels, scale, priceRange, theme, chartW, priceH
|
|
|
1704
1704
|
dragAreas,
|
|
1705
1705
|
confirmAreas,
|
|
1706
1706
|
addBracketAreas,
|
|
1707
|
-
hideConfirmCancel
|
|
1707
|
+
hideConfirmCancel,
|
|
1708
|
+
level.label === draftOrderLabel ? draftBracketPnl : void 0
|
|
1708
1709
|
);
|
|
1709
1710
|
}
|
|
1710
1711
|
ctx.restore();
|
|
@@ -1768,7 +1769,7 @@ function drawDotHoverBox(ctx, level, y, dotX, dotR, theme, precision) {
|
|
|
1768
1769
|
});
|
|
1769
1770
|
ctx.restore();
|
|
1770
1771
|
}
|
|
1771
|
-
function drawLevel(ctx, level, layout, chartW, colors, theme, precision, hovered, selected, scale, priceRange, priceH, draggingLabel, dragNewPrice, bracketLineXMap, hasPendingChanges, positionStyle, hitAreas, editAreas, hoverAreas, dragAreas, confirmAreas, addBracketAreas, hideConfirmCancel = false) {
|
|
1772
|
+
function drawLevel(ctx, level, layout, chartW, colors, theme, precision, hovered, selected, scale, priceRange, priceH, draggingLabel, dragNewPrice, bracketLineXMap, hasPendingChanges, positionStyle, hitAreas, editAreas, hoverAreas, dragAreas, confirmAreas, addBracketAreas, hideConfirmCancel = false, draftBracketPnl) {
|
|
1772
1773
|
const { y, boxY, boxW, boxH } = layout;
|
|
1773
1774
|
const color = levelColor(level, colors, theme);
|
|
1774
1775
|
const active = hovered || selected;
|
|
@@ -1830,7 +1831,8 @@ function drawLevel(ctx, level, layout, chartW, colors, theme, precision, hovered
|
|
|
1830
1831
|
dragNewPrice,
|
|
1831
1832
|
hitAreas,
|
|
1832
1833
|
dragAreas,
|
|
1833
|
-
bracketHost._slOrderLabel
|
|
1834
|
+
bracketHost._slOrderLabel,
|
|
1835
|
+
draftBracketPnl?.sl
|
|
1834
1836
|
);
|
|
1835
1837
|
} else if (draggingLabel === slDragKey && dragNewPrice !== null) {
|
|
1836
1838
|
const bLineX = chartW * BRACKET_BASE_RATIO;
|
|
@@ -1850,7 +1852,8 @@ function drawLevel(ctx, level, layout, chartW, colors, theme, precision, hovered
|
|
|
1850
1852
|
dragNewPrice,
|
|
1851
1853
|
hitAreas,
|
|
1852
1854
|
dragAreas,
|
|
1853
|
-
void 0
|
|
1855
|
+
void 0,
|
|
1856
|
+
draftBracketPnl?.sl
|
|
1854
1857
|
);
|
|
1855
1858
|
}
|
|
1856
1859
|
if (bracketHost.takeProfitPrice !== void 0) {
|
|
@@ -1871,7 +1874,8 @@ function drawLevel(ctx, level, layout, chartW, colors, theme, precision, hovered
|
|
|
1871
1874
|
dragNewPrice,
|
|
1872
1875
|
hitAreas,
|
|
1873
1876
|
dragAreas,
|
|
1874
|
-
bracketHost._tpOrderLabel
|
|
1877
|
+
bracketHost._tpOrderLabel,
|
|
1878
|
+
draftBracketPnl?.tp
|
|
1875
1879
|
);
|
|
1876
1880
|
} else if (draggingLabel === tpDragKey && dragNewPrice !== null) {
|
|
1877
1881
|
const bLineX = chartW * BRACKET_BASE_RATIO;
|
|
@@ -1891,7 +1895,8 @@ function drawLevel(ctx, level, layout, chartW, colors, theme, precision, hovered
|
|
|
1891
1895
|
dragNewPrice,
|
|
1892
1896
|
hitAreas,
|
|
1893
1897
|
dragAreas,
|
|
1894
|
-
void 0
|
|
1898
|
+
void 0,
|
|
1899
|
+
draftBracketPnl?.tp
|
|
1895
1900
|
);
|
|
1896
1901
|
}
|
|
1897
1902
|
if (selected && bracketHost.side !== void 0) {
|
|
@@ -2162,7 +2167,7 @@ function drawAddBracketButton(ctx, level, bracketType, entryY, bracketLineX, cha
|
|
|
2162
2167
|
h: bH
|
|
2163
2168
|
});
|
|
2164
2169
|
}
|
|
2165
|
-
function drawBracketLine(ctx, level, bracketType, bracketPrice, scale, priceRange, priceH, bracketLineX, chartW, theme, precision, draggingLabel, dragNewPrice, hitAreas, dragAreas, bracketOrderLabel) {
|
|
2170
|
+
function drawBracketLine(ctx, level, bracketType, bracketPrice, scale, priceRange, priceH, bracketLineX, chartW, theme, precision, draggingLabel, dragNewPrice, hitAreas, dragAreas, bracketOrderLabel, pnlText) {
|
|
2166
2171
|
const isDraggingThis = draggingLabel === `${level.label}:${bracketType}`;
|
|
2167
2172
|
const price = isDraggingThis && dragNewPrice !== null ? dragNewPrice : bracketPrice;
|
|
2168
2173
|
const y = scale.yToPixel(price, priceRange, priceH);
|
|
@@ -2181,7 +2186,8 @@ function drawBracketLine(ctx, level, bracketType, bracketPrice, scale, priceRang
|
|
|
2181
2186
|
const boxLabel = `${typeLabel} ${priceStr}`;
|
|
2182
2187
|
ctx.font = FONT_BRACKET;
|
|
2183
2188
|
const textW = ctx.measureText(boxLabel).width;
|
|
2184
|
-
const
|
|
2189
|
+
const pnlW = pnlText ? ctx.measureText(pnlText).width + BRACKET_BOX_PAD_X : 0;
|
|
2190
|
+
const bW = BRACKET_BOX_PAD_X + textW + pnlW + BRACKET_BOX_PAD_X + CLOSE_R * 2 + BRACKET_BOX_PAD_X / 2;
|
|
2185
2191
|
const bH = BRACKET_BOX_H;
|
|
2186
2192
|
const bX = bracketLineX - bW - 2;
|
|
2187
2193
|
const bY = y - bH / 2;
|
|
@@ -2195,6 +2201,11 @@ function drawBracketLine(ctx, level, bracketType, bracketPrice, scale, priceRang
|
|
|
2195
2201
|
ctx.textAlign = "left";
|
|
2196
2202
|
ctx.textBaseline = "middle";
|
|
2197
2203
|
ctx.fillText(boxLabel, bX + BRACKET_BOX_PAD_X, y);
|
|
2204
|
+
if (pnlText) {
|
|
2205
|
+
const isLoss = pnlText.trim().startsWith("-");
|
|
2206
|
+
ctx.fillStyle = isLoss ? theme.candle.down : theme.candle.up;
|
|
2207
|
+
ctx.fillText(pnlText, bX + BRACKET_BOX_PAD_X + textW + BRACKET_BOX_PAD_X, y);
|
|
2208
|
+
}
|
|
2198
2209
|
const btnX = bX + bW - BRACKET_BOX_PAD_X / 2 - CLOSE_R;
|
|
2199
2210
|
const btnY = y;
|
|
2200
2211
|
ctx.fillStyle = monoColor(theme);
|
|
@@ -3073,7 +3084,7 @@ function injectFlyoutScrollbarStyles() {
|
|
|
3073
3084
|
document.head.appendChild(style);
|
|
3074
3085
|
}
|
|
3075
3086
|
var TopBar = class {
|
|
3076
|
-
constructor(onSeriesChange, onTimeframeChange, onAddIndicator, onDurationChange, onSettingsClick, onFullscreenToggle, onDrawingToolsToggle, showDrawingToolsBtn, initialSeries, initialTf, initialDuration, durationMap, theme, cfg = DEFAULT_TOP_BAR_CONFIG, labels = DEFAULT_LABELS.topBar) {
|
|
3087
|
+
constructor(onSeriesChange, onTimeframeChange, onAddIndicator, onDurationChange, onSettingsClick, onFullscreenToggle, onDrawingToolsToggle, showDrawingToolsBtn, showSettingsBtn, initialSeries, initialTf, initialDuration, durationMap, theme, cfg = DEFAULT_TOP_BAR_CONFIG, labels = DEFAULT_LABELS.topBar) {
|
|
3077
3088
|
this.onSeriesChange = onSeriesChange;
|
|
3078
3089
|
this.onTimeframeChange = onTimeframeChange;
|
|
3079
3090
|
this.onAddIndicator = onAddIndicator;
|
|
@@ -3081,6 +3092,7 @@ var TopBar = class {
|
|
|
3081
3092
|
this.onSettingsClick = onSettingsClick;
|
|
3082
3093
|
this.onFullscreenToggle = onFullscreenToggle;
|
|
3083
3094
|
this.onDrawingToolsToggle = onDrawingToolsToggle;
|
|
3095
|
+
this.settingsBtn = null;
|
|
3084
3096
|
this.isFullscreen = false;
|
|
3085
3097
|
this.durationBtns = /* @__PURE__ */ new Map();
|
|
3086
3098
|
this.isMobileLayout = false;
|
|
@@ -3156,16 +3168,18 @@ var TopBar = class {
|
|
|
3156
3168
|
});
|
|
3157
3169
|
this.addBtnHover(this.fullscreenBtn);
|
|
3158
3170
|
this.rightGroup.appendChild(this.fullscreenBtn);
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3171
|
+
if (showSettingsBtn) {
|
|
3172
|
+
this.settingsBtn = document.createElement("button");
|
|
3173
|
+
this.settingsBtn.title = this.labels.settingsTitle;
|
|
3174
|
+
this.settingsBtn.innerHTML = ICON_SETTINGS;
|
|
3175
|
+
this.applyIconBtnStyles(this.settingsBtn, theme, false);
|
|
3176
|
+
this.settingsBtn.addEventListener("click", (e) => {
|
|
3177
|
+
e.stopPropagation();
|
|
3178
|
+
this.onSettingsClick();
|
|
3179
|
+
});
|
|
3180
|
+
this.addBtnHover(this.settingsBtn);
|
|
3181
|
+
this.rightGroup.appendChild(this.settingsBtn);
|
|
3182
|
+
}
|
|
3169
3183
|
this.el.append(this.leftGroup, this.rightGroup);
|
|
3170
3184
|
this.docClickHandler = (e) => {
|
|
3171
3185
|
if (!this.el.contains(e.target) && !this.flyout.contains(e.target)) {
|
|
@@ -3199,7 +3213,7 @@ var TopBar = class {
|
|
|
3199
3213
|
this.applyFlyoutStyles();
|
|
3200
3214
|
if (this.drawingBtn) this.applyIconBtnStyles(this.drawingBtn, theme, false);
|
|
3201
3215
|
this.applyIconBtnStyles(this.fullscreenBtn, theme, this.isFullscreen);
|
|
3202
|
-
this.applyIconBtnStyles(this.settingsBtn, theme, false);
|
|
3216
|
+
if (this.settingsBtn) this.applyIconBtnStyles(this.settingsBtn, theme, false);
|
|
3203
3217
|
this.applySeriesBtnStyles(this.seriesBtn, theme, false);
|
|
3204
3218
|
this.applyDropBtnStyles(this.tfBtn, theme, false);
|
|
3205
3219
|
this.applyDropBtnStyles(this.indBtn, theme, false);
|
|
@@ -11519,7 +11533,7 @@ var TAB_LABELS = {
|
|
|
11519
11533
|
trading: "Trading"
|
|
11520
11534
|
};
|
|
11521
11535
|
var ChartSettingsDialog = class {
|
|
11522
|
-
constructor(theme, initial, labels = DEFAULT_LABELS.dialogs) {
|
|
11536
|
+
constructor(theme, initial, labels = DEFAULT_LABELS.dialogs, showTradingTab = true) {
|
|
11523
11537
|
this.activeTab = "appearance";
|
|
11524
11538
|
this.onChange = null;
|
|
11525
11539
|
this.onReset = null;
|
|
@@ -11527,6 +11541,8 @@ var ChartSettingsDialog = class {
|
|
|
11527
11541
|
this.theme = theme;
|
|
11528
11542
|
this.labels = labels;
|
|
11529
11543
|
this.current = { ...initial };
|
|
11544
|
+
this.showTradingTab = showTradingTab;
|
|
11545
|
+
if (!showTradingTab && this.activeTab === "trading") this.activeTab = "appearance";
|
|
11530
11546
|
this.el = document.createElement("div");
|
|
11531
11547
|
Object.assign(this.el.style, {
|
|
11532
11548
|
position: "fixed",
|
|
@@ -11718,7 +11734,7 @@ var ChartSettingsDialog = class {
|
|
|
11718
11734
|
return sidebar;
|
|
11719
11735
|
}
|
|
11720
11736
|
buildSidebarTabs(container) {
|
|
11721
|
-
const tabs = ["appearance", "trading"];
|
|
11737
|
+
const tabs = this.showTradingTab ? ["appearance", "trading"] : ["appearance"];
|
|
11722
11738
|
for (const tab of tabs) {
|
|
11723
11739
|
const btn = document.createElement("button");
|
|
11724
11740
|
Object.assign(btn.style, {
|
|
@@ -12259,6 +12275,7 @@ var TradePopover = class {
|
|
|
12259
12275
|
this._pricePrecision = 2;
|
|
12260
12276
|
this._buyOrderType = "limit";
|
|
12261
12277
|
this._sellOrderType = "limit";
|
|
12278
|
+
this._isMarket = false;
|
|
12262
12279
|
this.onDocMouseDown = (e) => {
|
|
12263
12280
|
if (this.isVisible && !this.el.contains(e.target)) {
|
|
12264
12281
|
this.hide();
|
|
@@ -12288,11 +12305,12 @@ var TradePopover = class {
|
|
|
12288
12305
|
document.addEventListener("mousedown", this.onDocMouseDown);
|
|
12289
12306
|
}
|
|
12290
12307
|
// ── Public ────────────────────────────────────────────────────────────────
|
|
12291
|
-
show(price, buttonY, buttonRight, pricePrecision = 2, buyOrderType = "limit", sellOrderType = "limit") {
|
|
12308
|
+
show(price, buttonY, buttonRight, pricePrecision = 2, buyOrderType = "limit", sellOrderType = "limit", isMarket = false) {
|
|
12292
12309
|
this._price = price;
|
|
12293
12310
|
this._pricePrecision = pricePrecision;
|
|
12294
12311
|
this._buyOrderType = buyOrderType;
|
|
12295
12312
|
this._sellOrderType = sellOrderType;
|
|
12313
|
+
this._isMarket = isMarket;
|
|
12296
12314
|
this.rebuild();
|
|
12297
12315
|
this.el.style.display = "flex";
|
|
12298
12316
|
const popW = 145;
|
|
@@ -12322,16 +12340,22 @@ var TradePopover = class {
|
|
|
12322
12340
|
// ── Private ───────────────────────────────────────────────────────────────
|
|
12323
12341
|
rebuild() {
|
|
12324
12342
|
this.el.innerHTML = "";
|
|
12325
|
-
this.
|
|
12326
|
-
this.
|
|
12327
|
-
|
|
12328
|
-
|
|
12343
|
+
if (this._isMarket) {
|
|
12344
|
+
this.el.append(
|
|
12345
|
+
this.makeRow(`${this.labels.buy} Market`, "#26a69a", "buy", "market"),
|
|
12346
|
+
this.makeRow(`${this.labels.sell} Market`, "#ef5350", "sell", "market")
|
|
12347
|
+
);
|
|
12348
|
+
} else {
|
|
12349
|
+
this.el.append(
|
|
12350
|
+
this.makeRow(this.labels.buy, "#26a69a", "buy", this._buyOrderType),
|
|
12351
|
+
this.makeRow(this.labels.sell, "#ef5350", "sell", this._sellOrderType)
|
|
12352
|
+
);
|
|
12353
|
+
}
|
|
12329
12354
|
}
|
|
12330
12355
|
makeRow(label, color, side, orderType) {
|
|
12331
12356
|
const btn = document.createElement("button");
|
|
12332
12357
|
const priceStr = this._price.toFixed(this._pricePrecision);
|
|
12333
|
-
|
|
12334
|
-
btn.textContent = `${label} ${typeLabel} ${priceStr}`;
|
|
12358
|
+
btn.textContent = orderType === "market" ? `${label} ${priceStr}` : `${label} ${orderType === "limit" ? this.labels.limit : this.labels.stop} ${priceStr}`;
|
|
12335
12359
|
Object.assign(btn.style, {
|
|
12336
12360
|
display: "block",
|
|
12337
12361
|
width: "100%",
|
|
@@ -16377,6 +16401,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
16377
16401
|
this.leftBar = null;
|
|
16378
16402
|
this.leftBarVisible = true;
|
|
16379
16403
|
this.drawingToolsEnabled = true;
|
|
16404
|
+
this.settingsEnabled = true;
|
|
16380
16405
|
this._mobileLayout = false;
|
|
16381
16406
|
this.indicatorOverlay = null;
|
|
16382
16407
|
this.subPaneOverlays = /* @__PURE__ */ new Map();
|
|
@@ -16400,6 +16425,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
16400
16425
|
this.draftOrderQtyInput = null;
|
|
16401
16426
|
this._hoverBuyType = "limit";
|
|
16402
16427
|
this._hoverSellType = "limit";
|
|
16428
|
+
this._hoverInSpread = false;
|
|
16403
16429
|
this._onOrderSubmit = void 0;
|
|
16404
16430
|
this._minLots = 1;
|
|
16405
16431
|
this.scrollToEndBtn = null;
|
|
@@ -16496,6 +16522,8 @@ var _ChartEngine = class _ChartEngine {
|
|
|
16496
16522
|
this.tradeAddBracketAreas = [];
|
|
16497
16523
|
this.hoveredTradeLabel = null;
|
|
16498
16524
|
this.selectedTradeLabel = null;
|
|
16525
|
+
/** Estimated PNL text for draft order SL/TP bracket lines, set by setDraftBracketPnl(). */
|
|
16526
|
+
this.draftBracketPnl = {};
|
|
16499
16527
|
// TFC components
|
|
16500
16528
|
this.tradeDragHandler = null;
|
|
16501
16529
|
this.tradeDragNewPrice = null;
|
|
@@ -16560,6 +16588,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
16560
16588
|
const lastClose = allBars.length > 0 ? allBars[allBars.length - 1].close : price;
|
|
16561
16589
|
this._hoverBuyType = price < lastClose ? "limit" : "stop";
|
|
16562
16590
|
this._hoverSellType = price > lastClose ? "limit" : "stop";
|
|
16591
|
+
this._hoverInSpread = !!(this.liveBidAsk && price >= this.liveBidAsk.bid && price <= this.liveBidAsk.ask);
|
|
16563
16592
|
} else if (!this.tradePopover?.isVisible) {
|
|
16564
16593
|
this.tradeButton.hide();
|
|
16565
16594
|
}
|
|
@@ -17172,9 +17201,28 @@ var _ChartEngine = class _ChartEngine {
|
|
|
17172
17201
|
this.selectedTradeLabel = hoverHit.label;
|
|
17173
17202
|
}
|
|
17174
17203
|
this.renderTradeLayer();
|
|
17204
|
+
if (this.hideLevelConfirmCancel) {
|
|
17205
|
+
const level = this.levels.find((l) => l.label === hoverHit.label);
|
|
17206
|
+
if (level) {
|
|
17207
|
+
this.emitter.emit("tradeLevelEditOpen", {
|
|
17208
|
+
label: level.label,
|
|
17209
|
+
type: level.type,
|
|
17210
|
+
data: level.data,
|
|
17211
|
+
price: level.price,
|
|
17212
|
+
side: level.side,
|
|
17213
|
+
stopLossPrice: level.stopLossPrice,
|
|
17214
|
+
takeProfitPrice: level.takeProfitPrice,
|
|
17215
|
+
isFullscreen: this.isFullscreen
|
|
17216
|
+
});
|
|
17217
|
+
}
|
|
17218
|
+
}
|
|
17175
17219
|
return;
|
|
17176
17220
|
} else if (this.selectedTradeLabel !== null) {
|
|
17177
|
-
if (
|
|
17221
|
+
if (this.hideLevelConfirmCancel) {
|
|
17222
|
+
this.revertPendingChanges(this.selectedTradeLabel);
|
|
17223
|
+
this.selectedTradeLabel = null;
|
|
17224
|
+
this.renderTradeLayer();
|
|
17225
|
+
} else if (!this.pendingLevelDrags.has(this.selectedTradeLabel)) {
|
|
17178
17226
|
this.selectedTradeLabel = null;
|
|
17179
17227
|
this.renderTradeLayer();
|
|
17180
17228
|
}
|
|
@@ -17384,6 +17432,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
17384
17432
|
} = config;
|
|
17385
17433
|
this.dataLoader = dataLoader ?? null;
|
|
17386
17434
|
this.drawingToolsEnabled = config.showDrawingTools !== false;
|
|
17435
|
+
this.settingsEnabled = config.showSettings !== false;
|
|
17387
17436
|
this.uiConfig = resolveUiConfig(uiConfig);
|
|
17388
17437
|
this.labels = resolveLabels(labels);
|
|
17389
17438
|
this.tickActivityMs = tickActivityMs;
|
|
@@ -17476,6 +17525,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
17476
17525
|
},
|
|
17477
17526
|
this.drawingToolsEnabled ? () => this.toggleDrawingTools() : null,
|
|
17478
17527
|
this.drawingToolsEnabled,
|
|
17528
|
+
this.settingsEnabled,
|
|
17479
17529
|
this.series,
|
|
17480
17530
|
this.timeframe,
|
|
17481
17531
|
null,
|
|
@@ -17734,7 +17784,10 @@ var _ChartEngine = class _ChartEngine {
|
|
|
17734
17784
|
this.tradePopover = new TradePopover(
|
|
17735
17785
|
this.theme,
|
|
17736
17786
|
lots,
|
|
17737
|
-
(side, price, orderType) =>
|
|
17787
|
+
(side, price, orderType) => {
|
|
17788
|
+
if (orderType === "market") this.showMarketDraft(price, side);
|
|
17789
|
+
else this.createDraftOrder(side, price, orderType);
|
|
17790
|
+
},
|
|
17738
17791
|
this.labels.trade
|
|
17739
17792
|
);
|
|
17740
17793
|
this.canvasWrap.appendChild(this.tradePopover.el);
|
|
@@ -17756,7 +17809,8 @@ var _ChartEngine = class _ChartEngine {
|
|
|
17756
17809
|
btnRight,
|
|
17757
17810
|
this.dataStore.getPricePrecision(),
|
|
17758
17811
|
this._hoverBuyType,
|
|
17759
|
-
this._hoverSellType
|
|
17812
|
+
this._hoverSellType,
|
|
17813
|
+
this._hoverInSpread
|
|
17760
17814
|
);
|
|
17761
17815
|
}
|
|
17762
17816
|
},
|
|
@@ -18776,8 +18830,10 @@ var _ChartEngine = class _ChartEngine {
|
|
|
18776
18830
|
return this;
|
|
18777
18831
|
}
|
|
18778
18832
|
/**
|
|
18779
|
-
* Remove the current draft order preview line
|
|
18833
|
+
* Remove the current draft order preview line and emit `draftCancelled`.
|
|
18780
18834
|
* Call when the order panel closes or the user switches to market order type.
|
|
18835
|
+
* To cancel whatever is currently being edited or drafted (draft or existing level),
|
|
18836
|
+
* use `cancelCurrentEdit()` instead.
|
|
18781
18837
|
*/
|
|
18782
18838
|
clearDraftOrder() {
|
|
18783
18839
|
this.removeDraftOrder();
|
|
@@ -18806,6 +18862,34 @@ var _ChartEngine = class _ChartEngine {
|
|
|
18806
18862
|
this.renderTradeLayer();
|
|
18807
18863
|
return this;
|
|
18808
18864
|
}
|
|
18865
|
+
/**
|
|
18866
|
+
* Add a SL or TP bracket to an existing level at an auto-computed default price.
|
|
18867
|
+
* Use this when an external panel enables a bracket without knowing the price.
|
|
18868
|
+
* The chart places the bracket at the same offset used by the canvas +SL/+TP button
|
|
18869
|
+
* and emits `tradeLevelBracketActivated` with the chosen price so the panel can populate its input.
|
|
18870
|
+
* No-op if the level doesn't exist or already has the requested bracket.
|
|
18871
|
+
*/
|
|
18872
|
+
addLevelBracket(label, type) {
|
|
18873
|
+
const level = this.levels.find((l) => l.label === label);
|
|
18874
|
+
if (!level) return this;
|
|
18875
|
+
const alreadyHas = type === "sl" ? level.stopLossPrice !== void 0 : level.takeProfitPrice !== void 0;
|
|
18876
|
+
if (alreadyHas) return this;
|
|
18877
|
+
const { priceH } = this.getChartLayout();
|
|
18878
|
+
const priceRange = this._displayedPriceRange ?? this.getScaledPriceRange(this.viewport);
|
|
18879
|
+
const entryY = this.scaleManager.yToPixel(level.price, priceRange, priceH);
|
|
18880
|
+
const side = level.side ?? "buy";
|
|
18881
|
+
const isBelowEntry = side === "buy" && type === "sl" || side === "sell" && type === "tp";
|
|
18882
|
+
const bracketY = entryY + (isBelowEntry ? 30 : -30);
|
|
18883
|
+
const defaultPrice = this.scaleManager.pixelToPrice(bracketY, priceRange, priceH);
|
|
18884
|
+
this.updateLevelBracket(label, type, defaultPrice);
|
|
18885
|
+
this.emitter.emit("tradeLevelBracketActivated", {
|
|
18886
|
+
label,
|
|
18887
|
+
bracketType: type,
|
|
18888
|
+
price: defaultPrice,
|
|
18889
|
+
isFullscreen: this.isFullscreen
|
|
18890
|
+
});
|
|
18891
|
+
return this;
|
|
18892
|
+
}
|
|
18809
18893
|
/**
|
|
18810
18894
|
* Move an existing level's entry price line to a new price and re-render.
|
|
18811
18895
|
* Used when an external panel updates the entry price for an open position or pending order.
|
|
@@ -18819,6 +18903,21 @@ var _ChartEngine = class _ChartEngine {
|
|
|
18819
18903
|
}
|
|
18820
18904
|
return this;
|
|
18821
18905
|
}
|
|
18906
|
+
/**
|
|
18907
|
+
* Set or clear the estimated PNL text shown on a draft order's SL or TP bracket line.
|
|
18908
|
+
* Pass `null` to clear the text. No-op if no draft order is active.
|
|
18909
|
+
* Use this to display the consumer-calculated estimated profit/loss next to the bracket.
|
|
18910
|
+
*/
|
|
18911
|
+
setDraftBracketPnl(type, pnlText) {
|
|
18912
|
+
if (!this.draftOrderLabel) return this;
|
|
18913
|
+
if (type === "sl") {
|
|
18914
|
+
this.draftBracketPnl = { ...this.draftBracketPnl, sl: pnlText ?? void 0 };
|
|
18915
|
+
} else {
|
|
18916
|
+
this.draftBracketPnl = { ...this.draftBracketPnl, tp: pnlText ?? void 0 };
|
|
18917
|
+
}
|
|
18918
|
+
this.renderTradeLayer();
|
|
18919
|
+
return this;
|
|
18920
|
+
}
|
|
18822
18921
|
/**
|
|
18823
18922
|
* Update the stop-loss or take-profit price on the current draft order.
|
|
18824
18923
|
* Pass `null` to remove the bracket. No-op if no draft order is active.
|
|
@@ -18871,6 +18970,22 @@ var _ChartEngine = class _ChartEngine {
|
|
|
18871
18970
|
this.renderTradeLayer();
|
|
18872
18971
|
return this;
|
|
18873
18972
|
}
|
|
18973
|
+
/**
|
|
18974
|
+
* Cancel whatever is currently being edited or drafted on the chart.
|
|
18975
|
+
* Handles all three cases without the caller needing to track the active label:
|
|
18976
|
+
* - active limit/stop draft (`draftOrderLabel === selectedTradeLabel`) — emits `draftCancelled`
|
|
18977
|
+
* - active market draft (`draftOrderLabel` set, no `selectedTradeLabel`) — emits `draftCancelled`
|
|
18978
|
+
* - in-progress level edit (`selectedTradeLabel` set with pending changes) — reverts staged changes
|
|
18979
|
+
* No-op when nothing is being edited.
|
|
18980
|
+
*/
|
|
18981
|
+
cancelCurrentEdit() {
|
|
18982
|
+
if (this.draftOrderLabel) {
|
|
18983
|
+
this.clearDraftOrder();
|
|
18984
|
+
} else if (this.selectedTradeLabel) {
|
|
18985
|
+
this.revertPendingChanges(this.selectedTradeLabel);
|
|
18986
|
+
}
|
|
18987
|
+
return this;
|
|
18988
|
+
}
|
|
18874
18989
|
/**
|
|
18875
18990
|
* Programmatically select (activate) a trade level by its label.
|
|
18876
18991
|
* Pass `null` to deselect the currently active level.
|
|
@@ -19061,7 +19176,8 @@ var _ChartEngine = class _ChartEngine {
|
|
|
19061
19176
|
positionRenderStyle: this.positionRenderStyle,
|
|
19062
19177
|
theme: this.themeName
|
|
19063
19178
|
},
|
|
19064
|
-
this.labels.dialogs
|
|
19179
|
+
this.labels.dialogs,
|
|
19180
|
+
!!this._onOrderSubmit
|
|
19065
19181
|
);
|
|
19066
19182
|
this.getFloatingRoot().appendChild(this.chartSettingsDialog.el);
|
|
19067
19183
|
}
|
|
@@ -19251,7 +19367,9 @@ var _ChartEngine = class _ChartEngine {
|
|
|
19251
19367
|
this.selectedTradeLabel,
|
|
19252
19368
|
new Set(this.pendingLevelDrags.keys()),
|
|
19253
19369
|
this.positionRenderStyle,
|
|
19254
|
-
this.hideLevelConfirmCancel
|
|
19370
|
+
this.hideLevelConfirmCancel,
|
|
19371
|
+
this.draftOrderLabel,
|
|
19372
|
+
this.draftBracketPnl
|
|
19255
19373
|
);
|
|
19256
19374
|
ctx.restore();
|
|
19257
19375
|
ctx.restore();
|
|
@@ -19330,6 +19448,10 @@ var _ChartEngine = class _ChartEngine {
|
|
|
19330
19448
|
filtered.push({ field, newPrice: result.newPrice, oldPrice, isNew, bracketOrderLabel });
|
|
19331
19449
|
this.pendingLevelDrags.set(result.label, filtered);
|
|
19332
19450
|
this.tradeDragNewPrice = null;
|
|
19451
|
+
if (this.hideLevelConfirmCancel) {
|
|
19452
|
+
this.applyPendingChanges(result.label);
|
|
19453
|
+
return;
|
|
19454
|
+
}
|
|
19333
19455
|
this.renderTradeLayer();
|
|
19334
19456
|
}
|
|
19335
19457
|
createDraftOrder(side, price, orderType) {
|
|
@@ -19365,6 +19487,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
19365
19487
|
this.pendingLevelDrags.delete(label);
|
|
19366
19488
|
this.selectedTradeLabel = null;
|
|
19367
19489
|
this.draftOrderLabel = null;
|
|
19490
|
+
this.draftBracketPnl = {};
|
|
19368
19491
|
this.draftOrderQtyInput?.hide();
|
|
19369
19492
|
this.renderTradeLayer();
|
|
19370
19493
|
this.emitter.emit("draftCancelled", { label, isFullscreen: this.isFullscreen });
|