@forgecharts/sdk 1.3.2 → 1.3.3
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/dist/api/TChart.d.ts +11 -2
- package/dist/api/TChart.d.ts.map +1 -1
- package/dist/index.js +187 -39
- package/dist/index.js.map +1 -1
- package/dist/internal.js +187 -39
- package/dist/internal.js.map +1 -1
- package/dist/licensing/ChartRuntimeResolver.d.ts +22 -0
- package/dist/licensing/ChartRuntimeResolver.d.ts.map +1 -1
- package/dist/licensing/__tests__/chartFeatureParity.test.d.ts +2 -0
- package/dist/licensing/__tests__/chartFeatureParity.test.d.ts.map +1 -0
- package/dist/licensing/licenseTypes.d.ts +9 -1
- package/dist/licensing/licenseTypes.d.ts.map +1 -1
- package/dist/react/canvas/ChartCanvas.d.ts.map +1 -1
- package/dist/react/canvas/toolbars/LeftToolbar.d.ts +3 -1
- package/dist/react/canvas/toolbars/LeftToolbar.d.ts.map +1 -1
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +1437 -117
- package/dist/react/index.js.map +1 -1
- package/dist/react/internal.js +6615 -6304
- package/dist/react/internal.js.map +1 -1
- package/dist/react/shell/OrderEntryPanel.d.ts +5 -1
- package/dist/react/shell/OrderEntryPanel.d.ts.map +1 -1
- package/dist/react/trading/OrderTicket.d.ts +38 -0
- package/dist/react/trading/OrderTicket.d.ts.map +1 -0
- package/dist/react/workspace/BottomTradingPanel.d.ts +14 -3
- package/dist/react/workspace/BottomTradingPanel.d.ts.map +1 -1
- package/dist/react/workspace/ChartWorkspace.d.ts +24 -1
- package/dist/react/workspace/ChartWorkspace.d.ts.map +1 -1
- package/dist/react/workspace/IndicatorsDialog.d.ts.map +1 -1
- package/dist/react/workspace/toolbars/BottomToolbar.d.ts +3 -1
- package/dist/react/workspace/toolbars/BottomToolbar.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/react/index.js
CHANGED
|
@@ -10613,7 +10613,9 @@ var TRADING_FEATURE_KEYS = /* @__PURE__ */ new Set([
|
|
|
10613
10613
|
"draggableOrders",
|
|
10614
10614
|
"bracketOrders",
|
|
10615
10615
|
"trailingStops",
|
|
10616
|
-
"positionsOverlay"
|
|
10616
|
+
"positionsOverlay",
|
|
10617
|
+
"depthOfMarket",
|
|
10618
|
+
"tradingPanel"
|
|
10617
10619
|
]);
|
|
10618
10620
|
|
|
10619
10621
|
// src/licensing/capabilityMatrix.ts
|
|
@@ -10965,6 +10967,14 @@ var ChartRuntimeResolver = class _ChartRuntimeResolver {
|
|
|
10965
10967
|
canUseDataExport() {
|
|
10966
10968
|
return this.#featureOrDefault("dataExport", true);
|
|
10967
10969
|
}
|
|
10970
|
+
/** Continuous futures roll rules + contract-switch handling. */
|
|
10971
|
+
canUseContinuousContracts() {
|
|
10972
|
+
return this.#featureOrDefault("continuousContracts", true);
|
|
10973
|
+
}
|
|
10974
|
+
/** Regular/Extended trading-hours session filtering. */
|
|
10975
|
+
canUseSessionHours() {
|
|
10976
|
+
return this.#featureOrDefault("sessionHours", true);
|
|
10977
|
+
}
|
|
10968
10978
|
// ── Trading capability queries (managed-only by default) ────────────────────
|
|
10969
10979
|
/**
|
|
10970
10980
|
* Built-in order entry UI hooks.
|
|
@@ -11041,6 +11051,23 @@ var ChartRuntimeResolver = class _ChartRuntimeResolver {
|
|
|
11041
11051
|
if (this.#isCoreTier()) return false;
|
|
11042
11052
|
return this.#featureOrDefault("positionsOverlay", true);
|
|
11043
11053
|
}
|
|
11054
|
+
/**
|
|
11055
|
+
* DOM price ladder. Managed deployments only — there is no unmanaged
|
|
11056
|
+
* depth-feed contract, so this is additionally vetoed off-managed.
|
|
11057
|
+
*/
|
|
11058
|
+
canUseDepthOfMarket() {
|
|
11059
|
+
if (!this.isManagedMode()) return false;
|
|
11060
|
+
if (this.#isCoreTier()) return false;
|
|
11061
|
+
return this.#featureOrDefault("depthOfMarket", true);
|
|
11062
|
+
}
|
|
11063
|
+
/**
|
|
11064
|
+
* Built-in positions/orders/history journal panel. Trading-tier feature,
|
|
11065
|
+
* gated on productTier (not deploymentMode).
|
|
11066
|
+
*/
|
|
11067
|
+
canUseTradingPanel() {
|
|
11068
|
+
if (this.#isCoreTier()) return false;
|
|
11069
|
+
return this.#featureOrDefault("tradingPanel", true);
|
|
11070
|
+
}
|
|
11044
11071
|
// ── Scripting capability queries ────────────────────────────────────────────
|
|
11045
11072
|
/** ForgeScript scripting engine. */
|
|
11046
11073
|
canUseForgeScript() {
|
|
@@ -11164,6 +11191,8 @@ var ChartRuntimeResolver = class _ChartRuntimeResolver {
|
|
|
11164
11191
|
realtimeData: this.canUseRealtimeData(),
|
|
11165
11192
|
multipleProviders: this.canUseMultipleProviders(),
|
|
11166
11193
|
dataExport: this.canUseDataExport(),
|
|
11194
|
+
continuousContracts: this.canUseContinuousContracts(),
|
|
11195
|
+
sessionHours: this.canUseSessionHours(),
|
|
11167
11196
|
// ── Trading features ────────────────────────────────────────────────
|
|
11168
11197
|
orderEntry: this.canUseOrderEntry(),
|
|
11169
11198
|
managedTrading: this.canUseManagedTrading(),
|
|
@@ -11171,6 +11200,8 @@ var ChartRuntimeResolver = class _ChartRuntimeResolver {
|
|
|
11171
11200
|
bracketOrders: this.canUseBracketOrders(),
|
|
11172
11201
|
trailingStops: this.canUseTrailingStops(),
|
|
11173
11202
|
positionsOverlay: this.canUsePositionsOverlay(),
|
|
11203
|
+
depthOfMarket: this.canUseDepthOfMarket(),
|
|
11204
|
+
tradingPanel: this.canUseTradingPanel(),
|
|
11174
11205
|
// ── Scripting features ──────────────────────────────────────────────
|
|
11175
11206
|
forgeScript: this.canUseForgeScript(),
|
|
11176
11207
|
customIndicators: this.canUseCustomIndicators(),
|
|
@@ -11211,6 +11242,13 @@ var ChartRuntimeResolver = class _ChartRuntimeResolver {
|
|
|
11211
11242
|
}
|
|
11212
11243
|
};
|
|
11213
11244
|
var resolver = () => ChartRuntimeResolver.getInstance();
|
|
11245
|
+
var canUseExtendedDrawings = () => resolver().canUseExtendedDrawings();
|
|
11246
|
+
var canUseMultiPane = () => resolver().canUseMultiPane();
|
|
11247
|
+
var canUseDraggableOrders = () => resolver().canUseDraggableOrders();
|
|
11248
|
+
var canUseBracketOrders = () => resolver().canUseBracketOrders();
|
|
11249
|
+
var canUsePositionsOverlay = () => resolver().canUsePositionsOverlay();
|
|
11250
|
+
var canUseForgeScript = () => resolver().canUseForgeScript();
|
|
11251
|
+
var canUseCustomIndicators = () => resolver().canUseCustomIndicators();
|
|
11214
11252
|
var canUseExternalIngestion = () => resolver().canUseExternalIngestion();
|
|
11215
11253
|
var getCapabilities = () => resolver().getCapabilities();
|
|
11216
11254
|
|
|
@@ -11867,7 +11905,16 @@ var TChart = class _TChart {
|
|
|
11867
11905
|
static OVERLAY_ONLY = /* @__PURE__ */ new Set(["bbands", "vwap", "sma", "ema", "wma"]);
|
|
11868
11906
|
addIndicator(config) {
|
|
11869
11907
|
this._assertAlive();
|
|
11870
|
-
|
|
11908
|
+
if (config.type === "script" && !canUseForgeScript()) {
|
|
11909
|
+
throw new Error(
|
|
11910
|
+
"[ForgeCharts] ForgeScript indicators are not enabled on the active license. Contact your license provider to enable the forgeScript feature."
|
|
11911
|
+
);
|
|
11912
|
+
}
|
|
11913
|
+
let finalConfig = _TChart.OVERLAY_ONLY.has(config.type) && config.overlay !== true ? { ...config, overlay: true } : config;
|
|
11914
|
+
if (finalConfig.overlay !== true && !canUseMultiPane()) {
|
|
11915
|
+
console.warn("[ForgeCharts] multiPane is not enabled on the active license \u2014 indicator added as a price-pane overlay instead.");
|
|
11916
|
+
finalConfig = { ...finalConfig, overlay: true };
|
|
11917
|
+
}
|
|
11871
11918
|
const id = this._indicators.add(finalConfig);
|
|
11872
11919
|
if (finalConfig.overlay !== true) {
|
|
11873
11920
|
const label = _indicatorTypeLabel(finalConfig.type);
|
|
@@ -11927,6 +11974,10 @@ var TChart = class _TChart {
|
|
|
11927
11974
|
*/
|
|
11928
11975
|
moveIndicatorToPane(id, targetPaneId) {
|
|
11929
11976
|
this._assertAlive();
|
|
11977
|
+
if (!canUseMultiPane()) {
|
|
11978
|
+
console.warn("[ForgeCharts] multiPane is not enabled on the active license \u2014 indicator stays on the price pane.");
|
|
11979
|
+
return;
|
|
11980
|
+
}
|
|
11930
11981
|
const inst = this._indicators.get(id);
|
|
11931
11982
|
if (!inst) return;
|
|
11932
11983
|
const oldPaneId = this._panes.getPanes().find((p) => p.indicatorIds.includes(id))?.id;
|
|
@@ -11998,12 +12049,49 @@ var TChart = class _TChart {
|
|
|
11998
12049
|
this._core.setDrawings(this._drawings.all());
|
|
11999
12050
|
this._core.markDirty();
|
|
12000
12051
|
}
|
|
12052
|
+
/**
|
|
12053
|
+
* Drawing tools in the "extended" library — gated by the `extendedDrawings`
|
|
12054
|
+
* plan feature. Basic tools (trend lines, rays, channels, h/v lines, text,
|
|
12055
|
+
* shapes) are covered by `drawingTools` alone; the catalog's description of
|
|
12056
|
+
* extendedDrawings is "Gann, Fibonacci, pitchforks, spirals, arcs" plus the
|
|
12057
|
+
* forecasting/measurement tools.
|
|
12058
|
+
*/
|
|
12059
|
+
static EXTENDED_DRAWING_TOOLS = /* @__PURE__ */ new Set([
|
|
12060
|
+
"fibRetracement",
|
|
12061
|
+
"trendBasedFibExtension",
|
|
12062
|
+
"fibChannel",
|
|
12063
|
+
"fibTimeZone",
|
|
12064
|
+
"fibSpeedResistanceFan",
|
|
12065
|
+
"trendBasedFibTime",
|
|
12066
|
+
"fibCircles",
|
|
12067
|
+
"fibSpiral",
|
|
12068
|
+
"fibSpeedResistanceArcs",
|
|
12069
|
+
"fibWedge",
|
|
12070
|
+
"pitchfan",
|
|
12071
|
+
"pitchfork",
|
|
12072
|
+
"schiffPitchfork",
|
|
12073
|
+
"modifiedSchiffPitchfork",
|
|
12074
|
+
"insidePitchfork",
|
|
12075
|
+
"gannBox",
|
|
12076
|
+
"gannSquareFixed",
|
|
12077
|
+
"gannSquare",
|
|
12078
|
+
"gannFan",
|
|
12079
|
+
"longPosition",
|
|
12080
|
+
"shortPosition",
|
|
12081
|
+
"priceRange",
|
|
12082
|
+
"dateRange"
|
|
12083
|
+
]);
|
|
12001
12084
|
/**
|
|
12002
12085
|
* Activates a drawing tool. Clicks will place anchors on the chart.
|
|
12003
12086
|
* The tool remains active after each commit (auto-repeat). Press Escape to cancel.
|
|
12087
|
+
* Extended-library tools require the `extendedDrawings` plan feature.
|
|
12004
12088
|
*/
|
|
12005
12089
|
startDrawingTool(type) {
|
|
12006
12090
|
this._assertAlive();
|
|
12091
|
+
if (_TChart.EXTENDED_DRAWING_TOOLS.has(type) && !canUseExtendedDrawings()) {
|
|
12092
|
+
console.warn(`[ForgeCharts] Drawing tool '${type}' requires the extendedDrawings feature, which is not enabled on the active license.`);
|
|
12093
|
+
return;
|
|
12094
|
+
}
|
|
12007
12095
|
this._core.startDrawingTool(type);
|
|
12008
12096
|
}
|
|
12009
12097
|
/** Cancels an active drawing tool and returns to cursor mode. */
|
|
@@ -12352,15 +12440,23 @@ var TChart = class _TChart {
|
|
|
12352
12440
|
this._core.markDirty();
|
|
12353
12441
|
}
|
|
12354
12442
|
// ─ Positions ──
|
|
12355
|
-
/** Replace the entire position set on the chart overlay. */
|
|
12443
|
+
/** Replace the entire position set on the chart overlay. Requires the `positionsOverlay` plan feature. */
|
|
12356
12444
|
setPositions(positions) {
|
|
12357
12445
|
this._assertAlive();
|
|
12446
|
+
if (!canUsePositionsOverlay()) {
|
|
12447
|
+
console.warn("[ForgeCharts] positionsOverlay is not enabled on the active license \u2014 positions not rendered.");
|
|
12448
|
+
return;
|
|
12449
|
+
}
|
|
12358
12450
|
this._overlayStore.setPositions(positions);
|
|
12359
12451
|
this._core.markDirty();
|
|
12360
12452
|
}
|
|
12361
|
-
/** Insert or update a single position (matched by `position.id`). */
|
|
12453
|
+
/** Insert or update a single position (matched by `position.id`). Requires the `positionsOverlay` plan feature. */
|
|
12362
12454
|
upsertPosition(position) {
|
|
12363
12455
|
this._assertAlive();
|
|
12456
|
+
if (!canUsePositionsOverlay()) {
|
|
12457
|
+
console.warn("[ForgeCharts] positionsOverlay is not enabled on the active license \u2014 position not rendered.");
|
|
12458
|
+
return;
|
|
12459
|
+
}
|
|
12364
12460
|
this._overlayStore.upsertPosition(position);
|
|
12365
12461
|
this._core.markDirty();
|
|
12366
12462
|
}
|
|
@@ -17654,6 +17750,55 @@ var DEMONSTRATION_RADIUS = 44;
|
|
|
17654
17750
|
var DEMONSTRATION_FILL = "rgba(255, 200, 50, 0.18)";
|
|
17655
17751
|
var DEMONSTRATION_STROKE = "rgba(255, 200, 50, 0.7)";
|
|
17656
17752
|
var DEMONSTRATION_COLOR = "var(--crosshair-overlay, rgba(255,255,255,0.75))";
|
|
17753
|
+
function applyRuntimeConfig(caps, config) {
|
|
17754
|
+
if (!config) return caps;
|
|
17755
|
+
const orderEntry = config.enableOrderEntry === false ? false : caps.orderEntry;
|
|
17756
|
+
const draggableOrders = config.enableDraggableOrders === false ? false : caps.draggableOrders;
|
|
17757
|
+
const bracketOrders = config.enableBracketOrders === false ? false : caps.bracketOrders;
|
|
17758
|
+
const managedTrading = config.enableManagedTrading === false ? false : caps.managedTrading;
|
|
17759
|
+
const trailingStops = config.enableTrailingStops === false ? false : caps.trailingStops;
|
|
17760
|
+
const indicators = config.enableIndicators === false ? false : caps.indicators;
|
|
17761
|
+
const drawingTools = config.enableDrawingTools === false ? false : caps.drawingTools;
|
|
17762
|
+
const multiPane = config.enableMultiPane === false ? false : caps.multiPane;
|
|
17763
|
+
const barReplay = config.enableBarReplay === false ? false : caps.barReplay;
|
|
17764
|
+
const forgeScript = config.enableForgeScript === false ? false : caps.forgeScript;
|
|
17765
|
+
const aiAgent = config.enableAiAgent === false ? false : caps.aiAgent;
|
|
17766
|
+
const multiChartLayout = config.enableMultiChartLayout === false ? false : caps.multiChartLayout;
|
|
17767
|
+
return {
|
|
17768
|
+
...caps,
|
|
17769
|
+
orderEntry,
|
|
17770
|
+
draggableOrders,
|
|
17771
|
+
bracketOrders,
|
|
17772
|
+
managedTrading,
|
|
17773
|
+
trailingStops,
|
|
17774
|
+
indicators,
|
|
17775
|
+
drawingTools,
|
|
17776
|
+
multiPane,
|
|
17777
|
+
barReplay,
|
|
17778
|
+
forgeScript,
|
|
17779
|
+
aiAgent,
|
|
17780
|
+
multiChartLayout,
|
|
17781
|
+
// Recalculate derived render flags from the updated feature flags.
|
|
17782
|
+
renderOrderEntry: orderEntry,
|
|
17783
|
+
renderBuySellButtons: orderEntry,
|
|
17784
|
+
renderOrderTicket: orderEntry,
|
|
17785
|
+
renderBracketControls: orderEntry && bracketOrders,
|
|
17786
|
+
renderOrderModificationControls: orderEntry && draggableOrders,
|
|
17787
|
+
renderManagedTradingControls: managedTrading,
|
|
17788
|
+
renderIndicators: indicators,
|
|
17789
|
+
renderDrawings: drawingTools
|
|
17790
|
+
};
|
|
17791
|
+
}
|
|
17792
|
+
function useChartCapabilities(config) {
|
|
17793
|
+
const [licCaps, setLicCaps] = useState(() => getCapabilities());
|
|
17794
|
+
useEffect(() => {
|
|
17795
|
+
const unsub = LicenseManager.getInstance().subscribe(() => {
|
|
17796
|
+
setLicCaps(getCapabilities());
|
|
17797
|
+
});
|
|
17798
|
+
return unsub;
|
|
17799
|
+
}, []);
|
|
17800
|
+
return useMemo(() => applyRuntimeConfig(licCaps, config), [licCaps, config]);
|
|
17801
|
+
}
|
|
17657
17802
|
|
|
17658
17803
|
// src/react/assets/logo_dark.png
|
|
17659
17804
|
var logo_dark_default = "../logo_dark-B2KCSRPJ.png";
|
|
@@ -18727,7 +18872,7 @@ function nextRenewalDate(subscribedAt) {
|
|
|
18727
18872
|
const periods = Math.floor((now - start) / MS_30D);
|
|
18728
18873
|
return new Date(start + (periods + 1) * MS_30D);
|
|
18729
18874
|
}
|
|
18730
|
-
function LeftToolbar({ activeTool, onSelectTool, drawingFavorites, onDrawingFavoritesChange, onVisibilityAction, visibilityActiveAction, onVisibilityDeactivate, onLinkClick, onDeleteClick, onLogout, magnetMode, onMagnetModeChange, user, getAuthToken, apiUrl, containerRef, showDrawingTools }) {
|
|
18875
|
+
function LeftToolbar({ activeTool, onSelectTool, drawingFavorites, onDrawingFavoritesChange, onVisibilityAction, visibilityActiveAction, onVisibilityDeactivate, onLinkClick, onDeleteClick, onLogout, magnetMode, onMagnetModeChange, user, getAuthToken, apiUrl, containerRef, showDrawingTools, showExtendedDrawings }) {
|
|
18731
18876
|
const [favorites, setFavorites] = useState(drawingFavorites ?? []);
|
|
18732
18877
|
const [profileOpen, setProfileOpen] = useState(false);
|
|
18733
18878
|
const [profileTab, setProfileTab] = useState("profile");
|
|
@@ -19065,7 +19210,7 @@ function LeftToolbar({ activeTool, onSelectTool, drawingFavorites, onDrawingFavo
|
|
|
19065
19210
|
onFavoritesChange: handleFavoritesChange
|
|
19066
19211
|
}
|
|
19067
19212
|
),
|
|
19068
|
-
/* @__PURE__ */ jsx(
|
|
19213
|
+
showExtendedDrawings !== false && /* @__PURE__ */ jsx(
|
|
19069
19214
|
FibGannGroup,
|
|
19070
19215
|
{
|
|
19071
19216
|
activeTool,
|
|
@@ -19102,34 +19247,36 @@ function LeftToolbar({ activeTool, onSelectTool, drawingFavorites, onDrawingFavo
|
|
|
19102
19247
|
onFavoritesChange: handleFavoritesChange
|
|
19103
19248
|
}
|
|
19104
19249
|
),
|
|
19105
|
-
/* @__PURE__ */
|
|
19106
|
-
|
|
19107
|
-
|
|
19108
|
-
|
|
19109
|
-
|
|
19110
|
-
|
|
19111
|
-
|
|
19112
|
-
|
|
19113
|
-
|
|
19114
|
-
|
|
19115
|
-
|
|
19116
|
-
|
|
19117
|
-
|
|
19118
|
-
|
|
19119
|
-
|
|
19120
|
-
|
|
19121
|
-
|
|
19122
|
-
|
|
19123
|
-
|
|
19124
|
-
|
|
19125
|
-
|
|
19126
|
-
|
|
19127
|
-
|
|
19128
|
-
|
|
19129
|
-
|
|
19130
|
-
|
|
19131
|
-
|
|
19132
|
-
|
|
19250
|
+
showExtendedDrawings !== false && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
19251
|
+
/* @__PURE__ */ jsx(
|
|
19252
|
+
ToolFlyoutGroup,
|
|
19253
|
+
{
|
|
19254
|
+
items: FORECAST_ITEMS,
|
|
19255
|
+
toolSet: FORECAST_TOOLS,
|
|
19256
|
+
itemMap: FORECAST_ITEM_MAP,
|
|
19257
|
+
defaultTool: "longPosition",
|
|
19258
|
+
groupTitle: "Forecasting",
|
|
19259
|
+
activeTool,
|
|
19260
|
+
favorites,
|
|
19261
|
+
onSelectTool,
|
|
19262
|
+
onFavoritesChange: handleFavoritesChange
|
|
19263
|
+
}
|
|
19264
|
+
),
|
|
19265
|
+
/* @__PURE__ */ jsx(
|
|
19266
|
+
ToolFlyoutGroup,
|
|
19267
|
+
{
|
|
19268
|
+
items: MEASURER_ITEMS,
|
|
19269
|
+
toolSet: MEASURER_TOOLS,
|
|
19270
|
+
itemMap: MEASURER_ITEM_MAP,
|
|
19271
|
+
defaultTool: "priceRange",
|
|
19272
|
+
groupTitle: "Measurers",
|
|
19273
|
+
activeTool,
|
|
19274
|
+
favorites,
|
|
19275
|
+
onSelectTool,
|
|
19276
|
+
onFavoritesChange: handleFavoritesChange
|
|
19277
|
+
}
|
|
19278
|
+
)
|
|
19279
|
+
] }),
|
|
19133
19280
|
/* @__PURE__ */ jsx(
|
|
19134
19281
|
MagnetTool,
|
|
19135
19282
|
{
|
|
@@ -22158,6 +22305,7 @@ var ChartCanvas = forwardRef(
|
|
|
22158
22305
|
const [error, setError] = useState(null);
|
|
22159
22306
|
const [bars, setBars] = useState([]);
|
|
22160
22307
|
const [panes, setPanes] = useState([]);
|
|
22308
|
+
const capabilities = useChartCapabilities();
|
|
22161
22309
|
const [indicators, setIndicators] = useState([]);
|
|
22162
22310
|
const [totalHeight, setTotalHeight] = useState(600);
|
|
22163
22311
|
const [activeTool, setActiveTool] = useState(initialActiveTool ?? "cursor");
|
|
@@ -22214,6 +22362,10 @@ var ChartCanvas = forwardRef(
|
|
|
22214
22362
|
onOrderDragEndRef.current = onOrderDragEnd;
|
|
22215
22363
|
const onTpSlCreateRef = useRef(onTpSlCreate);
|
|
22216
22364
|
onTpSlCreateRef.current = onTpSlCreate;
|
|
22365
|
+
const tradingBridgeRef = useRef(tradingBridge);
|
|
22366
|
+
tradingBridgeRef.current = tradingBridge;
|
|
22367
|
+
const symbolRef = useRef(symbol);
|
|
22368
|
+
symbolRef.current = symbol;
|
|
22217
22369
|
const dagRef = useRef(null);
|
|
22218
22370
|
const [computedResults, setComputedResults] = useState(
|
|
22219
22371
|
() => /* @__PURE__ */ new Map()
|
|
@@ -22346,10 +22498,48 @@ var ChartCanvas = forwardRef(
|
|
|
22346
22498
|
onPositionClickRef.current?.(positionId);
|
|
22347
22499
|
});
|
|
22348
22500
|
chart.on("orderDragEnd", ({ orderId, newPrice }) => {
|
|
22349
|
-
onOrderDragEndRef.current
|
|
22501
|
+
if (onOrderDragEndRef.current) {
|
|
22502
|
+
onOrderDragEndRef.current(orderId, newPrice);
|
|
22503
|
+
return;
|
|
22504
|
+
}
|
|
22505
|
+
const bridge = tradingBridgeRef.current;
|
|
22506
|
+
if (!bridge) return;
|
|
22507
|
+
const order = chart.getTradingOverlayStore()?.getOrders().find((o) => o.id === orderId);
|
|
22508
|
+
const isBracketLeg = order?.role === "take_profit" || order?.role === "stop_loss";
|
|
22509
|
+
if (isBracketLeg) {
|
|
22510
|
+
if (!canUseBracketOrders()) return;
|
|
22511
|
+
void bridge.onBracketAdjustIntent?.({
|
|
22512
|
+
type: "bracket_adjust",
|
|
22513
|
+
entryOrderId: order?.groupId ?? orderId,
|
|
22514
|
+
symbol: symbolRef.current,
|
|
22515
|
+
...order?.role === "stop_loss" ? { stopLossPrice: newPrice } : { takeProfitPrice: newPrice },
|
|
22516
|
+
timestamp: Date.now()
|
|
22517
|
+
});
|
|
22518
|
+
} else {
|
|
22519
|
+
if (!canUseDraggableOrders()) return;
|
|
22520
|
+
void bridge.onModifyOrderIntent?.({
|
|
22521
|
+
type: "modify",
|
|
22522
|
+
orderId,
|
|
22523
|
+
symbol: symbolRef.current,
|
|
22524
|
+
limitPrice: newPrice,
|
|
22525
|
+
timestamp: Date.now()
|
|
22526
|
+
});
|
|
22527
|
+
}
|
|
22350
22528
|
});
|
|
22351
22529
|
chart.on("tpSlCreate", ({ entryOrderId, kind, price }) => {
|
|
22352
|
-
onTpSlCreateRef.current
|
|
22530
|
+
if (onTpSlCreateRef.current) {
|
|
22531
|
+
onTpSlCreateRef.current(entryOrderId, kind, price);
|
|
22532
|
+
return;
|
|
22533
|
+
}
|
|
22534
|
+
const bridge = tradingBridgeRef.current;
|
|
22535
|
+
if (!bridge || !canUseBracketOrders()) return;
|
|
22536
|
+
void bridge.onBracketAdjustIntent?.({
|
|
22537
|
+
type: "bracket_adjust",
|
|
22538
|
+
entryOrderId,
|
|
22539
|
+
symbol: symbolRef.current,
|
|
22540
|
+
...kind === "sl" ? { stopLossPrice: price } : { takeProfitPrice: price },
|
|
22541
|
+
timestamp: Date.now()
|
|
22542
|
+
});
|
|
22353
22543
|
});
|
|
22354
22544
|
if (layoutToRestore) {
|
|
22355
22545
|
try {
|
|
@@ -22472,7 +22662,7 @@ var ChartCanvas = forwardRef(
|
|
|
22472
22662
|
}, [initialCanvasSettings]);
|
|
22473
22663
|
useEffect(() => {
|
|
22474
22664
|
const chart = chartRef.current;
|
|
22475
|
-
if (!chart || !canvasSettings.showContractSwitches) {
|
|
22665
|
+
if (!chart || !canvasSettings.showContractSwitches || !capabilities.continuousContracts) {
|
|
22476
22666
|
chart?.setContractSwitches([]);
|
|
22477
22667
|
contractSwitchesRef.current = [];
|
|
22478
22668
|
return;
|
|
@@ -22509,7 +22699,7 @@ var ChartCanvas = forwardRef(
|
|
|
22509
22699
|
return () => {
|
|
22510
22700
|
cancelled = true;
|
|
22511
22701
|
};
|
|
22512
|
-
}, [symbol, apiUrl, canvasSettings.showContractSwitches, providerKey]);
|
|
22702
|
+
}, [symbol, apiUrl, canvasSettings.showContractSwitches, providerKey, capabilities.continuousContracts]);
|
|
22513
22703
|
useEffect(() => {
|
|
22514
22704
|
if (!dagRef.current) dagRef.current = new IndicatorDAG();
|
|
22515
22705
|
const dag = dagRef.current;
|
|
@@ -23248,7 +23438,8 @@ var ChartCanvas = forwardRef(
|
|
|
23248
23438
|
onDrawingFavoritesChange?.(favs);
|
|
23249
23439
|
},
|
|
23250
23440
|
containerRef: outerRef,
|
|
23251
|
-
showDrawingTools,
|
|
23441
|
+
showDrawingTools: showDrawingTools !== false && capabilities.drawingTools,
|
|
23442
|
+
showExtendedDrawings: capabilities.extendedDrawings,
|
|
23252
23443
|
...onLogout ? { onLogout } : {},
|
|
23253
23444
|
...user ? { user } : {},
|
|
23254
23445
|
...getAuthToken ? { getAuthToken } : {},
|
|
@@ -24857,7 +25048,7 @@ function IndicatorsDialog({ onAdd, onClose, maxIndicators, getAuthToken }) {
|
|
|
24857
25048
|
/* @__PURE__ */ jsx("line", { x1: "10", y1: "2", x2: "2", y2: "10" })
|
|
24858
25049
|
] }) })
|
|
24859
25050
|
] }),
|
|
24860
|
-
/* @__PURE__ */ jsx("div", { className: "ind-source-tabs", children: ["built-in", "community", "store"].map((tab) => /* @__PURE__ */ jsxs(
|
|
25051
|
+
/* @__PURE__ */ jsx("div", { className: "ind-source-tabs", children: (canUseCustomIndicators() ? ["built-in", "community", "store"] : ["built-in"]).map((tab) => /* @__PURE__ */ jsxs(
|
|
24861
25052
|
"button",
|
|
24862
25053
|
{
|
|
24863
25054
|
className: `ind-source-tab${tab === sourceTab ? " active" : ""}`,
|
|
@@ -25925,8 +26116,8 @@ function _formatClock(timezone) {
|
|
|
25925
26116
|
}).format(now);
|
|
25926
26117
|
}
|
|
25927
26118
|
}
|
|
25928
|
-
function BottomToolbar({ symbol, timezone, onTimezoneChange, session, onSessionChange, onToggleScriptDrawer, scriptDrawerOpen, showScriptButton, tradingPanelOpen, onToggleTradingPanel, rithmicConnected }) {
|
|
25929
|
-
const showSession = symbolSupportsSession(symbol);
|
|
26119
|
+
function BottomToolbar({ symbol, timezone, onTimezoneChange, session, onSessionChange, onToggleScriptDrawer, scriptDrawerOpen, showScriptButton, showSessionSelector, tradingPanelOpen, onToggleTradingPanel, rithmicConnected }) {
|
|
26120
|
+
const showSession = symbolSupportsSession(symbol) && showSessionSelector !== false;
|
|
25930
26121
|
const [open, setOpen] = useState(false);
|
|
25931
26122
|
const [sessionOpen, setSessionOpen] = useState(false);
|
|
25932
26123
|
const rootRef = useRef(null);
|
|
@@ -26169,6 +26360,8 @@ function sideBadge(side) {
|
|
|
26169
26360
|
function BottomTradingPanel({
|
|
26170
26361
|
apiUrl,
|
|
26171
26362
|
authToken,
|
|
26363
|
+
overlayStore,
|
|
26364
|
+
overlaySymbol,
|
|
26172
26365
|
refreshTick,
|
|
26173
26366
|
visible,
|
|
26174
26367
|
onToggle,
|
|
@@ -26196,7 +26389,7 @@ function BottomTradingPanel({
|
|
|
26196
26389
|
};
|
|
26197
26390
|
}, []);
|
|
26198
26391
|
const fetchData = useCallback(async () => {
|
|
26199
|
-
if (!authToken) return;
|
|
26392
|
+
if (overlayStore || !authToken || !apiUrl) return;
|
|
26200
26393
|
setLoading(true);
|
|
26201
26394
|
const headers = { Authorization: `Bearer ${authToken}` };
|
|
26202
26395
|
try {
|
|
@@ -26214,15 +26407,69 @@ function BottomTradingPanel({
|
|
|
26214
26407
|
} finally {
|
|
26215
26408
|
if (mountedRef.current) setLoading(false);
|
|
26216
26409
|
}
|
|
26217
|
-
}, [apiUrl, authToken]);
|
|
26410
|
+
}, [apiUrl, authToken, overlayStore]);
|
|
26218
26411
|
useEffect(() => {
|
|
26219
26412
|
if (visible) fetchData();
|
|
26220
26413
|
}, [visible, fetchData, refreshTick]);
|
|
26221
26414
|
useEffect(() => {
|
|
26222
|
-
if (!visible) return;
|
|
26415
|
+
if (!visible || overlayStore) return;
|
|
26223
26416
|
const id = setInterval(fetchData, 5e3);
|
|
26224
26417
|
return () => clearInterval(id);
|
|
26225
|
-
}, [visible, fetchData]);
|
|
26418
|
+
}, [visible, fetchData, overlayStore]);
|
|
26419
|
+
useEffect(() => {
|
|
26420
|
+
if (!overlayStore) return;
|
|
26421
|
+
const toIso = (ts) => ts === void 0 ? "" : typeof ts === "number" ? new Date(ts).toISOString() : ts;
|
|
26422
|
+
const sym = overlaySymbol ?? "\u2014";
|
|
26423
|
+
const mirror = () => {
|
|
26424
|
+
if (!mountedRef.current) return;
|
|
26425
|
+
setOrders(overlayStore.getOrders().map((o) => ({
|
|
26426
|
+
id: o.id,
|
|
26427
|
+
client_ref: o.id,
|
|
26428
|
+
broker_order_id: o.id,
|
|
26429
|
+
symbol: sym,
|
|
26430
|
+
side: o.side,
|
|
26431
|
+
order_type: o.role,
|
|
26432
|
+
qty: o.qty,
|
|
26433
|
+
filled_qty: 0,
|
|
26434
|
+
avg_fill_price: null,
|
|
26435
|
+
limit_price: o.price,
|
|
26436
|
+
stop_price: null,
|
|
26437
|
+
status: o.status,
|
|
26438
|
+
reject_reason: null,
|
|
26439
|
+
account_id: null,
|
|
26440
|
+
parent_order_id: o.groupId ?? null,
|
|
26441
|
+
created_at: toIso(o.timestamp),
|
|
26442
|
+
updated_at: toIso(o.timestamp)
|
|
26443
|
+
})));
|
|
26444
|
+
setPositions(overlayStore.getPositions().map((p) => ({
|
|
26445
|
+
id: p.id,
|
|
26446
|
+
symbol: sym,
|
|
26447
|
+
side: p.side,
|
|
26448
|
+
qty: p.qty,
|
|
26449
|
+
entry_price: p.entryPrice,
|
|
26450
|
+
exit_price: null,
|
|
26451
|
+
realized_pnl: null,
|
|
26452
|
+
// ChartPosition additionally has 'partial' — the journal shows it as open.
|
|
26453
|
+
status: p.status === "closed" ? "closed" : "open",
|
|
26454
|
+
account_id: null,
|
|
26455
|
+
opened_at: "",
|
|
26456
|
+
closed_at: null
|
|
26457
|
+
})));
|
|
26458
|
+
setExecutions(overlayStore.getFills().map((f) => ({
|
|
26459
|
+
id: f.id,
|
|
26460
|
+
order_id: f.orderId ?? null,
|
|
26461
|
+
broker_order_id: null,
|
|
26462
|
+
symbol: sym,
|
|
26463
|
+
side: f.side,
|
|
26464
|
+
fill_qty: f.qty,
|
|
26465
|
+
fill_price: f.price,
|
|
26466
|
+
commission: null,
|
|
26467
|
+
executed_at: toIso(f.timestamp)
|
|
26468
|
+
})));
|
|
26469
|
+
};
|
|
26470
|
+
mirror();
|
|
26471
|
+
return overlayStore.onChange(mirror);
|
|
26472
|
+
}, [overlayStore, overlaySymbol]);
|
|
26226
26473
|
const isDev = typeof import.meta !== "undefined" && import.meta.env?.MODE === "development" || typeof process !== "undefined" && process.env?.NODE_ENV === "development";
|
|
26227
26474
|
const [clearing, setClearing] = useState(false);
|
|
26228
26475
|
const handleClearAll = useCallback(async () => {
|
|
@@ -26338,7 +26585,7 @@ function BottomTradingPanel({
|
|
|
26338
26585
|
] })
|
|
26339
26586
|
] }),
|
|
26340
26587
|
/* @__PURE__ */ jsx("div", { style: { flex: 1 } }),
|
|
26341
|
-
isDev && /* @__PURE__ */ jsx(
|
|
26588
|
+
isDev && !overlayStore && /* @__PURE__ */ jsx(
|
|
26342
26589
|
"button",
|
|
26343
26590
|
{
|
|
26344
26591
|
onClick: handleClearAll,
|
|
@@ -26690,6 +26937,1087 @@ function EmptyState({ text }) {
|
|
|
26690
26937
|
fontSize: 12
|
|
26691
26938
|
}, children: text });
|
|
26692
26939
|
}
|
|
26940
|
+
var MIN_RETRY_MS = 2e3;
|
|
26941
|
+
var MAX_RETRY_MS = 3e4;
|
|
26942
|
+
function useBrokerEvents({
|
|
26943
|
+
apiUrl,
|
|
26944
|
+
getAuthToken,
|
|
26945
|
+
onEvent,
|
|
26946
|
+
enabled = true
|
|
26947
|
+
}) {
|
|
26948
|
+
const onEventRef = useRef(onEvent);
|
|
26949
|
+
onEventRef.current = onEvent;
|
|
26950
|
+
useEffect(() => {
|
|
26951
|
+
if (!enabled) return;
|
|
26952
|
+
let active = true;
|
|
26953
|
+
let retryDelay = MIN_RETRY_MS;
|
|
26954
|
+
const controller = new AbortController();
|
|
26955
|
+
async function connect() {
|
|
26956
|
+
while (active) {
|
|
26957
|
+
try {
|
|
26958
|
+
const headers = { Accept: "text/event-stream" };
|
|
26959
|
+
if (getAuthToken) {
|
|
26960
|
+
const token = await getAuthToken();
|
|
26961
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
26962
|
+
}
|
|
26963
|
+
const resp = await fetch(`${apiUrl}/api/broker/events`, {
|
|
26964
|
+
headers,
|
|
26965
|
+
signal: controller.signal
|
|
26966
|
+
});
|
|
26967
|
+
if (!resp.ok || !resp.body) {
|
|
26968
|
+
await sleep(retryDelay, controller.signal);
|
|
26969
|
+
retryDelay = Math.min(retryDelay * 2, MAX_RETRY_MS);
|
|
26970
|
+
continue;
|
|
26971
|
+
}
|
|
26972
|
+
retryDelay = MIN_RETRY_MS;
|
|
26973
|
+
const reader = resp.body.getReader();
|
|
26974
|
+
const decoder = new TextDecoder();
|
|
26975
|
+
let buffer = "";
|
|
26976
|
+
while (active) {
|
|
26977
|
+
const { done, value } = await reader.read();
|
|
26978
|
+
if (done) break;
|
|
26979
|
+
buffer += decoder.decode(value, { stream: true });
|
|
26980
|
+
const lines = buffer.split("\n");
|
|
26981
|
+
buffer = lines.pop() ?? "";
|
|
26982
|
+
for (const line of lines) {
|
|
26983
|
+
if (line.startsWith("data: ")) {
|
|
26984
|
+
try {
|
|
26985
|
+
const payload = JSON.parse(line.slice(6));
|
|
26986
|
+
onEventRef.current(payload);
|
|
26987
|
+
} catch {
|
|
26988
|
+
}
|
|
26989
|
+
}
|
|
26990
|
+
}
|
|
26991
|
+
}
|
|
26992
|
+
} catch (err) {
|
|
26993
|
+
if (!active) break;
|
|
26994
|
+
if (err instanceof Error && err.name === "AbortError") break;
|
|
26995
|
+
await sleep(retryDelay, controller.signal);
|
|
26996
|
+
retryDelay = Math.min(retryDelay * 2, MAX_RETRY_MS);
|
|
26997
|
+
}
|
|
26998
|
+
}
|
|
26999
|
+
}
|
|
27000
|
+
void connect();
|
|
27001
|
+
return () => {
|
|
27002
|
+
active = false;
|
|
27003
|
+
controller.abort();
|
|
27004
|
+
};
|
|
27005
|
+
}, [apiUrl, enabled]);
|
|
27006
|
+
}
|
|
27007
|
+
function sleep(ms, signal) {
|
|
27008
|
+
return new Promise((resolve, reject) => {
|
|
27009
|
+
const id = setTimeout(resolve, ms);
|
|
27010
|
+
signal?.addEventListener("abort", () => {
|
|
27011
|
+
clearTimeout(id);
|
|
27012
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
27013
|
+
}, { once: true });
|
|
27014
|
+
});
|
|
27015
|
+
}
|
|
27016
|
+
var ORDER_TYPES = [
|
|
27017
|
+
{ value: "market", label: "Market" },
|
|
27018
|
+
{ value: "limit", label: "Limit" },
|
|
27019
|
+
{ value: "stop_market", label: "Stop Market" },
|
|
27020
|
+
{ value: "stop_limit", label: "Stop Limit" },
|
|
27021
|
+
{ value: "trailing_stop", label: "Trailing Stop" }
|
|
27022
|
+
];
|
|
27023
|
+
var ORDER_TYPE_HINTS = {
|
|
27024
|
+
market: "Executes immediately at best available price",
|
|
27025
|
+
limit: "Executes at your price or better",
|
|
27026
|
+
stop_market: "Market order triggered when stop price is hit",
|
|
27027
|
+
stop_limit: "Limit order triggered when stop price is hit",
|
|
27028
|
+
trailing_stop: "Stop that trails price by the specified number of ticks"
|
|
27029
|
+
};
|
|
27030
|
+
var TIF_OPTIONS = [
|
|
27031
|
+
{ value: "DAY", label: "DAY \u2014 Good for Day" },
|
|
27032
|
+
{ value: "GTC", label: "GTC \u2014 Good till Cancel" },
|
|
27033
|
+
{ value: "IOC", label: "IOC \u2014 Immediate or Cancel" },
|
|
27034
|
+
{ value: "FOK", label: "FOK \u2014 Fill or Kill" }
|
|
27035
|
+
];
|
|
27036
|
+
var SYMBOL_INFO_DEFAULT = { tickSize: 0.25, isFutures: false };
|
|
27037
|
+
function getDisplayTickSizeFallback(sym) {
|
|
27038
|
+
const base = sym.includes(":") ? sym.slice(sym.indexOf(":") + 1) : sym.split(".")[0] ?? sym;
|
|
27039
|
+
const root = base.replace(/[FGHJKMNQUVXZ]\d{1,4}$/, "").toUpperCase();
|
|
27040
|
+
const DISPLAY_FALLBACKS = {
|
|
27041
|
+
ES: 0.25,
|
|
27042
|
+
MES: 0.25,
|
|
27043
|
+
NQ: 0.25,
|
|
27044
|
+
MNQ: 0.25,
|
|
27045
|
+
RTY: 0.1,
|
|
27046
|
+
M2K: 0.1,
|
|
27047
|
+
YM: 1,
|
|
27048
|
+
MYM: 1,
|
|
27049
|
+
CL: 0.01,
|
|
27050
|
+
MCL: 0.01,
|
|
27051
|
+
NG: 1e-3,
|
|
27052
|
+
GC: 0.1,
|
|
27053
|
+
MGC: 0.1,
|
|
27054
|
+
SI: 5e-3,
|
|
27055
|
+
ZB: 0.03125,
|
|
27056
|
+
ZN: 0.015625,
|
|
27057
|
+
ZF: 78125e-7,
|
|
27058
|
+
ZT: 390625e-8,
|
|
27059
|
+
ZC: 0.25,
|
|
27060
|
+
ZS: 0.25,
|
|
27061
|
+
ZW: 0.25,
|
|
27062
|
+
HE: 25e-5,
|
|
27063
|
+
LE: 25e-5
|
|
27064
|
+
};
|
|
27065
|
+
return DISPLAY_FALLBACKS[root] ?? 0.25;
|
|
27066
|
+
}
|
|
27067
|
+
function detectFuturesDisplayHint(sym) {
|
|
27068
|
+
if (sym.includes(".")) return true;
|
|
27069
|
+
const colon = sym.indexOf(":");
|
|
27070
|
+
const ticker = colon > 0 ? sym.slice(colon + 1) : sym;
|
|
27071
|
+
return /^[A-Z0-9]+[FGHJKMNQUVXZ]\d{1,4}$/.test(ticker);
|
|
27072
|
+
}
|
|
27073
|
+
function stripPrefix(sym) {
|
|
27074
|
+
if (!sym) return "";
|
|
27075
|
+
const colon = sym.indexOf(":");
|
|
27076
|
+
return colon > 0 ? sym.slice(colon + 1) : sym;
|
|
27077
|
+
}
|
|
27078
|
+
function OrderEntryPanel({
|
|
27079
|
+
sessionId,
|
|
27080
|
+
brokerSlug,
|
|
27081
|
+
accounts,
|
|
27082
|
+
selectedAccountId,
|
|
27083
|
+
defaultSymbol = "",
|
|
27084
|
+
apiUrl = "",
|
|
27085
|
+
getAuthToken,
|
|
27086
|
+
connecting = false,
|
|
27087
|
+
connectionError,
|
|
27088
|
+
onRetryConnect,
|
|
27089
|
+
onChangeAccount,
|
|
27090
|
+
onClose,
|
|
27091
|
+
presets,
|
|
27092
|
+
onSavePreset,
|
|
27093
|
+
onLoadPreset,
|
|
27094
|
+
onClosePosition,
|
|
27095
|
+
onCancelOrders,
|
|
27096
|
+
onReverse,
|
|
27097
|
+
onFlattenAll,
|
|
27098
|
+
onPlaceOrder,
|
|
27099
|
+
onPlaceBracketOrder,
|
|
27100
|
+
onDisconnect,
|
|
27101
|
+
onConnect,
|
|
27102
|
+
disconnected = false,
|
|
27103
|
+
symbolResolver,
|
|
27104
|
+
onSymbolChange,
|
|
27105
|
+
showTrailingStop = true,
|
|
27106
|
+
showBracketToggle = true
|
|
27107
|
+
// onToggleTradingPanel and tradingPanelOpen are accepted but not rendered here;
|
|
27108
|
+
// the "Orders · Positions · History" toggle lives in the BottomToolbar instead.
|
|
27109
|
+
}) {
|
|
27110
|
+
const [panelWidth, setPanelWidth] = useState(260);
|
|
27111
|
+
const resizeDrag = useRef(null);
|
|
27112
|
+
const onResizeMouseDown = (e) => {
|
|
27113
|
+
e.preventDefault();
|
|
27114
|
+
resizeDrag.current = { startX: e.clientX, startW: panelWidth };
|
|
27115
|
+
const onMove = (ev) => {
|
|
27116
|
+
if (!resizeDrag.current) return;
|
|
27117
|
+
const delta = ev.clientX - resizeDrag.current.startX;
|
|
27118
|
+
setPanelWidth(Math.min(500, Math.max(220, resizeDrag.current.startW + delta)));
|
|
27119
|
+
};
|
|
27120
|
+
const onUp = () => {
|
|
27121
|
+
resizeDrag.current = null;
|
|
27122
|
+
window.removeEventListener("mousemove", onMove);
|
|
27123
|
+
window.removeEventListener("mouseup", onUp);
|
|
27124
|
+
};
|
|
27125
|
+
window.addEventListener("mousemove", onMove);
|
|
27126
|
+
window.addEventListener("mouseup", onUp);
|
|
27127
|
+
};
|
|
27128
|
+
const [side, setSide] = useState("buy");
|
|
27129
|
+
const [orderType, setOrderType] = useState("market");
|
|
27130
|
+
const [tif, setTif] = useState("DAY");
|
|
27131
|
+
const [symbol, setSymbol] = useState(stripPrefix(defaultSymbol));
|
|
27132
|
+
const [symbolEdited, setSymbolEdited] = useState(false);
|
|
27133
|
+
const [qty, setQty] = useState("1");
|
|
27134
|
+
const [limitPrice, setLimitPrice] = useState("");
|
|
27135
|
+
const [stopPrice, setStopPrice] = useState("");
|
|
27136
|
+
const [trailTicks, setTrailTicks] = useState("10");
|
|
27137
|
+
const [bracketOn, setBracketOn] = useState(false);
|
|
27138
|
+
const [tpTicks, setTpTicks] = useState("20");
|
|
27139
|
+
const [slTicks, setSlTicks] = useState("10");
|
|
27140
|
+
const [submitState, setSubmitState] = useState("idle");
|
|
27141
|
+
const [errorMsg, setErrorMsg] = useState("");
|
|
27142
|
+
const [savingPreset, setSavingPreset] = useState(false);
|
|
27143
|
+
const [presetName, setPresetName] = useState("");
|
|
27144
|
+
const [selectedPreset, setSelectedPreset] = useState("");
|
|
27145
|
+
const [brokerStatus, setBrokerStatus] = useState(null);
|
|
27146
|
+
const [pendingClientRef, setPendingClientRef] = useState(null);
|
|
27147
|
+
const [symDialogOpen, setSymDialogOpen] = useState(false);
|
|
27148
|
+
useBrokerEvents({
|
|
27149
|
+
apiUrl: apiUrl ?? "",
|
|
27150
|
+
...getAuthToken ? { getAuthToken } : {},
|
|
27151
|
+
enabled: !!(apiUrl && getAuthToken),
|
|
27152
|
+
onEvent: useCallback((event) => {
|
|
27153
|
+
if (!pendingClientRef) return;
|
|
27154
|
+
if (!("clientRef" in event) || event.clientRef !== pendingClientRef) return;
|
|
27155
|
+
switch (event.type) {
|
|
27156
|
+
case "order.updated":
|
|
27157
|
+
if (event.status === "open") {
|
|
27158
|
+
setBrokerStatus({ kind: "open" });
|
|
27159
|
+
} else if (event.status === "partial") {
|
|
27160
|
+
setBrokerStatus({ kind: "partial", filledQty: event.filledQty ?? 0, totalQty: parseFloat(event.filledQty?.toString() ?? "0") });
|
|
27161
|
+
}
|
|
27162
|
+
break;
|
|
27163
|
+
case "order.filled":
|
|
27164
|
+
setBrokerStatus({ kind: "filled", filledQty: event.filledQty ?? 0, avgPrice: event.avgFillPrice ?? 0 });
|
|
27165
|
+
break;
|
|
27166
|
+
case "order.rejected":
|
|
27167
|
+
setBrokerStatus({ kind: "rejected", reason: event.reason ?? "Unknown reason" });
|
|
27168
|
+
break;
|
|
27169
|
+
case "order.cancelled":
|
|
27170
|
+
setBrokerStatus({ kind: "cancelled" });
|
|
27171
|
+
break;
|
|
27172
|
+
}
|
|
27173
|
+
}, [pendingClientRef])
|
|
27174
|
+
});
|
|
27175
|
+
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
27176
|
+
const [draftTp, setDraftTp] = useState("20");
|
|
27177
|
+
const [draftSl, setDraftSl] = useState("10");
|
|
27178
|
+
const _pickedSymbolRef = useRef(null);
|
|
27179
|
+
const prevDefaultRef = useRef(defaultSymbol);
|
|
27180
|
+
useEffect(() => {
|
|
27181
|
+
if (defaultSymbol !== prevDefaultRef.current) {
|
|
27182
|
+
prevDefaultRef.current = defaultSymbol;
|
|
27183
|
+
_pickedSymbolRef.current = null;
|
|
27184
|
+
setSymbol(stripPrefix(defaultSymbol));
|
|
27185
|
+
setSymbolEdited(false);
|
|
27186
|
+
}
|
|
27187
|
+
}, [defaultSymbol]);
|
|
27188
|
+
const symbolForSubmit = _pickedSymbolRef.current ?? (!symbolEdited && defaultSymbol ? defaultSymbol : symbol);
|
|
27189
|
+
const [symbolInfo, setSymbolInfo] = useState(() => ({
|
|
27190
|
+
...SYMBOL_INFO_DEFAULT,
|
|
27191
|
+
tickSize: getDisplayTickSizeFallback(defaultSymbol || symbol),
|
|
27192
|
+
isFutures: detectFuturesDisplayHint(defaultSymbol || symbol)
|
|
27193
|
+
}));
|
|
27194
|
+
useEffect(() => {
|
|
27195
|
+
const sym = symbolForSubmit || defaultSymbol;
|
|
27196
|
+
if (!sym) return;
|
|
27197
|
+
setSymbolInfo({
|
|
27198
|
+
tickSize: getDisplayTickSizeFallback(sym),
|
|
27199
|
+
isFutures: detectFuturesDisplayHint(sym)
|
|
27200
|
+
});
|
|
27201
|
+
if (!apiUrl) return;
|
|
27202
|
+
const ctrl = new AbortController();
|
|
27203
|
+
fetch(`${apiUrl}/api/symbols/resolve?q=${encodeURIComponent(sym)}`, { signal: ctrl.signal }).then((r) => r.ok ? r.json() : Promise.reject()).then(({ tickSize: tickSize2, isFutures, assetClass }) => {
|
|
27204
|
+
const serverIsFutures = isFutures ?? (assetClass === "futures" || detectFuturesDisplayHint(sym));
|
|
27205
|
+
setSymbolInfo({
|
|
27206
|
+
tickSize: tickSize2 ?? getDisplayTickSizeFallback(sym),
|
|
27207
|
+
isFutures: serverIsFutures
|
|
27208
|
+
});
|
|
27209
|
+
}).catch(() => {
|
|
27210
|
+
});
|
|
27211
|
+
return () => ctrl.abort();
|
|
27212
|
+
}, [symbolForSubmit, defaultSymbol, apiUrl]);
|
|
27213
|
+
const futures = symbolInfo.isFutures;
|
|
27214
|
+
const tickSize = symbolInfo.tickSize;
|
|
27215
|
+
const [normalizedHint, setNormalizedHint] = useState(null);
|
|
27216
|
+
useEffect(() => {
|
|
27217
|
+
if (!symbol || !symbolEdited || !apiUrl) {
|
|
27218
|
+
setNormalizedHint(null);
|
|
27219
|
+
return;
|
|
27220
|
+
}
|
|
27221
|
+
const t = setTimeout(() => {
|
|
27222
|
+
fetch(`${apiUrl}/api/symbols/resolve?q=${encodeURIComponent(symbol)}`).then((r) => r.ok ? r.json() : Promise.reject()).then(({ canonical }) => setNormalizedHint(canonical !== symbol ? canonical : null)).catch(() => setNormalizedHint(null));
|
|
27223
|
+
}, 400);
|
|
27224
|
+
return () => clearTimeout(t);
|
|
27225
|
+
}, [symbol, symbolEdited, apiUrl]);
|
|
27226
|
+
const showLimit = orderType === "limit" || orderType === "stop_limit";
|
|
27227
|
+
const showStop = orderType === "stop_market" || orderType === "stop_limit";
|
|
27228
|
+
const showTrailing = orderType === "trailing_stop";
|
|
27229
|
+
const showBracket = orderType === "market" || orderType === "limit";
|
|
27230
|
+
const tpPoints = (parseInt(draftTp, 10) || 0) * tickSize;
|
|
27231
|
+
const slPoints = (parseInt(draftSl, 10) || 0) * tickSize;
|
|
27232
|
+
const handleToggleBracket = useCallback(() => {
|
|
27233
|
+
if (bracketOn) {
|
|
27234
|
+
setBracketOn(false);
|
|
27235
|
+
} else {
|
|
27236
|
+
setDraftTp(tpTicks);
|
|
27237
|
+
setDraftSl(slTicks);
|
|
27238
|
+
setDrawerOpen(true);
|
|
27239
|
+
}
|
|
27240
|
+
}, [bracketOn, tpTicks, slTicks]);
|
|
27241
|
+
const handleOpenBracketSettings = useCallback(() => {
|
|
27242
|
+
setDraftTp(tpTicks);
|
|
27243
|
+
setDraftSl(slTicks);
|
|
27244
|
+
setDrawerOpen(true);
|
|
27245
|
+
}, [tpTicks, slTicks]);
|
|
27246
|
+
const handleDrawerSave = useCallback(() => {
|
|
27247
|
+
setTpTicks(draftTp);
|
|
27248
|
+
setSlTicks(draftSl);
|
|
27249
|
+
setBracketOn(true);
|
|
27250
|
+
setDrawerOpen(false);
|
|
27251
|
+
}, [draftTp, draftSl]);
|
|
27252
|
+
const handleDrawerCancel = useCallback(() => {
|
|
27253
|
+
setDrawerOpen(false);
|
|
27254
|
+
}, []);
|
|
27255
|
+
const handleLoadPreset = useCallback((name) => {
|
|
27256
|
+
setSelectedPreset(name);
|
|
27257
|
+
const preset = presets?.find((p) => p.name === name);
|
|
27258
|
+
if (!preset || !onLoadPreset) return;
|
|
27259
|
+
if (preset.side) setSide(preset.side);
|
|
27260
|
+
if (preset.orderType) setOrderType(preset.orderType);
|
|
27261
|
+
if (preset.qty) setQty(preset.qty);
|
|
27262
|
+
if (preset.tif) setTif(preset.tif);
|
|
27263
|
+
if (preset.limitPrice !== void 0) setLimitPrice(preset.limitPrice);
|
|
27264
|
+
if (preset.stopPrice !== void 0) setStopPrice(preset.stopPrice);
|
|
27265
|
+
if (preset.trailTicks !== void 0) setTrailTicks(preset.trailTicks);
|
|
27266
|
+
if (preset.bracketOn !== void 0) setBracketOn(preset.bracketOn);
|
|
27267
|
+
if (preset.tpTicks !== void 0) setTpTicks(preset.tpTicks);
|
|
27268
|
+
if (preset.slTicks !== void 0) setSlTicks(preset.slTicks);
|
|
27269
|
+
onLoadPreset(preset);
|
|
27270
|
+
}, [presets, onLoadPreset]);
|
|
27271
|
+
const handleSavePreset = useCallback(() => {
|
|
27272
|
+
const name = presetName.trim();
|
|
27273
|
+
if (!name || !onSavePreset) return;
|
|
27274
|
+
onSavePreset({ name, side, orderType, qty, tif, limitPrice, stopPrice, trailTicks, bracketOn, tpTicks, slTicks });
|
|
27275
|
+
setPresetName("");
|
|
27276
|
+
setSavingPreset(false);
|
|
27277
|
+
setSelectedPreset(name);
|
|
27278
|
+
}, [presetName, onSavePreset, side, orderType, qty, tif, limitPrice, stopPrice, trailTicks, bracketOn, tpTicks, slTicks]);
|
|
27279
|
+
const handleSubmit = useCallback(async (e) => {
|
|
27280
|
+
e.preventDefault();
|
|
27281
|
+
setErrorMsg("");
|
|
27282
|
+
setBrokerStatus(null);
|
|
27283
|
+
const qtyNum = futures ? parseInt(qty, 10) : parseFloat(qty);
|
|
27284
|
+
if (!symbol.trim()) {
|
|
27285
|
+
setErrorMsg("Symbol is required");
|
|
27286
|
+
return;
|
|
27287
|
+
}
|
|
27288
|
+
if (!qtyNum || qtyNum <= 0) {
|
|
27289
|
+
setErrorMsg("Quantity must be positive");
|
|
27290
|
+
return;
|
|
27291
|
+
}
|
|
27292
|
+
if (showLimit && !limitPrice) {
|
|
27293
|
+
setErrorMsg("Limit price is required");
|
|
27294
|
+
return;
|
|
27295
|
+
}
|
|
27296
|
+
if (showStop && !stopPrice) {
|
|
27297
|
+
setErrorMsg("Stop price is required");
|
|
27298
|
+
return;
|
|
27299
|
+
}
|
|
27300
|
+
if (showTrailing && parseInt(trailTicks, 10) < 1) {
|
|
27301
|
+
setErrorMsg("Trail ticks must be at least 1");
|
|
27302
|
+
return;
|
|
27303
|
+
}
|
|
27304
|
+
if (bracketOn && showBracket) {
|
|
27305
|
+
if (parseInt(tpTicks, 10) < 1) {
|
|
27306
|
+
setErrorMsg("Take-profit ticks are required");
|
|
27307
|
+
return;
|
|
27308
|
+
}
|
|
27309
|
+
if (parseInt(slTicks, 10) < 1) {
|
|
27310
|
+
setErrorMsg("Stop-loss ticks are required");
|
|
27311
|
+
return;
|
|
27312
|
+
}
|
|
27313
|
+
}
|
|
27314
|
+
setSubmitState("submitting");
|
|
27315
|
+
try {
|
|
27316
|
+
const clientRef = crypto.randomUUID();
|
|
27317
|
+
setPendingClientRef(clientRef);
|
|
27318
|
+
let resolvedSymbol = symbolForSubmit;
|
|
27319
|
+
if (!resolvedSymbol.includes(":") && apiUrl) {
|
|
27320
|
+
try {
|
|
27321
|
+
const r = await fetch(
|
|
27322
|
+
`${apiUrl}/api/symbols/resolve?q=${encodeURIComponent(resolvedSymbol)}`,
|
|
27323
|
+
{ signal: AbortSignal.timeout(1500) }
|
|
27324
|
+
);
|
|
27325
|
+
if (r.ok) {
|
|
27326
|
+
const { canonical } = await r.json();
|
|
27327
|
+
resolvedSymbol = canonical;
|
|
27328
|
+
_pickedSymbolRef.current = canonical;
|
|
27329
|
+
}
|
|
27330
|
+
} catch {
|
|
27331
|
+
}
|
|
27332
|
+
}
|
|
27333
|
+
if (bracketOn && showBracket) {
|
|
27334
|
+
const bracketParams = {
|
|
27335
|
+
clientRef,
|
|
27336
|
+
symbol: resolvedSymbol,
|
|
27337
|
+
accountId: selectedAccountId,
|
|
27338
|
+
side,
|
|
27339
|
+
type: orderType,
|
|
27340
|
+
qty: qtyNum,
|
|
27341
|
+
timeInForce: tif,
|
|
27342
|
+
...showLimit ? { limitPrice: parseFloat(limitPrice) } : {},
|
|
27343
|
+
takeProfitTicks: parseInt(tpTicks, 10),
|
|
27344
|
+
stopLossTicks: parseInt(slTicks, 10)
|
|
27345
|
+
};
|
|
27346
|
+
if (onPlaceBracketOrder) {
|
|
27347
|
+
await onPlaceBracketOrder(bracketParams);
|
|
27348
|
+
} else {
|
|
27349
|
+
const headers = { "Content-Type": "application/json" };
|
|
27350
|
+
if (getAuthToken) headers["Authorization"] = `Bearer ${await getAuthToken()}`;
|
|
27351
|
+
const resp = await fetch(`${apiUrl}/api/broker/user-bracket`, {
|
|
27352
|
+
method: "POST",
|
|
27353
|
+
headers,
|
|
27354
|
+
body: JSON.stringify({ sessionId, ...bracketParams })
|
|
27355
|
+
});
|
|
27356
|
+
if (!resp.ok) {
|
|
27357
|
+
const err = await resp.json().catch(() => ({}));
|
|
27358
|
+
throw new Error(err.error ?? `Error ${resp.status}`);
|
|
27359
|
+
}
|
|
27360
|
+
}
|
|
27361
|
+
} else {
|
|
27362
|
+
const orderParams = {
|
|
27363
|
+
clientRef,
|
|
27364
|
+
symbol: resolvedSymbol,
|
|
27365
|
+
accountId: selectedAccountId,
|
|
27366
|
+
side,
|
|
27367
|
+
type: orderType,
|
|
27368
|
+
qty: qtyNum,
|
|
27369
|
+
timeInForce: tif,
|
|
27370
|
+
...showLimit ? { limitPrice: parseFloat(limitPrice) } : {},
|
|
27371
|
+
...showStop ? { stopPrice: parseFloat(stopPrice) } : {},
|
|
27372
|
+
...showTrailing ? { trailTicks: parseInt(trailTicks, 10) } : {}
|
|
27373
|
+
};
|
|
27374
|
+
if (onPlaceOrder) {
|
|
27375
|
+
await onPlaceOrder(orderParams);
|
|
27376
|
+
} else {
|
|
27377
|
+
const headers = { "Content-Type": "application/json" };
|
|
27378
|
+
if (getAuthToken) headers["Authorization"] = `Bearer ${await getAuthToken()}`;
|
|
27379
|
+
const resp = await fetch(`${apiUrl}/api/broker/user-order`, {
|
|
27380
|
+
method: "POST",
|
|
27381
|
+
headers,
|
|
27382
|
+
body: JSON.stringify({ sessionId, ...orderParams })
|
|
27383
|
+
});
|
|
27384
|
+
if (!resp.ok) {
|
|
27385
|
+
const err = await resp.json().catch(() => ({}));
|
|
27386
|
+
throw new Error(err.error ?? `Error ${resp.status}`);
|
|
27387
|
+
}
|
|
27388
|
+
}
|
|
27389
|
+
}
|
|
27390
|
+
setSubmitState("success");
|
|
27391
|
+
setTimeout(() => {
|
|
27392
|
+
setSubmitState("idle");
|
|
27393
|
+
setQty("1");
|
|
27394
|
+
setLimitPrice("");
|
|
27395
|
+
setStopPrice("");
|
|
27396
|
+
setTrailTicks("10");
|
|
27397
|
+
}, 1400);
|
|
27398
|
+
} catch (err) {
|
|
27399
|
+
setSubmitState("error");
|
|
27400
|
+
setErrorMsg(err instanceof Error ? err.message : "Order failed");
|
|
27401
|
+
}
|
|
27402
|
+
}, [
|
|
27403
|
+
symbol,
|
|
27404
|
+
side,
|
|
27405
|
+
orderType,
|
|
27406
|
+
tif,
|
|
27407
|
+
qty,
|
|
27408
|
+
limitPrice,
|
|
27409
|
+
stopPrice,
|
|
27410
|
+
trailTicks,
|
|
27411
|
+
bracketOn,
|
|
27412
|
+
tpTicks,
|
|
27413
|
+
slTicks,
|
|
27414
|
+
showLimit,
|
|
27415
|
+
showStop,
|
|
27416
|
+
showTrailing,
|
|
27417
|
+
showBracket,
|
|
27418
|
+
futures,
|
|
27419
|
+
sessionId,
|
|
27420
|
+
selectedAccountId,
|
|
27421
|
+
apiUrl,
|
|
27422
|
+
getAuthToken,
|
|
27423
|
+
symbolForSubmit,
|
|
27424
|
+
setPendingClientRef,
|
|
27425
|
+
setBrokerStatus,
|
|
27426
|
+
onPlaceOrder,
|
|
27427
|
+
onPlaceBracketOrder
|
|
27428
|
+
]);
|
|
27429
|
+
return /* @__PURE__ */ jsxs("div", { className: "oep-panel", style: { width: panelWidth }, children: [
|
|
27430
|
+
/* @__PURE__ */ jsx("div", { className: "oep-resize-handle", onMouseDown: onResizeMouseDown }),
|
|
27431
|
+
/* @__PURE__ */ jsxs("div", { className: "oep-header", children: [
|
|
27432
|
+
/* @__PURE__ */ jsxs("span", { className: "oep-title", children: [
|
|
27433
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "13", height: "13", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", style: { marginRight: 6 }, children: [
|
|
27434
|
+
/* @__PURE__ */ jsx("rect", { x: "1.5", y: "3", width: "13", height: "10", rx: "1.5" }),
|
|
27435
|
+
/* @__PURE__ */ jsx("line", { x1: "5", y1: "8", x2: "11", y2: "8" }),
|
|
27436
|
+
/* @__PURE__ */ jsx("line", { x1: "8", y1: "5", x2: "8", y2: "11" })
|
|
27437
|
+
] }),
|
|
27438
|
+
drawerOpen ? "Bracket Order Settings" : "Order Entry"
|
|
27439
|
+
] }),
|
|
27440
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
|
|
27441
|
+
!drawerOpen && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
27442
|
+
/* @__PURE__ */ jsxs("span", { className: "oep-broker-badge", children: [
|
|
27443
|
+
/* @__PURE__ */ jsx(
|
|
27444
|
+
"span",
|
|
27445
|
+
{
|
|
27446
|
+
className: `oep-status-dot ${connecting ? "oep-status-dot--connecting" : disconnected || connectionError ? "oep-status-dot--disconnected" : "oep-status-dot--connected"}`,
|
|
27447
|
+
title: connecting ? "Connecting\u2026" : disconnected ? "Disconnected" : "Connected"
|
|
27448
|
+
}
|
|
27449
|
+
),
|
|
27450
|
+
brokerSlug || "\u2014"
|
|
27451
|
+
] }),
|
|
27452
|
+
disconnected ? onConnect && /* @__PURE__ */ jsxs("button", { className: "oep-disconnect-btn oep-disconnect-btn--connect", onClick: onConnect, title: "Reconnect broker", children: [
|
|
27453
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 14 14", width: "10", height: "10", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", children: [
|
|
27454
|
+
/* @__PURE__ */ jsx("path", { d: "M9 7h4M13 7l-2-2M13 7l-2 2" }),
|
|
27455
|
+
/* @__PURE__ */ jsx("path", { d: "M5 7H1M1 7l2-2M1 7l2 2" }),
|
|
27456
|
+
/* @__PURE__ */ jsx("circle", { cx: "7", cy: "7", r: "1.5", fill: "currentColor", stroke: "none" })
|
|
27457
|
+
] }),
|
|
27458
|
+
"Connect"
|
|
27459
|
+
] }) : onDisconnect && /* @__PURE__ */ jsxs("button", { className: "oep-disconnect-btn", onClick: onDisconnect, title: "Disconnect broker", children: [
|
|
27460
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 14 14", width: "10", height: "10", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", children: [
|
|
27461
|
+
/* @__PURE__ */ jsx("path", { d: "M5 7H1M1 7l2-2M1 7l2 2" }),
|
|
27462
|
+
/* @__PURE__ */ jsx("path", { d: "M7 3V1M7 1l-2 2M7 1l2 2" }),
|
|
27463
|
+
/* @__PURE__ */ jsx("path", { d: "M9 7h4M13 7l-2-2M13 7l-2 2" }),
|
|
27464
|
+
/* @__PURE__ */ jsx("path", { d: "M7 11v2M7 13l-2-2M7 13l2-2" })
|
|
27465
|
+
] }),
|
|
27466
|
+
"Disconnect"
|
|
27467
|
+
] })
|
|
27468
|
+
] }),
|
|
27469
|
+
/* @__PURE__ */ jsx(
|
|
27470
|
+
"button",
|
|
27471
|
+
{
|
|
27472
|
+
className: "oep-close",
|
|
27473
|
+
onClick: drawerOpen ? handleDrawerCancel : onClose,
|
|
27474
|
+
title: drawerOpen ? "Back" : "Close order panel",
|
|
27475
|
+
children: drawerOpen ? /* @__PURE__ */ jsx("svg", { viewBox: "0 0 14 14", width: "11", height: "11", stroke: "currentColor", strokeWidth: "1.8", fill: "none", children: /* @__PURE__ */ jsx("polyline", { points: "9,2 4,7 9,12" }) }) : /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 14 14", width: "11", height: "11", stroke: "currentColor", strokeWidth: "1.8", fill: "none", children: [
|
|
27476
|
+
/* @__PURE__ */ jsx("line", { x1: "2", y1: "2", x2: "12", y2: "12" }),
|
|
27477
|
+
/* @__PURE__ */ jsx("line", { x1: "12", y1: "2", x2: "2", y2: "12" })
|
|
27478
|
+
] })
|
|
27479
|
+
}
|
|
27480
|
+
)
|
|
27481
|
+
] })
|
|
27482
|
+
] }),
|
|
27483
|
+
symDialogOpen && symbolResolver && /* @__PURE__ */ jsx(
|
|
27484
|
+
SymbolSearchDialog,
|
|
27485
|
+
{
|
|
27486
|
+
current: symbol,
|
|
27487
|
+
symbolResolver,
|
|
27488
|
+
onSelect: (picked) => {
|
|
27489
|
+
_pickedSymbolRef.current = picked;
|
|
27490
|
+
setSymbol(stripPrefix(picked));
|
|
27491
|
+
setSymbolEdited(false);
|
|
27492
|
+
onSymbolChange?.(picked);
|
|
27493
|
+
},
|
|
27494
|
+
onClose: () => setSymDialogOpen(false)
|
|
27495
|
+
}
|
|
27496
|
+
),
|
|
27497
|
+
connecting && /* @__PURE__ */ jsxs("div", { className: "oep-body oep-body--status", children: [
|
|
27498
|
+
/* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", style: { animation: "spin 0.9s linear infinite", flexShrink: 0 }, children: [
|
|
27499
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "9", fill: "none", stroke: "var(--border)", strokeWidth: "2.5" }),
|
|
27500
|
+
/* @__PURE__ */ jsx("path", { d: "M12 3 A9 9 0 0 1 21 12", fill: "none", stroke: "var(--primary)", strokeWidth: "2.5", strokeLinecap: "round" })
|
|
27501
|
+
] }),
|
|
27502
|
+
/* @__PURE__ */ jsx("span", { style: { color: "var(--text-muted)", fontSize: 13 }, children: "Connecting to broker\u2026" })
|
|
27503
|
+
] }),
|
|
27504
|
+
!connecting && connectionError && /* @__PURE__ */ jsxs("div", { className: "oep-body oep-body--status", children: [
|
|
27505
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 20 20", width: "22", height: "22", fill: "none", stroke: "#ef4444", strokeWidth: "1.8", strokeLinecap: "round", children: [
|
|
27506
|
+
/* @__PURE__ */ jsx("circle", { cx: "10", cy: "10", r: "8" }),
|
|
27507
|
+
/* @__PURE__ */ jsx("line", { x1: "10", y1: "6", x2: "10", y2: "11" }),
|
|
27508
|
+
/* @__PURE__ */ jsx("circle", { cx: "10", cy: "14", r: "0.8", fill: "#ef4444" })
|
|
27509
|
+
] }),
|
|
27510
|
+
/* @__PURE__ */ jsx("span", { style: { color: "var(--text-muted)", fontSize: 13, textAlign: "center" }, children: connectionError }),
|
|
27511
|
+
onRetryConnect && /* @__PURE__ */ jsx("button", { className: "oep-retry-btn", onClick: onRetryConnect, children: "Retry Connection" })
|
|
27512
|
+
] }),
|
|
27513
|
+
!connecting && !connectionError && drawerOpen && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
27514
|
+
/* @__PURE__ */ jsxs("div", { className: "oep-drawer-body", children: [
|
|
27515
|
+
/* @__PURE__ */ jsx("p", { className: "oep-drawer-desc", children: "Set your TP and SL as tick offsets from entry price. The bracket order will automatically place both orders when your entry fills." }),
|
|
27516
|
+
/* @__PURE__ */ jsxs("p", { className: "oep-drawer-ticksize", children: [
|
|
27517
|
+
"Tick size for ",
|
|
27518
|
+
symbol || "\u2014",
|
|
27519
|
+
": ",
|
|
27520
|
+
tickSize.toString()
|
|
27521
|
+
] }),
|
|
27522
|
+
/* @__PURE__ */ jsxs("div", { className: "oep-drawer-field", children: [
|
|
27523
|
+
/* @__PURE__ */ jsx("label", { className: "oep-drawer-label oep-drawer-label--tp", children: "Take Profit (ticks)" }),
|
|
27524
|
+
/* @__PURE__ */ jsx(
|
|
27525
|
+
"input",
|
|
27526
|
+
{
|
|
27527
|
+
className: "oep-input",
|
|
27528
|
+
type: "number",
|
|
27529
|
+
min: 1,
|
|
27530
|
+
step: 1,
|
|
27531
|
+
value: draftTp,
|
|
27532
|
+
onChange: (e) => setDraftTp(e.target.value)
|
|
27533
|
+
}
|
|
27534
|
+
),
|
|
27535
|
+
/* @__PURE__ */ jsx(
|
|
27536
|
+
"input",
|
|
27537
|
+
{
|
|
27538
|
+
className: "oep-slider oep-slider--tp",
|
|
27539
|
+
type: "range",
|
|
27540
|
+
min: 1,
|
|
27541
|
+
max: 100,
|
|
27542
|
+
value: parseInt(draftTp, 10) || 1,
|
|
27543
|
+
onChange: (e) => setDraftTp(e.target.value)
|
|
27544
|
+
}
|
|
27545
|
+
),
|
|
27546
|
+
/* @__PURE__ */ jsxs("span", { className: "oep-tick-convert", children: [
|
|
27547
|
+
parseInt(draftTp, 10) || 0,
|
|
27548
|
+
" ticks = ",
|
|
27549
|
+
tpPoints.toFixed(tickSize < 0.01 ? 6 : tickSize < 0.1 ? 4 : 2),
|
|
27550
|
+
" points"
|
|
27551
|
+
] })
|
|
27552
|
+
] }),
|
|
27553
|
+
/* @__PURE__ */ jsxs("div", { className: "oep-drawer-field", children: [
|
|
27554
|
+
/* @__PURE__ */ jsx("label", { className: "oep-drawer-label oep-drawer-label--sl", children: "Stop Loss (ticks)" }),
|
|
27555
|
+
/* @__PURE__ */ jsx(
|
|
27556
|
+
"input",
|
|
27557
|
+
{
|
|
27558
|
+
className: "oep-input",
|
|
27559
|
+
type: "number",
|
|
27560
|
+
min: 1,
|
|
27561
|
+
step: 1,
|
|
27562
|
+
value: draftSl,
|
|
27563
|
+
onChange: (e) => setDraftSl(e.target.value)
|
|
27564
|
+
}
|
|
27565
|
+
),
|
|
27566
|
+
/* @__PURE__ */ jsx(
|
|
27567
|
+
"input",
|
|
27568
|
+
{
|
|
27569
|
+
className: "oep-slider oep-slider--sl",
|
|
27570
|
+
type: "range",
|
|
27571
|
+
min: 1,
|
|
27572
|
+
max: 100,
|
|
27573
|
+
value: parseInt(draftSl, 10) || 1,
|
|
27574
|
+
onChange: (e) => setDraftSl(e.target.value)
|
|
27575
|
+
}
|
|
27576
|
+
),
|
|
27577
|
+
/* @__PURE__ */ jsxs("span", { className: "oep-tick-convert", children: [
|
|
27578
|
+
parseInt(draftSl, 10) || 0,
|
|
27579
|
+
" ticks = ",
|
|
27580
|
+
slPoints.toFixed(tickSize < 0.01 ? 6 : tickSize < 0.1 ? 4 : 2),
|
|
27581
|
+
" points"
|
|
27582
|
+
] })
|
|
27583
|
+
] })
|
|
27584
|
+
] }),
|
|
27585
|
+
/* @__PURE__ */ jsxs("div", { className: "oep-drawer-footer", children: [
|
|
27586
|
+
/* @__PURE__ */ jsx("button", { className: "oep-drawer-btn oep-drawer-btn--cancel", onClick: handleDrawerCancel, children: "Cancel" }),
|
|
27587
|
+
/* @__PURE__ */ jsx(
|
|
27588
|
+
"button",
|
|
27589
|
+
{
|
|
27590
|
+
className: "oep-drawer-btn oep-drawer-btn--save",
|
|
27591
|
+
onClick: handleDrawerSave,
|
|
27592
|
+
disabled: !draftTp || parseInt(draftTp, 10) < 1 || !draftSl || parseInt(draftSl, 10) < 1,
|
|
27593
|
+
children: "Save"
|
|
27594
|
+
}
|
|
27595
|
+
)
|
|
27596
|
+
] })
|
|
27597
|
+
] }),
|
|
27598
|
+
!connecting && !connectionError && !drawerOpen && /* @__PURE__ */ jsxs("div", { className: "oep-body", children: [
|
|
27599
|
+
/* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, noValidate: true, children: [
|
|
27600
|
+
accounts.length === 1 && /* @__PURE__ */ jsxs("div", { className: "oep-row", children: [
|
|
27601
|
+
/* @__PURE__ */ jsx("label", { className: "oep-label", children: "Account" }),
|
|
27602
|
+
/* @__PURE__ */ jsx("div", { className: "oep-account-chip", children: accounts[0].name })
|
|
27603
|
+
] }),
|
|
27604
|
+
accounts.length > 1 && /* @__PURE__ */ jsxs("div", { className: "oep-row", children: [
|
|
27605
|
+
/* @__PURE__ */ jsx("label", { className: "oep-label", children: "Account" }),
|
|
27606
|
+
/* @__PURE__ */ jsx(
|
|
27607
|
+
"select",
|
|
27608
|
+
{
|
|
27609
|
+
className: "oep-select",
|
|
27610
|
+
value: selectedAccountId,
|
|
27611
|
+
onChange: (e) => onChangeAccount(e.target.value),
|
|
27612
|
+
children: accounts.map((a) => /* @__PURE__ */ jsx("option", { value: a.id, children: a.name }, a.id))
|
|
27613
|
+
}
|
|
27614
|
+
)
|
|
27615
|
+
] }),
|
|
27616
|
+
/* @__PURE__ */ jsxs("div", { className: "oep-row", children: [
|
|
27617
|
+
/* @__PURE__ */ jsx("label", { className: "oep-label", children: futures ? "Contract" : "Symbol" }),
|
|
27618
|
+
symbolResolver ? /* @__PURE__ */ jsxs(
|
|
27619
|
+
"button",
|
|
27620
|
+
{
|
|
27621
|
+
type: "button",
|
|
27622
|
+
className: "oep-input oep-symbol-btn",
|
|
27623
|
+
onClick: () => setSymDialogOpen(true),
|
|
27624
|
+
title: "Click to search for a symbol",
|
|
27625
|
+
children: [
|
|
27626
|
+
/* @__PURE__ */ jsx("span", { className: "oep-symbol-btn-text", children: symbol || (futures ? "e.g. ESH6" : "e.g. BTCUSDT") }),
|
|
27627
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 14 14", width: "11", height: "11", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", style: { flexShrink: 0, opacity: 0.5 }, children: [
|
|
27628
|
+
/* @__PURE__ */ jsx("circle", { cx: "6", cy: "6", r: "4" }),
|
|
27629
|
+
/* @__PURE__ */ jsx("line", { x1: "9.5", y1: "9.5", x2: "13", y2: "13" })
|
|
27630
|
+
] })
|
|
27631
|
+
]
|
|
27632
|
+
}
|
|
27633
|
+
) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
27634
|
+
/* @__PURE__ */ jsx(
|
|
27635
|
+
"input",
|
|
27636
|
+
{
|
|
27637
|
+
className: "oep-input",
|
|
27638
|
+
type: "text",
|
|
27639
|
+
value: symbol,
|
|
27640
|
+
onChange: (e) => {
|
|
27641
|
+
setSymbol(e.target.value.toUpperCase());
|
|
27642
|
+
setSymbolEdited(true);
|
|
27643
|
+
},
|
|
27644
|
+
onBlur: () => {
|
|
27645
|
+
if (!symbol) return;
|
|
27646
|
+
if (apiUrl) {
|
|
27647
|
+
fetch(`${apiUrl}/api/symbols/resolve?q=${encodeURIComponent(symbol)}`).then((r) => r.ok ? r.json() : Promise.reject(r.status)).then(({ canonical, ticker }) => {
|
|
27648
|
+
_pickedSymbolRef.current = canonical;
|
|
27649
|
+
setSymbol(ticker);
|
|
27650
|
+
setSymbolEdited(false);
|
|
27651
|
+
setNormalizedHint(null);
|
|
27652
|
+
onSymbolChange?.(canonical);
|
|
27653
|
+
}).catch(() => {
|
|
27654
|
+
});
|
|
27655
|
+
}
|
|
27656
|
+
},
|
|
27657
|
+
placeholder: futures ? "e.g. ES or ESH2026" : "e.g. BTCUSDT",
|
|
27658
|
+
autoCapitalize: "characters"
|
|
27659
|
+
}
|
|
27660
|
+
),
|
|
27661
|
+
normalizedHint && /* @__PURE__ */ jsxs("span", { style: { fontSize: 11, color: "var(--text-muted)", marginTop: 2, display: "block" }, children: [
|
|
27662
|
+
"\u2192 ",
|
|
27663
|
+
normalizedHint
|
|
27664
|
+
] })
|
|
27665
|
+
] })
|
|
27666
|
+
] }),
|
|
27667
|
+
/* @__PURE__ */ jsx("div", { className: "oep-row", style: { marginTop: 6 }, children: /* @__PURE__ */ jsxs("div", { className: "oep-side-toggle", children: [
|
|
27668
|
+
/* @__PURE__ */ jsx(
|
|
27669
|
+
"button",
|
|
27670
|
+
{
|
|
27671
|
+
type: "button",
|
|
27672
|
+
className: `oep-side-btn oep-side-btn--buy${side === "buy" ? " oep-side-btn--active" : ""}`,
|
|
27673
|
+
onClick: () => setSide("buy"),
|
|
27674
|
+
children: "Buy"
|
|
27675
|
+
}
|
|
27676
|
+
),
|
|
27677
|
+
/* @__PURE__ */ jsx(
|
|
27678
|
+
"button",
|
|
27679
|
+
{
|
|
27680
|
+
type: "button",
|
|
27681
|
+
className: `oep-side-btn oep-side-btn--sell${side === "sell" ? " oep-side-btn--active" : ""}`,
|
|
27682
|
+
onClick: () => setSide("sell"),
|
|
27683
|
+
children: "Sell"
|
|
27684
|
+
}
|
|
27685
|
+
)
|
|
27686
|
+
] }) }),
|
|
27687
|
+
showBracket && showBracketToggle && /* @__PURE__ */ jsxs("div", { className: "oep-bracket-row", children: [
|
|
27688
|
+
/* @__PURE__ */ jsx(
|
|
27689
|
+
"button",
|
|
27690
|
+
{
|
|
27691
|
+
type: "button",
|
|
27692
|
+
role: "switch",
|
|
27693
|
+
"aria-checked": bracketOn,
|
|
27694
|
+
className: `oep-toggle${bracketOn ? " oep-toggle--on" : ""}`,
|
|
27695
|
+
onClick: handleToggleBracket,
|
|
27696
|
+
children: /* @__PURE__ */ jsx("span", { className: "oep-toggle-thumb" })
|
|
27697
|
+
}
|
|
27698
|
+
),
|
|
27699
|
+
/* @__PURE__ */ jsx("span", { className: "oep-bracket-label", children: "Bracket (OCO)" }),
|
|
27700
|
+
bracketOn && /* @__PURE__ */ jsx(
|
|
27701
|
+
"button",
|
|
27702
|
+
{
|
|
27703
|
+
type: "button",
|
|
27704
|
+
className: "oep-gear-btn",
|
|
27705
|
+
title: "Edit bracket settings",
|
|
27706
|
+
onClick: handleOpenBracketSettings,
|
|
27707
|
+
children: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "12", height: "12", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [
|
|
27708
|
+
/* @__PURE__ */ jsx("circle", { cx: "8", cy: "8", r: "2.5" }),
|
|
27709
|
+
/* @__PURE__ */ jsx("path", { d: "M8 1.5v1M8 13.5v1M1.5 8h1M13.5 8h1M3.4 3.4l.7.7M11.9 11.9l.7.7M3.4 12.6l.7-.7M11.9 4.1l.7-.7", strokeLinecap: "round" })
|
|
27710
|
+
] })
|
|
27711
|
+
}
|
|
27712
|
+
)
|
|
27713
|
+
] }),
|
|
27714
|
+
/* @__PURE__ */ jsxs("div", { className: "oep-row", children: [
|
|
27715
|
+
/* @__PURE__ */ jsx("label", { className: "oep-label", children: "TYPE" }),
|
|
27716
|
+
/* @__PURE__ */ jsx(
|
|
27717
|
+
"select",
|
|
27718
|
+
{
|
|
27719
|
+
className: "oep-select",
|
|
27720
|
+
value: orderType,
|
|
27721
|
+
onChange: (e) => setOrderType(e.target.value),
|
|
27722
|
+
children: ORDER_TYPES.filter((o) => showTrailingStop || o.value !== "trailing_stop").map((o) => /* @__PURE__ */ jsx("option", { value: o.value, children: o.label }, o.value))
|
|
27723
|
+
}
|
|
27724
|
+
),
|
|
27725
|
+
ORDER_TYPE_HINTS[orderType] && /* @__PURE__ */ jsx("span", { className: "oep-hint", children: ORDER_TYPE_HINTS[orderType] })
|
|
27726
|
+
] }),
|
|
27727
|
+
/* @__PURE__ */ jsxs("div", { className: "oep-row", children: [
|
|
27728
|
+
/* @__PURE__ */ jsx("label", { className: "oep-label", children: "QUANTITY" }),
|
|
27729
|
+
/* @__PURE__ */ jsx(
|
|
27730
|
+
"input",
|
|
27731
|
+
{
|
|
27732
|
+
className: "oep-input",
|
|
27733
|
+
type: "number",
|
|
27734
|
+
min: 1,
|
|
27735
|
+
step: futures ? 1 : "any",
|
|
27736
|
+
value: qty,
|
|
27737
|
+
onChange: (e) => setQty(e.target.value)
|
|
27738
|
+
}
|
|
27739
|
+
)
|
|
27740
|
+
] }),
|
|
27741
|
+
showLimit && /* @__PURE__ */ jsxs("div", { className: "oep-row", children: [
|
|
27742
|
+
/* @__PURE__ */ jsx("label", { className: "oep-label", children: "Limit Price" }),
|
|
27743
|
+
/* @__PURE__ */ jsx(
|
|
27744
|
+
"input",
|
|
27745
|
+
{
|
|
27746
|
+
className: "oep-input",
|
|
27747
|
+
type: "number",
|
|
27748
|
+
step: "any",
|
|
27749
|
+
value: limitPrice,
|
|
27750
|
+
onChange: (e) => setLimitPrice(e.target.value),
|
|
27751
|
+
placeholder: "0.00"
|
|
27752
|
+
}
|
|
27753
|
+
)
|
|
27754
|
+
] }),
|
|
27755
|
+
showStop && /* @__PURE__ */ jsxs("div", { className: "oep-row", children: [
|
|
27756
|
+
/* @__PURE__ */ jsx("label", { className: "oep-label", children: "Stop Price" }),
|
|
27757
|
+
/* @__PURE__ */ jsx(
|
|
27758
|
+
"input",
|
|
27759
|
+
{
|
|
27760
|
+
className: "oep-input",
|
|
27761
|
+
type: "number",
|
|
27762
|
+
step: "any",
|
|
27763
|
+
value: stopPrice,
|
|
27764
|
+
onChange: (e) => setStopPrice(e.target.value),
|
|
27765
|
+
placeholder: "0.00"
|
|
27766
|
+
}
|
|
27767
|
+
)
|
|
27768
|
+
] }),
|
|
27769
|
+
showTrailing && /* @__PURE__ */ jsxs("div", { className: "oep-row", children: [
|
|
27770
|
+
/* @__PURE__ */ jsx("label", { className: "oep-label", children: "Trail Distance" }),
|
|
27771
|
+
/* @__PURE__ */ jsxs("div", { className: "oep-input-tick-wrap", children: [
|
|
27772
|
+
/* @__PURE__ */ jsx(
|
|
27773
|
+
"input",
|
|
27774
|
+
{
|
|
27775
|
+
className: "oep-input",
|
|
27776
|
+
type: "number",
|
|
27777
|
+
min: 1,
|
|
27778
|
+
step: 1,
|
|
27779
|
+
value: trailTicks,
|
|
27780
|
+
onChange: (e) => setTrailTicks(e.target.value),
|
|
27781
|
+
placeholder: "10"
|
|
27782
|
+
}
|
|
27783
|
+
),
|
|
27784
|
+
/* @__PURE__ */ jsx("span", { className: "oep-tick-unit", children: "ticks" })
|
|
27785
|
+
] }),
|
|
27786
|
+
/* @__PURE__ */ jsx(
|
|
27787
|
+
"input",
|
|
27788
|
+
{
|
|
27789
|
+
className: "oep-slider oep-slider--trail",
|
|
27790
|
+
type: "range",
|
|
27791
|
+
min: 1,
|
|
27792
|
+
max: 100,
|
|
27793
|
+
value: parseInt(trailTicks, 10) || 1,
|
|
27794
|
+
onChange: (e) => setTrailTicks(e.target.value)
|
|
27795
|
+
}
|
|
27796
|
+
)
|
|
27797
|
+
] }),
|
|
27798
|
+
orderType !== "trailing_stop" && /* @__PURE__ */ jsxs("div", { className: "oep-row", children: [
|
|
27799
|
+
/* @__PURE__ */ jsx("label", { className: "oep-label", children: "TIF" }),
|
|
27800
|
+
/* @__PURE__ */ jsx(
|
|
27801
|
+
"select",
|
|
27802
|
+
{
|
|
27803
|
+
className: "oep-select",
|
|
27804
|
+
value: tif,
|
|
27805
|
+
onChange: (e) => setTif(e.target.value),
|
|
27806
|
+
children: TIF_OPTIONS.map((t) => /* @__PURE__ */ jsx("option", { value: t.value, children: t.label }, t.value))
|
|
27807
|
+
}
|
|
27808
|
+
)
|
|
27809
|
+
] }),
|
|
27810
|
+
errorMsg && submitState === "error" && /* @__PURE__ */ jsx("div", { className: "oep-error", children: errorMsg }),
|
|
27811
|
+
submitState === "success" && !brokerStatus && /* @__PURE__ */ jsxs("div", { className: "oep-success", children: [
|
|
27812
|
+
/* @__PURE__ */ jsx("svg", { viewBox: "0 0 16 16", width: "13", height: "13", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: /* @__PURE__ */ jsx("polyline", { points: "3,8 6,11 13,4" }) }),
|
|
27813
|
+
"Order submitted"
|
|
27814
|
+
] }),
|
|
27815
|
+
brokerStatus && brokerStatus.kind === "open" && /* @__PURE__ */ jsxs("div", { className: "oep-broker-status oep-broker-status--open", children: [
|
|
27816
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "12", height: "12", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", children: [
|
|
27817
|
+
/* @__PURE__ */ jsx("circle", { cx: "8", cy: "8", r: "6" }),
|
|
27818
|
+
/* @__PURE__ */ jsx("line", { x1: "8", y1: "5", x2: "8", y2: "8.5" }),
|
|
27819
|
+
/* @__PURE__ */ jsx("circle", { cx: "8", cy: "10.5", r: "0.8", fill: "currentColor" })
|
|
27820
|
+
] }),
|
|
27821
|
+
"Working"
|
|
27822
|
+
] }),
|
|
27823
|
+
brokerStatus && brokerStatus.kind === "partial" && /* @__PURE__ */ jsxs("div", { className: "oep-broker-status oep-broker-status--partial", children: [
|
|
27824
|
+
/* @__PURE__ */ jsx("svg", { viewBox: "0 0 16 16", width: "12", height: "12", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", children: /* @__PURE__ */ jsx("polyline", { points: "3,8 6,11 13,4" }) }),
|
|
27825
|
+
"Partial fill: ",
|
|
27826
|
+
brokerStatus.filledQty,
|
|
27827
|
+
" filled"
|
|
27828
|
+
] }),
|
|
27829
|
+
brokerStatus && brokerStatus.kind === "filled" && /* @__PURE__ */ jsxs("div", { className: "oep-broker-status oep-broker-status--filled", children: [
|
|
27830
|
+
/* @__PURE__ */ jsx("svg", { viewBox: "0 0 16 16", width: "12", height: "12", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: /* @__PURE__ */ jsx("polyline", { points: "3,8 6,11 13,4" }) }),
|
|
27831
|
+
"Filled ",
|
|
27832
|
+
brokerStatus.filledQty,
|
|
27833
|
+
" @ ",
|
|
27834
|
+
brokerStatus.avgPrice.toFixed(2)
|
|
27835
|
+
] }),
|
|
27836
|
+
brokerStatus && brokerStatus.kind === "rejected" && /* @__PURE__ */ jsxs("div", { className: "oep-broker-status oep-broker-status--rejected", children: [
|
|
27837
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "12", height: "12", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", children: [
|
|
27838
|
+
/* @__PURE__ */ jsx("circle", { cx: "8", cy: "8", r: "6" }),
|
|
27839
|
+
/* @__PURE__ */ jsx("line", { x1: "5.5", y1: "5.5", x2: "10.5", y2: "10.5" }),
|
|
27840
|
+
/* @__PURE__ */ jsx("line", { x1: "10.5", y1: "5.5", x2: "5.5", y2: "10.5" })
|
|
27841
|
+
] }),
|
|
27842
|
+
"Rejected: ",
|
|
27843
|
+
brokerStatus.reason
|
|
27844
|
+
] }),
|
|
27845
|
+
brokerStatus && brokerStatus.kind === "cancelled" && /* @__PURE__ */ jsxs("div", { className: "oep-broker-status oep-broker-status--cancelled", children: [
|
|
27846
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "12", height: "12", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", children: [
|
|
27847
|
+
/* @__PURE__ */ jsx("line", { x1: "3", y1: "3", x2: "13", y2: "13" }),
|
|
27848
|
+
/* @__PURE__ */ jsx("line", { x1: "13", y1: "3", x2: "3", y2: "13" })
|
|
27849
|
+
] }),
|
|
27850
|
+
"Cancelled"
|
|
27851
|
+
] }),
|
|
27852
|
+
/* @__PURE__ */ jsx("div", { className: "oep-actions", children: /* @__PURE__ */ jsx(
|
|
27853
|
+
"button",
|
|
27854
|
+
{
|
|
27855
|
+
type: "submit",
|
|
27856
|
+
disabled: submitState === "submitting",
|
|
27857
|
+
className: `oep-submit oep-submit--${side}`,
|
|
27858
|
+
children: submitState === "submitting" ? "Placing\u2026" : `${side === "buy" ? "Buy" : "Sell"} ${symbol || "\u2014"}`
|
|
27859
|
+
}
|
|
27860
|
+
) })
|
|
27861
|
+
] }),
|
|
27862
|
+
(onSavePreset || presets && presets.length > 0) && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
27863
|
+
/* @__PURE__ */ jsx("div", { className: "oep-section-sep" }),
|
|
27864
|
+
/* @__PURE__ */ jsx("div", { className: "oep-preset-row", children: /* @__PURE__ */ jsxs(
|
|
27865
|
+
"select",
|
|
27866
|
+
{
|
|
27867
|
+
className: "oep-select oep-preset-select",
|
|
27868
|
+
value: selectedPreset,
|
|
27869
|
+
onChange: (e) => handleLoadPreset(e.target.value),
|
|
27870
|
+
disabled: !presets || presets.length === 0,
|
|
27871
|
+
children: [
|
|
27872
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "Load Preset" }),
|
|
27873
|
+
presets?.map((p) => /* @__PURE__ */ jsx("option", { value: p.name, children: p.name }, p.name))
|
|
27874
|
+
]
|
|
27875
|
+
}
|
|
27876
|
+
) }),
|
|
27877
|
+
onSavePreset && (savingPreset ? /* @__PURE__ */ jsxs("div", { className: "oep-preset-save-row", children: [
|
|
27878
|
+
/* @__PURE__ */ jsx(
|
|
27879
|
+
"input",
|
|
27880
|
+
{
|
|
27881
|
+
className: "oep-input oep-preset-name-input",
|
|
27882
|
+
type: "text",
|
|
27883
|
+
placeholder: "Preset name\u2026",
|
|
27884
|
+
value: presetName,
|
|
27885
|
+
onChange: (e) => setPresetName(e.target.value),
|
|
27886
|
+
onKeyDown: (e) => {
|
|
27887
|
+
if (e.key === "Enter") handleSavePreset();
|
|
27888
|
+
if (e.key === "Escape") {
|
|
27889
|
+
setSavingPreset(false);
|
|
27890
|
+
setPresetName("");
|
|
27891
|
+
}
|
|
27892
|
+
},
|
|
27893
|
+
autoFocus: true
|
|
27894
|
+
}
|
|
27895
|
+
),
|
|
27896
|
+
/* @__PURE__ */ jsx("button", { className: "oep-preset-confirm-btn", onClick: handleSavePreset, disabled: !presetName.trim(), children: "Save" }),
|
|
27897
|
+
/* @__PURE__ */ jsx("button", { className: "oep-preset-cancel-btn", onClick: () => {
|
|
27898
|
+
setSavingPreset(false);
|
|
27899
|
+
setPresetName("");
|
|
27900
|
+
}, children: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 12 12", width: "10", height: "10", stroke: "currentColor", strokeWidth: "1.8", fill: "none", children: [
|
|
27901
|
+
/* @__PURE__ */ jsx("line", { x1: "2", y1: "2", x2: "10", y2: "10" }),
|
|
27902
|
+
/* @__PURE__ */ jsx("line", { x1: "10", y1: "2", x2: "2", y2: "10" })
|
|
27903
|
+
] }) })
|
|
27904
|
+
] }) : /* @__PURE__ */ jsxs("button", { className: "oep-preset-save-btn", onClick: () => setSavingPreset(true), children: [
|
|
27905
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "12", height: "12", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
27906
|
+
/* @__PURE__ */ jsx("rect", { x: "2", y: "2", width: "12", height: "12", rx: "1.5" }),
|
|
27907
|
+
/* @__PURE__ */ jsx("rect", { x: "5", y: "2", width: "6", height: "4", rx: "0.5" }),
|
|
27908
|
+
/* @__PURE__ */ jsx("rect", { x: "4", y: "9", width: "8", height: "5", rx: "0.5" })
|
|
27909
|
+
] }),
|
|
27910
|
+
"Save Current as Preset"
|
|
27911
|
+
] }))
|
|
27912
|
+
] }),
|
|
27913
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
27914
|
+
/* @__PURE__ */ jsx("div", { className: "oep-section-sep" }),
|
|
27915
|
+
/* @__PURE__ */ jsxs("button", { className: "oep-pos-btn oep-pos-btn--close", onClick: () => onClosePosition?.({ symbol: symbolForSubmit, accountId: selectedAccountId }), disabled: !onClosePosition, children: [
|
|
27916
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "12", height: "12", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", children: [
|
|
27917
|
+
/* @__PURE__ */ jsx("circle", { cx: "8", cy: "8", r: "6" }),
|
|
27918
|
+
/* @__PURE__ */ jsx("line", { x1: "5.5", y1: "5.5", x2: "10.5", y2: "10.5" }),
|
|
27919
|
+
/* @__PURE__ */ jsx("line", { x1: "10.5", y1: "5.5", x2: "5.5", y2: "10.5" })
|
|
27920
|
+
] }),
|
|
27921
|
+
"Close Position"
|
|
27922
|
+
] }),
|
|
27923
|
+
/* @__PURE__ */ jsxs("div", { className: "oep-pos-row", children: [
|
|
27924
|
+
/* @__PURE__ */ jsxs("button", { className: "oep-pos-btn oep-pos-btn--cancel", onClick: () => onCancelOrders?.({ symbol: symbolForSubmit, accountId: selectedAccountId }), disabled: !onCancelOrders, children: [
|
|
27925
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "12", height: "12", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", children: [
|
|
27926
|
+
/* @__PURE__ */ jsx("circle", { cx: "8", cy: "8", r: "6" }),
|
|
27927
|
+
/* @__PURE__ */ jsx("line", { x1: "5", y1: "8", x2: "11", y2: "8" })
|
|
27928
|
+
] }),
|
|
27929
|
+
"Cancel Orders"
|
|
27930
|
+
] }),
|
|
27931
|
+
/* @__PURE__ */ jsxs("button", { className: "oep-pos-btn oep-pos-btn--reverse", onClick: () => onReverse?.({ symbol: symbolForSubmit, accountId: selectedAccountId, ...bracketOn ? { bracketOn: true, tpTicks: parseInt(tpTicks, 10), slTicks: parseInt(slTicks, 10) } : {} }), disabled: !onReverse, children: [
|
|
27932
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "12", height: "12", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
27933
|
+
/* @__PURE__ */ jsx("polyline", { points: "2,5 6,1 6,4 10,4 10,7 6,7 6,10" }),
|
|
27934
|
+
/* @__PURE__ */ jsx("polyline", { points: "14,11 10,15 10,12 6,12 6,9 10,9 10,6" })
|
|
27935
|
+
] }),
|
|
27936
|
+
"Reverse"
|
|
27937
|
+
] })
|
|
27938
|
+
] }),
|
|
27939
|
+
/* @__PURE__ */ jsxs("button", { className: "oep-pos-btn oep-pos-btn--flatten", onClick: () => onFlattenAll?.({ accountId: selectedAccountId }), disabled: !onFlattenAll, children: [
|
|
27940
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "12", height: "12", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", children: [
|
|
27941
|
+
/* @__PURE__ */ jsx("line", { x1: "2", y1: "5", x2: "14", y2: "5" }),
|
|
27942
|
+
/* @__PURE__ */ jsx("line", { x1: "2", y1: "8", x2: "14", y2: "8" }),
|
|
27943
|
+
/* @__PURE__ */ jsx("line", { x1: "2", y1: "11", x2: "14", y2: "11" })
|
|
27944
|
+
] }),
|
|
27945
|
+
"Flatten All"
|
|
27946
|
+
] })
|
|
27947
|
+
] })
|
|
27948
|
+
] })
|
|
27949
|
+
] });
|
|
27950
|
+
}
|
|
27951
|
+
function OrderTicket({
|
|
27952
|
+
symbol,
|
|
27953
|
+
tradingBridge,
|
|
27954
|
+
onClose,
|
|
27955
|
+
accountLabel = "Default",
|
|
27956
|
+
symbolResolver,
|
|
27957
|
+
onSymbolChange
|
|
27958
|
+
}) {
|
|
27959
|
+
const capabilities = useChartCapabilities();
|
|
27960
|
+
const handlePlaceOrder = useCallback(async (params) => {
|
|
27961
|
+
const intent = {
|
|
27962
|
+
type: "place",
|
|
27963
|
+
correlationId: params.clientRef,
|
|
27964
|
+
symbol: params.symbol,
|
|
27965
|
+
side: params.side,
|
|
27966
|
+
orderType: params.type,
|
|
27967
|
+
qty: params.qty,
|
|
27968
|
+
...params.limitPrice !== void 0 ? { limitPrice: params.limitPrice } : {},
|
|
27969
|
+
...params.stopPrice !== void 0 ? { stopPrice: params.stopPrice } : {},
|
|
27970
|
+
timestamp: Date.now(),
|
|
27971
|
+
meta: {
|
|
27972
|
+
source: "ticket",
|
|
27973
|
+
timeInForce: params.timeInForce,
|
|
27974
|
+
...params.trailTicks !== void 0 ? { trailTicks: params.trailTicks } : {}
|
|
27975
|
+
}
|
|
27976
|
+
};
|
|
27977
|
+
await tradingBridge.onPlaceOrderIntent?.(intent);
|
|
27978
|
+
}, [tradingBridge]);
|
|
27979
|
+
const handlePlaceBracketOrder = useCallback(async (params) => {
|
|
27980
|
+
const intent = {
|
|
27981
|
+
type: "place",
|
|
27982
|
+
correlationId: params.clientRef,
|
|
27983
|
+
symbol: params.symbol,
|
|
27984
|
+
side: params.side,
|
|
27985
|
+
orderType: params.type,
|
|
27986
|
+
qty: params.qty,
|
|
27987
|
+
...params.limitPrice !== void 0 ? { limitPrice: params.limitPrice } : {},
|
|
27988
|
+
groupId: params.clientRef,
|
|
27989
|
+
timestamp: Date.now(),
|
|
27990
|
+
meta: {
|
|
27991
|
+
source: "ticket",
|
|
27992
|
+
timeInForce: params.timeInForce,
|
|
27993
|
+
bracket: true,
|
|
27994
|
+
takeProfitTicks: params.takeProfitTicks,
|
|
27995
|
+
stopLossTicks: params.stopLossTicks
|
|
27996
|
+
}
|
|
27997
|
+
};
|
|
27998
|
+
await tradingBridge.onPlaceOrderIntent?.(intent);
|
|
27999
|
+
}, [tradingBridge]);
|
|
28000
|
+
if (!capabilities.orderEntry) return null;
|
|
28001
|
+
return /* @__PURE__ */ jsx(
|
|
28002
|
+
OrderEntryPanel,
|
|
28003
|
+
{
|
|
28004
|
+
sessionId: "",
|
|
28005
|
+
brokerSlug: accountLabel,
|
|
28006
|
+
accounts: [{ id: "default", name: accountLabel }],
|
|
28007
|
+
selectedAccountId: "default",
|
|
28008
|
+
defaultSymbol: symbol,
|
|
28009
|
+
onChangeAccount: () => {
|
|
28010
|
+
},
|
|
28011
|
+
onClose,
|
|
28012
|
+
onPlaceOrder: handlePlaceOrder,
|
|
28013
|
+
onPlaceBracketOrder: handlePlaceBracketOrder,
|
|
28014
|
+
showTrailingStop: capabilities.trailingStops,
|
|
28015
|
+
showBracketToggle: capabilities.bracketOrders,
|
|
28016
|
+
...symbolResolver ? { symbolResolver } : {},
|
|
28017
|
+
...onSymbolChange ? { onSymbolChange } : {}
|
|
28018
|
+
}
|
|
28019
|
+
);
|
|
28020
|
+
}
|
|
26693
28021
|
function ChartWorkspace({
|
|
26694
28022
|
// TabBar
|
|
26695
28023
|
tabs,
|
|
@@ -26783,8 +28111,29 @@ function ChartWorkspace({
|
|
|
26783
28111
|
wsLoaded,
|
|
26784
28112
|
leftDrawers,
|
|
26785
28113
|
drawers,
|
|
26786
|
-
getAuthToken
|
|
28114
|
+
getAuthToken,
|
|
28115
|
+
tradingBridge,
|
|
28116
|
+
tradingOverlayStore
|
|
26787
28117
|
}) {
|
|
28118
|
+
const capabilities = useChartCapabilities();
|
|
28119
|
+
const autoTrading = tradingBridge !== void 0;
|
|
28120
|
+
const [autoOrderEntryOpen, setAutoOrderEntryOpen] = React7.useState(false);
|
|
28121
|
+
const [autoTradingPanelOpen, setAutoTradingPanelOpen] = React7.useState(false);
|
|
28122
|
+
const effOnToggleOrderEntry = onToggleOrderEntry ?? (autoTrading ? () => setAutoOrderEntryOpen((o) => !o) : void 0);
|
|
28123
|
+
const effOrderEntryOpen = onToggleOrderEntry ? orderEntryOpen ?? false : autoOrderEntryOpen;
|
|
28124
|
+
const autoJournal = autoTrading && !tradingPanel && capabilities.tradingPanel;
|
|
28125
|
+
const gate = (hostProp, licensed) => hostProp !== false && licensed;
|
|
28126
|
+
const effShowTradeButton = gate(showTradeButton, capabilities.orderEntry) && onToggleTradeDrawer !== void 0;
|
|
28127
|
+
const effShowOrderEntry = gate(showOrderEntry, capabilities.orderEntry) && effOnToggleOrderEntry !== void 0;
|
|
28128
|
+
const effShowAgent = gate(showAgent, capabilities.aiAgent) && onToggleAgent !== void 0;
|
|
28129
|
+
const effShowIndicators = gate(showIndicators, capabilities.indicators);
|
|
28130
|
+
const effShowForgeScript = gate(showForgeScript, capabilities.forgeScript);
|
|
28131
|
+
const effShowWatchlist = gate(showWatchlist, capabilities.watchlists);
|
|
28132
|
+
const effShowSavedLayouts = gate(showSavedLayouts, capabilities.savedLayouts);
|
|
28133
|
+
const effShowCustomTimeframes = gate(showCustomTimeframes, capabilities.customTimeframes);
|
|
28134
|
+
const effShowDataExport = gate(showDataExport, capabilities.dataExport);
|
|
28135
|
+
const effShowMultiChartLayout = gate(showMultiChartLayout, capabilities.multiChartLayout);
|
|
28136
|
+
const effShowMultipleTabs = gate(showMultipleTabs, capabilities.multipleWorkspaces);
|
|
26788
28137
|
const tabItems = tabs.map((t) => ({
|
|
26789
28138
|
id: t.id,
|
|
26790
28139
|
label: t.label,
|
|
@@ -26849,7 +28198,7 @@ function ChartWorkspace({
|
|
|
26849
28198
|
onAdd: onAddTab,
|
|
26850
28199
|
onClose: onCloseTab,
|
|
26851
28200
|
onRename: onRenameTab,
|
|
26852
|
-
|
|
28201
|
+
showAddTab: effShowMultipleTabs
|
|
26853
28202
|
}
|
|
26854
28203
|
),
|
|
26855
28204
|
/* @__PURE__ */ jsx(
|
|
@@ -26886,9 +28235,9 @@ function ChartWorkspace({
|
|
|
26886
28235
|
onDeleteLayout,
|
|
26887
28236
|
onOpenLayoutInNewTab,
|
|
26888
28237
|
symbolResolver,
|
|
26889
|
-
...rollRule !== void 0 ? { rollRule } : {},
|
|
26890
|
-
...onRollRuleChange !== void 0 ? { onRollRuleChange } : {},
|
|
26891
|
-
|
|
28238
|
+
...rollRule !== void 0 && capabilities.continuousContracts ? { rollRule } : {},
|
|
28239
|
+
...onRollRuleChange !== void 0 && capabilities.continuousContracts ? { onRollRuleChange } : {},
|
|
28240
|
+
showTradeButton: effShowTradeButton,
|
|
26892
28241
|
...tradeDrawerOpen !== void 0 ? { tradeDrawerOpen } : {},
|
|
26893
28242
|
...onToggleTradeDrawer !== void 0 ? { onToggleTradeDrawer } : {},
|
|
26894
28243
|
...extraTfGroups !== void 0 ? { extraTfGroups } : {},
|
|
@@ -26898,11 +28247,11 @@ function ChartWorkspace({
|
|
|
26898
28247
|
...onGridTemplateChange !== void 0 ? { onGridTemplateChange } : {},
|
|
26899
28248
|
...onGridSyncChange !== void 0 ? { onGridSyncChange } : {},
|
|
26900
28249
|
...onExitGridMode !== void 0 ? { onExitGridMode } : {},
|
|
26901
|
-
|
|
26902
|
-
|
|
26903
|
-
|
|
26904
|
-
|
|
26905
|
-
|
|
28250
|
+
showIndicators: effShowIndicators,
|
|
28251
|
+
showSavedLayouts: effShowSavedLayouts,
|
|
28252
|
+
showCustomTimeframes: effShowCustomTimeframes,
|
|
28253
|
+
showDataExport: effShowDataExport,
|
|
28254
|
+
showMultiChartLayout: effShowMultiChartLayout
|
|
26906
28255
|
}
|
|
26907
28256
|
),
|
|
26908
28257
|
/* @__PURE__ */ jsx("div", { style: { flex: 1, display: "flex", gap: 4, minHeight: 0, overflow: "hidden" }, children: /* @__PURE__ */ jsxs("div", { style: { position: "relative", flex: 1, display: "flex", gap: 4, minWidth: 0, minHeight: 0 }, children: [
|
|
@@ -26926,6 +28275,16 @@ function ChartWorkspace({
|
|
|
26926
28275
|
/* @__PURE__ */ jsx("div", { style: { color: "rgba(255,255,255,0.65)", fontSize: 13 }, children: "Please activate your license in the admin studio" })
|
|
26927
28276
|
] }),
|
|
26928
28277
|
/* @__PURE__ */ jsx("div", { style: { position: "relative", flex: 1, minWidth: 0, minHeight: 0 }, children: chartSlots }),
|
|
28278
|
+
autoTrading && !onToggleOrderEntry && effOrderEntryOpen && capabilities.orderEntry && tradingBridge && /* @__PURE__ */ jsx(
|
|
28279
|
+
OrderTicket,
|
|
28280
|
+
{
|
|
28281
|
+
symbol: activeSymbol,
|
|
28282
|
+
tradingBridge,
|
|
28283
|
+
onClose: () => setAutoOrderEntryOpen(false),
|
|
28284
|
+
symbolResolver,
|
|
28285
|
+
onSymbolChange
|
|
28286
|
+
}
|
|
28287
|
+
),
|
|
26929
28288
|
leftDrawers,
|
|
26930
28289
|
drawers,
|
|
26931
28290
|
/* @__PURE__ */ jsx(
|
|
@@ -26933,20 +28292,20 @@ function ChartWorkspace({
|
|
|
26933
28292
|
{
|
|
26934
28293
|
watchlistOpen,
|
|
26935
28294
|
onToggleWatchlist,
|
|
26936
|
-
orderEntryOpen:
|
|
26937
|
-
onToggleOrderEntry:
|
|
28295
|
+
orderEntryOpen: effOrderEntryOpen,
|
|
28296
|
+
onToggleOrderEntry: effOnToggleOrderEntry ?? (() => {
|
|
26938
28297
|
}),
|
|
26939
|
-
showOrderEntry,
|
|
26940
|
-
|
|
26941
|
-
|
|
28298
|
+
showOrderEntry: effShowOrderEntry,
|
|
28299
|
+
showWatchlist: effShowWatchlist,
|
|
28300
|
+
showAgent: effShowAgent,
|
|
26942
28301
|
...agentOpen !== void 0 ? { agentOpen } : {},
|
|
26943
28302
|
...onToggleAgent !== void 0 ? { onToggleAgent } : {},
|
|
26944
|
-
...domOpen !== void 0 ? { domOpen } : {},
|
|
26945
|
-
...onToggleDOM !== void 0 ? { onToggleDOM } : {}
|
|
28303
|
+
...domOpen !== void 0 && capabilities.depthOfMarket ? { domOpen } : {},
|
|
28304
|
+
...onToggleDOM !== void 0 && capabilities.depthOfMarket ? { onToggleDOM } : {}
|
|
26946
28305
|
}
|
|
26947
28306
|
)
|
|
26948
28307
|
] }) }),
|
|
26949
|
-
tradingPanel && onToggleTradingPanel && /* @__PURE__ */ jsx(
|
|
28308
|
+
tradingPanel && onToggleTradingPanel && capabilities.tradingPanel && /* @__PURE__ */ jsx(
|
|
26950
28309
|
BottomTradingPanel,
|
|
26951
28310
|
{
|
|
26952
28311
|
apiUrl: tradingPanel.apiUrl,
|
|
@@ -26961,6 +28320,15 @@ function ChartWorkspace({
|
|
|
26961
28320
|
...tradingPanel.onModifyOrder ? { onModifyOrder: tradingPanel.onModifyOrder } : {}
|
|
26962
28321
|
}
|
|
26963
28322
|
),
|
|
28323
|
+
autoJournal && /* @__PURE__ */ jsx(
|
|
28324
|
+
BottomTradingPanel,
|
|
28325
|
+
{
|
|
28326
|
+
...tradingOverlayStore ? { overlayStore: tradingOverlayStore } : {},
|
|
28327
|
+
overlaySymbol: activeSymbol,
|
|
28328
|
+
visible: autoTradingPanelOpen,
|
|
28329
|
+
onToggle: () => setAutoTradingPanelOpen((o) => !o)
|
|
28330
|
+
}
|
|
28331
|
+
),
|
|
26964
28332
|
/* @__PURE__ */ jsx(
|
|
26965
28333
|
BottomToolbar,
|
|
26966
28334
|
{
|
|
@@ -26969,11 +28337,12 @@ function ChartWorkspace({
|
|
|
26969
28337
|
onTimezoneChange,
|
|
26970
28338
|
session,
|
|
26971
28339
|
onSessionChange,
|
|
28340
|
+
showSessionSelector: capabilities.sessionHours,
|
|
26972
28341
|
onToggleScriptDrawer,
|
|
26973
28342
|
scriptDrawerOpen,
|
|
26974
|
-
|
|
28343
|
+
showScriptButton: effShowForgeScript,
|
|
26975
28344
|
...tradingPanelOpen !== void 0 ? { tradingPanelOpen } : {},
|
|
26976
|
-
...onToggleTradingPanel !== void 0 ? { onToggleTradingPanel } : {},
|
|
28345
|
+
...onToggleTradingPanel !== void 0 && capabilities.tradingPanel ? { onToggleTradingPanel } : autoJournal ? { tradingPanelOpen: autoTradingPanelOpen, onToggleTradingPanel: () => setAutoTradingPanelOpen((o) => !o) } : {},
|
|
26977
28346
|
...rithmicConnected !== void 0 ? { rithmicConnected } : {}
|
|
26978
28347
|
}
|
|
26979
28348
|
)
|
|
@@ -26981,55 +28350,6 @@ function ChartWorkspace({
|
|
|
26981
28350
|
}
|
|
26982
28351
|
);
|
|
26983
28352
|
}
|
|
26984
|
-
function applyRuntimeConfig(caps, config) {
|
|
26985
|
-
if (!config) return caps;
|
|
26986
|
-
const orderEntry = config.enableOrderEntry === false ? false : caps.orderEntry;
|
|
26987
|
-
const draggableOrders = config.enableDraggableOrders === false ? false : caps.draggableOrders;
|
|
26988
|
-
const bracketOrders = config.enableBracketOrders === false ? false : caps.bracketOrders;
|
|
26989
|
-
const managedTrading = config.enableManagedTrading === false ? false : caps.managedTrading;
|
|
26990
|
-
const trailingStops = config.enableTrailingStops === false ? false : caps.trailingStops;
|
|
26991
|
-
const indicators = config.enableIndicators === false ? false : caps.indicators;
|
|
26992
|
-
const drawingTools = config.enableDrawingTools === false ? false : caps.drawingTools;
|
|
26993
|
-
const multiPane = config.enableMultiPane === false ? false : caps.multiPane;
|
|
26994
|
-
const barReplay = config.enableBarReplay === false ? false : caps.barReplay;
|
|
26995
|
-
const forgeScript = config.enableForgeScript === false ? false : caps.forgeScript;
|
|
26996
|
-
const aiAgent = config.enableAiAgent === false ? false : caps.aiAgent;
|
|
26997
|
-
const multiChartLayout = config.enableMultiChartLayout === false ? false : caps.multiChartLayout;
|
|
26998
|
-
return {
|
|
26999
|
-
...caps,
|
|
27000
|
-
orderEntry,
|
|
27001
|
-
draggableOrders,
|
|
27002
|
-
bracketOrders,
|
|
27003
|
-
managedTrading,
|
|
27004
|
-
trailingStops,
|
|
27005
|
-
indicators,
|
|
27006
|
-
drawingTools,
|
|
27007
|
-
multiPane,
|
|
27008
|
-
barReplay,
|
|
27009
|
-
forgeScript,
|
|
27010
|
-
aiAgent,
|
|
27011
|
-
multiChartLayout,
|
|
27012
|
-
// Recalculate derived render flags from the updated feature flags.
|
|
27013
|
-
renderOrderEntry: orderEntry,
|
|
27014
|
-
renderBuySellButtons: orderEntry,
|
|
27015
|
-
renderOrderTicket: orderEntry,
|
|
27016
|
-
renderBracketControls: orderEntry && bracketOrders,
|
|
27017
|
-
renderOrderModificationControls: orderEntry && draggableOrders,
|
|
27018
|
-
renderManagedTradingControls: managedTrading,
|
|
27019
|
-
renderIndicators: indicators,
|
|
27020
|
-
renderDrawings: drawingTools
|
|
27021
|
-
};
|
|
27022
|
-
}
|
|
27023
|
-
function useChartCapabilities(config) {
|
|
27024
|
-
const [licCaps, setLicCaps] = useState(() => getCapabilities());
|
|
27025
|
-
useEffect(() => {
|
|
27026
|
-
const unsub = LicenseManager.getInstance().subscribe(() => {
|
|
27027
|
-
setLicCaps(getCapabilities());
|
|
27028
|
-
});
|
|
27029
|
-
return unsub;
|
|
27030
|
-
}, []);
|
|
27031
|
-
return useMemo(() => applyRuntimeConfig(licCaps, config), [licCaps, config]);
|
|
27032
|
-
}
|
|
27033
28353
|
|
|
27034
28354
|
// src/react/trading/TradingBridge.ts
|
|
27035
28355
|
function createTradingBridgeLogger(callbacks, label) {
|
|
@@ -29196,6 +30516,6 @@ var CommandDispatcher = class {
|
|
|
29196
30516
|
}
|
|
29197
30517
|
};
|
|
29198
30518
|
|
|
29199
|
-
export { AgentFAB, AgentProvider, AssistantPanel, ChartCanvas, ChartWorkspace, CommandDispatcher, VoiceInput, createTradingBridgeLogger, useAgent, useChartCapabilities };
|
|
30519
|
+
export { AgentFAB, AgentProvider, AssistantPanel, ChartCanvas, ChartWorkspace, CommandDispatcher, OrderTicket, VoiceInput, createTradingBridgeLogger, useAgent, useChartCapabilities };
|
|
29200
30520
|
//# sourceMappingURL=index.js.map
|
|
29201
30521
|
//# sourceMappingURL=index.js.map
|