@forgecharts/sdk 1.1.31 → 1.1.32
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/react/canvas/ChartCanvas.d.ts +8 -0
- package/dist/react/canvas/ChartCanvas.d.ts.map +1 -1
- package/dist/react/canvas/toolbars/LeftToolbar.d.ts +8 -1
- package/dist/react/canvas/toolbars/LeftToolbar.d.ts.map +1 -1
- package/dist/react/index.js +134 -18
- package/dist/react/index.js.map +1 -1
- package/dist/react/internal.js +1200 -96
- package/dist/react/internal.js.map +1 -1
- package/dist/react/shell/ManagedAppShell.d.ts +8 -0
- package/dist/react/shell/ManagedAppShell.d.ts.map +1 -1
- package/dist/react/shell/OrderEntryPanel.d.ts +24 -0
- package/dist/react/shell/OrderEntryPanel.d.ts.map +1 -0
- package/dist/react/shell/TradeDrawer.d.ts +18 -1
- package/dist/react/shell/TradeDrawer.d.ts.map +1 -1
- package/dist/react/workspace/ChartWorkspace.d.ts +4 -1
- package/dist/react/workspace/ChartWorkspace.d.ts.map +1 -1
- package/dist/react/workspace/toolbars/RightToolbar.d.ts +4 -1
- package/dist/react/workspace/toolbars/RightToolbar.d.ts.map +1 -1
- package/dist/react/workspace/toolbars/TopToolbar.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/react/internal.js
CHANGED
|
@@ -9338,8 +9338,9 @@ function VisibilityTool({
|
|
|
9338
9338
|
)) })
|
|
9339
9339
|
] });
|
|
9340
9340
|
}
|
|
9341
|
-
function LeftToolbar({ activeTool, onSelectTool, drawingFavorites, onDrawingFavoritesChange, onVisibilityAction, visibilityActiveAction, onVisibilityDeactivate, onLinkClick, onDeleteClick }) {
|
|
9341
|
+
function LeftToolbar({ activeTool, onSelectTool, drawingFavorites, onDrawingFavoritesChange, onVisibilityAction, visibilityActiveAction, onVisibilityDeactivate, onLinkClick, onDeleteClick, onLogout, user }) {
|
|
9342
9342
|
const [favorites, setFavorites] = useState(drawingFavorites ?? []);
|
|
9343
|
+
const [profileOpen, setProfileOpen] = useState(false);
|
|
9343
9344
|
const handleFavoritesChange = (favs) => {
|
|
9344
9345
|
setFavorites(favs);
|
|
9345
9346
|
onDrawingFavoritesChange?.(favs);
|
|
@@ -9358,7 +9359,8 @@ function LeftToolbar({ activeTool, onSelectTool, drawingFavorites, onDrawingFavo
|
|
|
9358
9359
|
borderRadius: 8,
|
|
9359
9360
|
width: 40,
|
|
9360
9361
|
flexShrink: 0,
|
|
9361
|
-
userSelect: "none"
|
|
9362
|
+
userSelect: "none",
|
|
9363
|
+
height: "100%"
|
|
9362
9364
|
},
|
|
9363
9365
|
children: [
|
|
9364
9366
|
/* @__PURE__ */ jsx(PointerGroup, { activeTool, onSelectTool }),
|
|
@@ -9439,6 +9441,29 @@ function LeftToolbar({ activeTool, onSelectTool, drawingFavorites, onDrawingFavo
|
|
|
9439
9441
|
/* @__PURE__ */ jsx("line", { x1: "9", y1: "6.5", x2: "9", y2: "11.5" })
|
|
9440
9442
|
] })
|
|
9441
9443
|
}
|
|
9444
|
+
),
|
|
9445
|
+
/* @__PURE__ */ jsx("div", { style: { flex: 1 } }),
|
|
9446
|
+
(user || onLogout) && /* @__PURE__ */ jsx(
|
|
9447
|
+
"button",
|
|
9448
|
+
{
|
|
9449
|
+
className: "drawing-tool-btn profile-avatar-btn",
|
|
9450
|
+
title: "Profile",
|
|
9451
|
+
onClick: () => setProfileOpen(true),
|
|
9452
|
+
children: (user?.username?.[0] ?? user?.email?.[0] ?? "?").toUpperCase()
|
|
9453
|
+
}
|
|
9454
|
+
),
|
|
9455
|
+
onLogout && /* @__PURE__ */ jsx(
|
|
9456
|
+
"button",
|
|
9457
|
+
{
|
|
9458
|
+
className: "drawing-tool-btn",
|
|
9459
|
+
title: "Log out",
|
|
9460
|
+
onClick: onLogout,
|
|
9461
|
+
children: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "15", height: "15", stroke: "currentColor", fill: "none", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
9462
|
+
/* @__PURE__ */ jsx("path", { d: "M6 2H3a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h3" }),
|
|
9463
|
+
/* @__PURE__ */ jsx("polyline", { points: "11 11 14 8 11 5" }),
|
|
9464
|
+
/* @__PURE__ */ jsx("line", { x1: "14", y1: "8", x2: "6", y2: "8" })
|
|
9465
|
+
] })
|
|
9466
|
+
}
|
|
9442
9467
|
)
|
|
9443
9468
|
]
|
|
9444
9469
|
}
|
|
@@ -9450,6 +9475,49 @@ function LeftToolbar({ activeTool, onSelectTool, drawingFavorites, onDrawingFavo
|
|
|
9450
9475
|
activeTool,
|
|
9451
9476
|
onSelectTool
|
|
9452
9477
|
}
|
|
9478
|
+
),
|
|
9479
|
+
profileOpen && ReactDOM.createPortal(
|
|
9480
|
+
/* @__PURE__ */ jsx("div", { className: "profile-drawer-overlay", onClick: () => setProfileOpen(false), children: /* @__PURE__ */ jsxs("div", { className: "profile-drawer", onClick: (e) => e.stopPropagation(), children: [
|
|
9481
|
+
/* @__PURE__ */ jsxs("div", { className: "profile-drawer-header", children: [
|
|
9482
|
+
/* @__PURE__ */ jsx("span", { children: "Profile" }),
|
|
9483
|
+
/* @__PURE__ */ jsx(
|
|
9484
|
+
"button",
|
|
9485
|
+
{
|
|
9486
|
+
className: "profile-drawer-close-btn",
|
|
9487
|
+
onClick: () => setProfileOpen(false),
|
|
9488
|
+
title: "Close",
|
|
9489
|
+
children: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 14 14", width: "12", height: "12", stroke: "currentColor", fill: "none", strokeWidth: "2", strokeLinecap: "round", children: [
|
|
9490
|
+
/* @__PURE__ */ jsx("line", { x1: "2", y1: "2", x2: "12", y2: "12" }),
|
|
9491
|
+
/* @__PURE__ */ jsx("line", { x1: "12", y1: "2", x2: "2", y2: "12" })
|
|
9492
|
+
] })
|
|
9493
|
+
}
|
|
9494
|
+
)
|
|
9495
|
+
] }),
|
|
9496
|
+
/* @__PURE__ */ jsx("div", { className: "profile-drawer-avatar", children: (user?.username?.[0] ?? user?.email?.[0] ?? "?").toUpperCase() }),
|
|
9497
|
+
user?.username && /* @__PURE__ */ jsx("div", { className: "profile-drawer-name", children: user.username }),
|
|
9498
|
+
user?.email && /* @__PURE__ */ jsx("div", { className: "profile-drawer-email", children: user.email }),
|
|
9499
|
+
user?.role && /* @__PURE__ */ jsx("div", { className: "profile-drawer-role", children: /* @__PURE__ */ jsx("span", { className: "profile-drawer-role-badge", children: user.role }) }),
|
|
9500
|
+
/* @__PURE__ */ jsx("div", { style: { flex: 1 } }),
|
|
9501
|
+
onLogout && /* @__PURE__ */ jsx("div", { className: "profile-drawer-footer", children: /* @__PURE__ */ jsxs(
|
|
9502
|
+
"button",
|
|
9503
|
+
{
|
|
9504
|
+
className: "profile-drawer-logout-btn",
|
|
9505
|
+
onClick: () => {
|
|
9506
|
+
setProfileOpen(false);
|
|
9507
|
+
onLogout();
|
|
9508
|
+
},
|
|
9509
|
+
children: [
|
|
9510
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "14", height: "14", stroke: "currentColor", fill: "none", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
9511
|
+
/* @__PURE__ */ jsx("path", { d: "M6 2H3a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h3" }),
|
|
9512
|
+
/* @__PURE__ */ jsx("polyline", { points: "11 11 14 8 11 5" }),
|
|
9513
|
+
/* @__PURE__ */ jsx("line", { x1: "14", y1: "8", x2: "6", y2: "8" })
|
|
9514
|
+
] }),
|
|
9515
|
+
"Log out"
|
|
9516
|
+
]
|
|
9517
|
+
}
|
|
9518
|
+
) })
|
|
9519
|
+
] }) }),
|
|
9520
|
+
document.body
|
|
9453
9521
|
)
|
|
9454
9522
|
] });
|
|
9455
9523
|
}
|
|
@@ -9906,7 +9974,9 @@ var ChartCanvas = forwardRef(
|
|
|
9906
9974
|
priceFraction: priceFractionProp,
|
|
9907
9975
|
onPriceFractionChange,
|
|
9908
9976
|
getExchangeLogoUrl,
|
|
9909
|
-
tradingBridge
|
|
9977
|
+
tradingBridge,
|
|
9978
|
+
onLogout,
|
|
9979
|
+
user
|
|
9910
9980
|
}, ref) {
|
|
9911
9981
|
const outerRef = useRef(null);
|
|
9912
9982
|
const priceRef = useRef(null);
|
|
@@ -10289,7 +10359,9 @@ var ChartCanvas = forwardRef(
|
|
|
10289
10359
|
onVisibilityAction: handleVisibilityAction,
|
|
10290
10360
|
visibilityActiveAction,
|
|
10291
10361
|
onVisibilityDeactivate: handleVisibilityDeactivate,
|
|
10292
|
-
onDeleteClick: () => chartRef.current?.deleteSelectedDrawing()
|
|
10362
|
+
onDeleteClick: () => chartRef.current?.deleteSelectedDrawing(),
|
|
10363
|
+
...onLogout ? { onLogout } : {},
|
|
10364
|
+
...user ? { user } : {}
|
|
10293
10365
|
}
|
|
10294
10366
|
),
|
|
10295
10367
|
/* @__PURE__ */ jsxs(
|
|
@@ -10314,6 +10386,29 @@ var ChartCanvas = forwardRef(
|
|
|
10314
10386
|
crosshairXRef.current = null;
|
|
10315
10387
|
},
|
|
10316
10388
|
children: [
|
|
10389
|
+
/* @__PURE__ */ jsx(
|
|
10390
|
+
"div",
|
|
10391
|
+
{
|
|
10392
|
+
style: {
|
|
10393
|
+
position: "absolute",
|
|
10394
|
+
bottom: 36,
|
|
10395
|
+
left: 8,
|
|
10396
|
+
zIndex: 5,
|
|
10397
|
+
pointerEvents: "none",
|
|
10398
|
+
fontFamily: "system-ui, -apple-system, 'Segoe UI', sans-serif",
|
|
10399
|
+
fontSize: 16,
|
|
10400
|
+
fontWeight: 900,
|
|
10401
|
+
letterSpacing: "-0.03em",
|
|
10402
|
+
background: "linear-gradient(90deg, var(--text, #fff) 20%, rgba(128,128,128,0.7) 100%)",
|
|
10403
|
+
WebkitBackgroundClip: "text",
|
|
10404
|
+
WebkitTextFillColor: "transparent",
|
|
10405
|
+
backgroundClip: "text",
|
|
10406
|
+
opacity: 0.45,
|
|
10407
|
+
userSelect: "none"
|
|
10408
|
+
},
|
|
10409
|
+
children: "ForgeCharts"
|
|
10410
|
+
}
|
|
10411
|
+
),
|
|
10317
10412
|
/* @__PURE__ */ jsxs(
|
|
10318
10413
|
"div",
|
|
10319
10414
|
{
|
|
@@ -11622,8 +11717,6 @@ function TopToolbar({
|
|
|
11622
11717
|
flexShrink: 0
|
|
11623
11718
|
},
|
|
11624
11719
|
children: [
|
|
11625
|
-
/* @__PURE__ */ jsx("span", { style: { fontWeight: 700, letterSpacing: "-0.5px", color: "var(--accent)" }, children: "ForgeCharts" }),
|
|
11626
|
-
/* @__PURE__ */ jsx("div", { className: "toolbar-sep" }),
|
|
11627
11720
|
/* @__PURE__ */ jsxs(
|
|
11628
11721
|
"button",
|
|
11629
11722
|
{
|
|
@@ -11824,6 +11917,11 @@ function TopToolbar({
|
|
|
11824
11917
|
}
|
|
11825
11918
|
);
|
|
11826
11919
|
}
|
|
11920
|
+
var OrderEntryIcon = () => /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "16", height: "16", fill: "none", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
11921
|
+
/* @__PURE__ */ jsx("rect", { x: "2", y: "2", width: "12", height: "12", rx: "1.5" }),
|
|
11922
|
+
/* @__PURE__ */ jsx("line", { x1: "5", y1: "8", x2: "11", y2: "8" }),
|
|
11923
|
+
/* @__PURE__ */ jsx("line", { x1: "8", y1: "5", x2: "8", y2: "11" })
|
|
11924
|
+
] });
|
|
11827
11925
|
var WatchListIcon = () => /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "16", height: "16", stroke: "currentColor", fill: "none", strokeWidth: "1.5", children: [
|
|
11828
11926
|
/* @__PURE__ */ jsx("rect", { x: "2", y: "3", width: "12", height: "1.5", rx: "0.5", fill: "currentColor", stroke: "none" }),
|
|
11829
11927
|
/* @__PURE__ */ jsx("rect", { x: "2", y: "7", width: "9", height: "1.5", rx: "0.5", fill: "currentColor", stroke: "none" }),
|
|
@@ -11832,8 +11930,8 @@ var WatchListIcon = () => /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", wi
|
|
|
11832
11930
|
/* @__PURE__ */ jsx("line", { x1: "13", y1: "10.75", x2: "13", y2: "12.75", strokeWidth: "1.2" }),
|
|
11833
11931
|
/* @__PURE__ */ jsx("line", { x1: "12", y1: "11.75", x2: "14", y2: "11.75", strokeWidth: "1.2" })
|
|
11834
11932
|
] });
|
|
11835
|
-
function RightToolbar({ watchlistOpen, onToggleWatchlist }) {
|
|
11836
|
-
return /* @__PURE__ */
|
|
11933
|
+
function RightToolbar({ watchlistOpen, onToggleWatchlist, orderEntryOpen, onToggleOrderEntry, showOrderEntry }) {
|
|
11934
|
+
return /* @__PURE__ */ jsxs(
|
|
11837
11935
|
"div",
|
|
11838
11936
|
{
|
|
11839
11937
|
style: {
|
|
@@ -11848,15 +11946,26 @@ function RightToolbar({ watchlistOpen, onToggleWatchlist }) {
|
|
|
11848
11946
|
flexShrink: 0,
|
|
11849
11947
|
userSelect: "none"
|
|
11850
11948
|
},
|
|
11851
|
-
children:
|
|
11852
|
-
|
|
11853
|
-
|
|
11854
|
-
|
|
11855
|
-
|
|
11856
|
-
|
|
11857
|
-
|
|
11858
|
-
|
|
11859
|
-
|
|
11949
|
+
children: [
|
|
11950
|
+
showOrderEntry !== false && /* @__PURE__ */ jsx(
|
|
11951
|
+
"button",
|
|
11952
|
+
{
|
|
11953
|
+
title: "Order Entry",
|
|
11954
|
+
className: `drawing-tool-btn${orderEntryOpen ? " is-active" : ""}`,
|
|
11955
|
+
onClick: onToggleOrderEntry,
|
|
11956
|
+
children: /* @__PURE__ */ jsx(OrderEntryIcon, {})
|
|
11957
|
+
}
|
|
11958
|
+
),
|
|
11959
|
+
/* @__PURE__ */ jsx(
|
|
11960
|
+
"button",
|
|
11961
|
+
{
|
|
11962
|
+
title: "Watch List",
|
|
11963
|
+
className: `drawing-tool-btn${watchlistOpen ? " is-active" : ""}`,
|
|
11964
|
+
onClick: onToggleWatchlist,
|
|
11965
|
+
children: /* @__PURE__ */ jsx(WatchListIcon, {})
|
|
11966
|
+
}
|
|
11967
|
+
)
|
|
11968
|
+
]
|
|
11860
11969
|
}
|
|
11861
11970
|
);
|
|
11862
11971
|
}
|
|
@@ -12248,6 +12357,9 @@ function ChartWorkspace({
|
|
|
12248
12357
|
// RightToolbar
|
|
12249
12358
|
watchlistOpen,
|
|
12250
12359
|
onToggleWatchlist,
|
|
12360
|
+
orderEntryOpen,
|
|
12361
|
+
onToggleOrderEntry,
|
|
12362
|
+
showOrderEntry,
|
|
12251
12363
|
// BottomToolbar
|
|
12252
12364
|
activeSymbol,
|
|
12253
12365
|
timezone,
|
|
@@ -12366,7 +12478,11 @@ function ChartWorkspace({
|
|
|
12366
12478
|
RightToolbar,
|
|
12367
12479
|
{
|
|
12368
12480
|
watchlistOpen,
|
|
12369
|
-
onToggleWatchlist
|
|
12481
|
+
onToggleWatchlist,
|
|
12482
|
+
orderEntryOpen: orderEntryOpen ?? false,
|
|
12483
|
+
onToggleOrderEntry: onToggleOrderEntry ?? (() => {
|
|
12484
|
+
}),
|
|
12485
|
+
showOrderEntry
|
|
12370
12486
|
}
|
|
12371
12487
|
)
|
|
12372
12488
|
] }),
|
|
@@ -13467,45 +13583,930 @@ function WatchlistDrawer({ onClose, onSelectSymbol, symbolResolver }) {
|
|
|
13467
13583
|
);
|
|
13468
13584
|
}
|
|
13469
13585
|
var BROKERS = [
|
|
13470
|
-
{ id: "rithmic", name: "Rithmic", description: "Futures & Forex", status: "available" },
|
|
13471
|
-
{ id: "ibkr", name: "Interactive Brokers", description: "Stocks, Options & More", status: "coming-soon" },
|
|
13472
|
-
{ id: "tradovate", name: "Tradovate", description: "Futures & Options", status: "coming-soon" },
|
|
13473
13586
|
{ id: "alpaca", name: "Alpaca", description: "Stocks & Crypto", status: "coming-soon" },
|
|
13587
|
+
{ id: "ibkr", name: "Interactive Brokers", description: "Stocks, Options & More", status: "coming-soon" },
|
|
13588
|
+
{ id: "ninjabrokerage", name: "NinjaTrader Brokerage", description: "Futures & Forex", status: "coming-soon" },
|
|
13589
|
+
{ id: "rithmic", name: "Rithmic", description: "Futures & Forex", status: "available" },
|
|
13590
|
+
{ id: "tastytrade", name: "tastytrade", description: "Options & Futures", status: "coming-soon" },
|
|
13474
13591
|
{ id: "tradestation", name: "TradeStation", description: "Stocks, Futures & Options", status: "coming-soon" },
|
|
13475
13592
|
{ id: "tradier", name: "Tradier", description: "Stocks & Options", status: "coming-soon" },
|
|
13476
|
-
{ id: "
|
|
13477
|
-
|
|
13593
|
+
{ id: "tradovate", name: "Tradovate", description: "Futures & Options", status: "coming-soon" }
|
|
13594
|
+
];
|
|
13595
|
+
function CloseIcon() {
|
|
13596
|
+
return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 14 14", width: "12", height: "12", stroke: "currentColor", strokeWidth: "1.8", fill: "none", children: [
|
|
13597
|
+
/* @__PURE__ */ jsx("line", { x1: "2", y1: "2", x2: "12", y2: "12" }),
|
|
13598
|
+
/* @__PURE__ */ jsx("line", { x1: "12", y1: "2", x2: "2", y2: "12" })
|
|
13599
|
+
] });
|
|
13600
|
+
}
|
|
13601
|
+
function BackIcon() {
|
|
13602
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 14 14", width: "13", height: "13", stroke: "currentColor", strokeWidth: "1.8", fill: "none", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("polyline", { points: "9,2 4,7 9,12" }) });
|
|
13603
|
+
}
|
|
13604
|
+
var RITHMIC_PROP_FIRMS = [
|
|
13605
|
+
{ id: "apex", name: "Apex Trader Funding" },
|
|
13606
|
+
{ id: "bulenox", name: "Bulenox" },
|
|
13607
|
+
{ id: "direct", name: "Direct / Rithmic account" },
|
|
13608
|
+
{ id: "earn2trade", name: "Earn2Trade" },
|
|
13609
|
+
{ id: "fundedtrader", name: "The Funded Trader" },
|
|
13610
|
+
{ id: "ftmo", name: "FTMO" },
|
|
13611
|
+
{ id: "ninjatrader", name: "NinjaTrader Brokerage" },
|
|
13612
|
+
{ id: "takeprofittrader", name: "Take Profit Trader" },
|
|
13613
|
+
{ id: "topstep", name: "TopstepTrader" },
|
|
13614
|
+
{ id: "tradeday", name: "Tradeday" },
|
|
13615
|
+
{ id: "wedbush", name: "Wedbush Futures" }
|
|
13478
13616
|
];
|
|
13479
|
-
|
|
13480
|
-
|
|
13617
|
+
var RITHMIC_GATEWAY_OPTIONS = [
|
|
13618
|
+
// Test / UAT
|
|
13619
|
+
{ id: "test-01", name: "Rithmic Test 01", region: "N. America", group: "Test / UAT" },
|
|
13620
|
+
// Paper Trading
|
|
13621
|
+
{ id: "paper-01", name: "Rithmic Paper Trading 01", region: "N. America", group: "Paper Trading" },
|
|
13622
|
+
{ id: "paper-colo-75", name: "Rithmic Paper Trading Colo 75", region: "N. America (colo)", group: "Paper Trading" },
|
|
13623
|
+
// Production
|
|
13624
|
+
{ id: "prod-chicago-01", name: "Rithmic 01", region: "Chicago, US", group: "Production" },
|
|
13625
|
+
{ id: "prod-new-york-04", name: "Rithmic 04", region: "New York, US", group: "Production" },
|
|
13626
|
+
{ id: "prod-frankfurt-01", name: "Rithmic 01 Europe", region: "Frankfurt, DE", group: "Production" },
|
|
13627
|
+
{ id: "prod-sydney-01", name: "Rithmic 01 Asia", region: "Sydney, AU", group: "Production" },
|
|
13628
|
+
{ id: "prod-colo-75", name: "Rithmic 01 Colo 75", region: "Chicago, US (colo)", group: "Production" }
|
|
13629
|
+
];
|
|
13630
|
+
function validateCreds(f) {
|
|
13631
|
+
const errs = {};
|
|
13632
|
+
if (!f.propFirm) errs.propFirm = "Please select your broker / prop firm";
|
|
13633
|
+
if (!f.username.trim()) errs.username = "Username is required";
|
|
13634
|
+
if (!f.password.trim()) errs.password = "Password is required";
|
|
13635
|
+
return errs;
|
|
13636
|
+
}
|
|
13637
|
+
function RithmicConnectDrawer({ onClose, onConnected, getAuthToken }) {
|
|
13638
|
+
const [step, setStep] = useState("credentials");
|
|
13639
|
+
const [fields, setFields] = useState({
|
|
13640
|
+
propFirm: "",
|
|
13641
|
+
username: "",
|
|
13642
|
+
password: "",
|
|
13643
|
+
gatewayId: "test-01"
|
|
13644
|
+
});
|
|
13645
|
+
const [errors, setErrors] = useState({});
|
|
13646
|
+
const [loading, setLoading] = useState(false);
|
|
13647
|
+
const [accounts, setAccounts] = useState([]);
|
|
13648
|
+
const [apiError, setApiError] = useState("");
|
|
13649
|
+
function set(key, value) {
|
|
13650
|
+
setFields((prev) => ({ ...prev, [key]: value }));
|
|
13651
|
+
if (errors[key]) setErrors((prev) => ({ ...prev, [key]: void 0 }));
|
|
13652
|
+
setApiError("");
|
|
13653
|
+
}
|
|
13654
|
+
async function authHeaders() {
|
|
13655
|
+
const h = { "Content-Type": "application/json" };
|
|
13656
|
+
if (getAuthToken) {
|
|
13657
|
+
try {
|
|
13658
|
+
h["Authorization"] = `Bearer ${await getAuthToken()}`;
|
|
13659
|
+
} catch {
|
|
13660
|
+
}
|
|
13661
|
+
}
|
|
13662
|
+
return h;
|
|
13663
|
+
}
|
|
13664
|
+
async function handleConnect(e) {
|
|
13665
|
+
e.preventDefault();
|
|
13666
|
+
const errs = validateCreds(fields);
|
|
13667
|
+
if (Object.keys(errs).length > 0) {
|
|
13668
|
+
setErrors(errs);
|
|
13669
|
+
return;
|
|
13670
|
+
}
|
|
13671
|
+
setLoading(true);
|
|
13672
|
+
setApiError("");
|
|
13673
|
+
try {
|
|
13674
|
+
const res = await fetch("/api/broker/rithmic/verify", {
|
|
13675
|
+
method: "POST",
|
|
13676
|
+
headers: await authHeaders(),
|
|
13677
|
+
body: JSON.stringify({
|
|
13678
|
+
propFirm: fields.propFirm,
|
|
13679
|
+
username: fields.username,
|
|
13680
|
+
password: fields.password,
|
|
13681
|
+
gatewayId: fields.gatewayId
|
|
13682
|
+
})
|
|
13683
|
+
});
|
|
13684
|
+
const body = await res.json().catch(() => ({}));
|
|
13685
|
+
if (!res.ok) {
|
|
13686
|
+
setApiError(body.error ?? `Error ${res.status} \u2014 check your credentials and try again`);
|
|
13687
|
+
return;
|
|
13688
|
+
}
|
|
13689
|
+
setAccounts(body.accounts ?? []);
|
|
13690
|
+
setStep("accounts");
|
|
13691
|
+
} catch {
|
|
13692
|
+
setApiError("Network error \u2014 please try again");
|
|
13693
|
+
} finally {
|
|
13694
|
+
setLoading(false);
|
|
13695
|
+
}
|
|
13696
|
+
}
|
|
13697
|
+
async function handleSelectAccount(account) {
|
|
13698
|
+
setLoading(true);
|
|
13699
|
+
setApiError("");
|
|
13700
|
+
try {
|
|
13701
|
+
const res = await fetch("/api/broker/credentials", {
|
|
13702
|
+
method: "POST",
|
|
13703
|
+
headers: await authHeaders(),
|
|
13704
|
+
body: JSON.stringify({
|
|
13705
|
+
broker: "rithmic",
|
|
13706
|
+
propFirm: fields.propFirm,
|
|
13707
|
+
username: fields.username,
|
|
13708
|
+
password: fields.password,
|
|
13709
|
+
gatewayId: fields.gatewayId,
|
|
13710
|
+
accountId: account.id,
|
|
13711
|
+
accountName: account.name
|
|
13712
|
+
})
|
|
13713
|
+
});
|
|
13714
|
+
if (!res.ok) {
|
|
13715
|
+
const body2 = await res.json().catch(() => ({}));
|
|
13716
|
+
setApiError(body2.error ?? `Error ${res.status}`);
|
|
13717
|
+
return;
|
|
13718
|
+
}
|
|
13719
|
+
const body = await res.json().catch(() => ({}));
|
|
13720
|
+
onConnected(body.connectionId ?? "");
|
|
13721
|
+
setStep("done");
|
|
13722
|
+
} catch {
|
|
13723
|
+
setApiError("Network error \u2014 please try again");
|
|
13724
|
+
} finally {
|
|
13725
|
+
setLoading(false);
|
|
13726
|
+
}
|
|
13727
|
+
}
|
|
13728
|
+
return /* @__PURE__ */ jsxs("div", { className: "trade-drawer trade-drawer--connect", children: [
|
|
13481
13729
|
/* @__PURE__ */ jsxs("div", { className: "trade-drawer-header", children: [
|
|
13482
13730
|
/* @__PURE__ */ jsxs("span", { className: "trade-drawer-title", children: [
|
|
13483
|
-
/* @__PURE__ */
|
|
13731
|
+
/* @__PURE__ */ jsx("span", { className: "broker-logo broker-logo--rithmic", children: "R" }),
|
|
13732
|
+
"Rithmic \u2014 Connect"
|
|
13733
|
+
] }),
|
|
13734
|
+
/* @__PURE__ */ jsxs("button", { className: "trade-drawer-close", onClick: onClose, title: "Close drawer", children: [
|
|
13735
|
+
/* @__PURE__ */ jsx(BackIcon, {}),
|
|
13736
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: 11, marginLeft: 3 }, children: "Close drawer" })
|
|
13737
|
+
] })
|
|
13738
|
+
] }),
|
|
13739
|
+
/* @__PURE__ */ jsxs("div", { className: "trade-drawer-body trade-drawer-body--form", children: [
|
|
13740
|
+
step === "credentials" && /* @__PURE__ */ jsxs("form", { className: "broker-connect-form", onSubmit: handleConnect, noValidate: true, children: [
|
|
13741
|
+
/* @__PURE__ */ jsx("div", { className: "bcf-section-label", children: "Your broker / prop firm" }),
|
|
13742
|
+
/* @__PURE__ */ jsxs("div", { className: "bcf-row", children: [
|
|
13743
|
+
/* @__PURE__ */ jsxs("label", { className: "bcf-label", children: [
|
|
13744
|
+
"Firm ",
|
|
13745
|
+
/* @__PURE__ */ jsx("span", { className: "bcf-req", children: "*" })
|
|
13746
|
+
] }),
|
|
13747
|
+
/* @__PURE__ */ jsxs(
|
|
13748
|
+
"select",
|
|
13749
|
+
{
|
|
13750
|
+
className: `bcf-input bcf-select${errors.propFirm ? " bcf-input--error" : ""}`,
|
|
13751
|
+
value: fields.propFirm,
|
|
13752
|
+
onChange: (e) => set("propFirm", e.target.value),
|
|
13753
|
+
children: [
|
|
13754
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "\u2014 Select your firm \u2014" }),
|
|
13755
|
+
RITHMIC_PROP_FIRMS.map((f) => /* @__PURE__ */ jsx("option", { value: f.id, children: f.name }, f.id))
|
|
13756
|
+
]
|
|
13757
|
+
}
|
|
13758
|
+
),
|
|
13759
|
+
errors.propFirm && /* @__PURE__ */ jsx("span", { className: "bcf-error", children: errors.propFirm })
|
|
13760
|
+
] }),
|
|
13761
|
+
/* @__PURE__ */ jsx("div", { className: "bcf-section-label", style: { marginTop: 16 }, children: "Rithmic credentials" }),
|
|
13762
|
+
/* @__PURE__ */ jsxs("div", { className: "bcf-row", children: [
|
|
13763
|
+
/* @__PURE__ */ jsxs("label", { className: "bcf-label", children: [
|
|
13764
|
+
"Username ",
|
|
13765
|
+
/* @__PURE__ */ jsx("span", { className: "bcf-req", children: "*" })
|
|
13766
|
+
] }),
|
|
13767
|
+
/* @__PURE__ */ jsx(
|
|
13768
|
+
"input",
|
|
13769
|
+
{
|
|
13770
|
+
className: `bcf-input${errors.username ? " bcf-input--error" : ""}`,
|
|
13771
|
+
type: "text",
|
|
13772
|
+
value: fields.username,
|
|
13773
|
+
autoComplete: "username",
|
|
13774
|
+
onChange: (e) => set("username", e.target.value)
|
|
13775
|
+
}
|
|
13776
|
+
),
|
|
13777
|
+
errors.username && /* @__PURE__ */ jsx("span", { className: "bcf-error", children: errors.username })
|
|
13778
|
+
] }),
|
|
13779
|
+
/* @__PURE__ */ jsxs("div", { className: "bcf-row", children: [
|
|
13780
|
+
/* @__PURE__ */ jsxs("label", { className: "bcf-label", children: [
|
|
13781
|
+
"Password ",
|
|
13782
|
+
/* @__PURE__ */ jsx("span", { className: "bcf-req", children: "*" })
|
|
13783
|
+
] }),
|
|
13784
|
+
/* @__PURE__ */ jsx(
|
|
13785
|
+
"input",
|
|
13786
|
+
{
|
|
13787
|
+
className: `bcf-input${errors.password ? " bcf-input--error" : ""}`,
|
|
13788
|
+
type: "password",
|
|
13789
|
+
value: fields.password,
|
|
13790
|
+
autoComplete: "current-password",
|
|
13791
|
+
onChange: (e) => set("password", e.target.value)
|
|
13792
|
+
}
|
|
13793
|
+
),
|
|
13794
|
+
errors.password && /* @__PURE__ */ jsx("span", { className: "bcf-error", children: errors.password })
|
|
13795
|
+
] }),
|
|
13796
|
+
/* @__PURE__ */ jsx("div", { className: "bcf-section-label", style: { marginTop: 16 }, children: "Gateway / Point-of-Presence" }),
|
|
13797
|
+
/* @__PURE__ */ jsxs("div", { className: "bcf-row", children: [
|
|
13798
|
+
/* @__PURE__ */ jsx("label", { className: "bcf-label", children: "Gateway" }),
|
|
13799
|
+
/* @__PURE__ */ jsx(
|
|
13800
|
+
"select",
|
|
13801
|
+
{
|
|
13802
|
+
className: "bcf-input bcf-select",
|
|
13803
|
+
value: fields.gatewayId,
|
|
13804
|
+
onChange: (e) => set("gatewayId", e.target.value),
|
|
13805
|
+
children: ["Test / UAT", "Paper Trading", "Production"].map((group) => /* @__PURE__ */ jsx("optgroup", { label: group, children: RITHMIC_GATEWAY_OPTIONS.filter((g) => g.group === group).map((g) => /* @__PURE__ */ jsxs("option", { value: g.id, children: [
|
|
13806
|
+
g.name,
|
|
13807
|
+
" \u2014 ",
|
|
13808
|
+
g.region
|
|
13809
|
+
] }, g.id)) }, group))
|
|
13810
|
+
}
|
|
13811
|
+
)
|
|
13812
|
+
] }),
|
|
13813
|
+
apiError && /* @__PURE__ */ jsx("div", { className: "bcf-api-error", children: apiError }),
|
|
13814
|
+
/* @__PURE__ */ jsx("div", { className: "bcf-actions", children: /* @__PURE__ */ jsx("button", { type: "submit", className: "bcf-btn-save", disabled: loading, children: loading ? "Connecting\u2026" : "Connect & Fetch Accounts" }) })
|
|
13815
|
+
] }),
|
|
13816
|
+
step === "accounts" && /* @__PURE__ */ jsxs("div", { className: "broker-connect-form", children: [
|
|
13817
|
+
/* @__PURE__ */ jsx("div", { className: "bcf-section-label", children: "Select your trading account" }),
|
|
13818
|
+
accounts.length === 0 ? /* @__PURE__ */ jsx("p", { className: "bcf-empty", children: "No accounts found for these credentials." }) : /* @__PURE__ */ jsx("div", { className: "bcf-account-list", children: accounts.map((acct) => /* @__PURE__ */ jsxs("div", { className: "bcf-account-row", children: [
|
|
13819
|
+
/* @__PURE__ */ jsxs("div", { className: "bcf-account-info", children: [
|
|
13820
|
+
/* @__PURE__ */ jsx("span", { className: "bcf-account-name", children: acct.name }),
|
|
13821
|
+
acct.type && /* @__PURE__ */ jsx("span", { className: "bcf-account-type", children: acct.type })
|
|
13822
|
+
] }),
|
|
13823
|
+
/* @__PURE__ */ jsx(
|
|
13824
|
+
"button",
|
|
13825
|
+
{
|
|
13826
|
+
className: "bcf-btn-select",
|
|
13827
|
+
disabled: loading,
|
|
13828
|
+
onClick: () => handleSelectAccount(acct),
|
|
13829
|
+
children: loading ? "\u2026" : "Select"
|
|
13830
|
+
}
|
|
13831
|
+
)
|
|
13832
|
+
] }, acct.id)) }),
|
|
13833
|
+
apiError && /* @__PURE__ */ jsx("div", { className: "bcf-api-error", children: apiError }),
|
|
13834
|
+
/* @__PURE__ */ jsx("div", { className: "bcf-actions", children: /* @__PURE__ */ jsx(
|
|
13835
|
+
"button",
|
|
13836
|
+
{
|
|
13837
|
+
type: "button",
|
|
13838
|
+
className: "bcf-btn-back",
|
|
13839
|
+
onClick: () => {
|
|
13840
|
+
setStep("credentials");
|
|
13841
|
+
setApiError("");
|
|
13842
|
+
},
|
|
13843
|
+
children: "\u2190 Back"
|
|
13844
|
+
}
|
|
13845
|
+
) })
|
|
13846
|
+
] }),
|
|
13847
|
+
step === "done" && /* @__PURE__ */ jsxs("div", { className: "broker-connect-form", children: [
|
|
13848
|
+
/* @__PURE__ */ jsxs("div", { className: "bcf-success", children: [
|
|
13849
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 20 20", width: "28", height: "28", fill: "none", stroke: "#22c55e", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
13850
|
+
/* @__PURE__ */ jsx("circle", { cx: "10", cy: "10", r: "8" }),
|
|
13851
|
+
/* @__PURE__ */ jsx("polyline", { points: "6,10 9,13 14,7" })
|
|
13852
|
+
] }),
|
|
13853
|
+
/* @__PURE__ */ jsx("span", { children: "Rithmic account connected successfully." })
|
|
13854
|
+
] }),
|
|
13855
|
+
/* @__PURE__ */ jsx("div", { className: "bcf-actions", children: /* @__PURE__ */ jsx("button", { type: "button", className: "bcf-btn-save", onClick: onClose, children: "Done" }) })
|
|
13856
|
+
] })
|
|
13857
|
+
] })
|
|
13858
|
+
] });
|
|
13859
|
+
}
|
|
13860
|
+
function BrokerCard({ broker, savedConnection, justConnected, onConnect, onStartTrading }) {
|
|
13861
|
+
const isConnected = !!(savedConnection || justConnected);
|
|
13862
|
+
return /* @__PURE__ */ jsxs("div", { className: `broker-tile${broker.status === "available" ? " broker-tile--available" : ""}${isConnected ? " broker-tile--connected" : ""}`, children: [
|
|
13863
|
+
/* @__PURE__ */ jsx("div", { className: "broker-tile-name", children: broker.name }),
|
|
13864
|
+
/* @__PURE__ */ jsx("div", { className: "broker-tile-desc", children: broker.description }),
|
|
13865
|
+
savedConnection && /* @__PURE__ */ jsxs("div", { className: "broker-tile-saved", children: [
|
|
13866
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 14 14", width: "11", height: "11", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", style: { flexShrink: 0, opacity: 0.6 }, children: [
|
|
13867
|
+
/* @__PURE__ */ jsx("circle", { cx: "7", cy: "5", r: "3" }),
|
|
13868
|
+
/* @__PURE__ */ jsx("path", { d: "M1 13c0-3.31 2.69-6 6-6s6 2.69 6 6" })
|
|
13869
|
+
] }),
|
|
13870
|
+
/* @__PURE__ */ jsx("span", { className: "broker-tile-saved-account", children: savedConnection.accountName || savedConnection.accountId }),
|
|
13871
|
+
/* @__PURE__ */ jsx("span", { className: "broker-tile-saved-sep", children: "\xB7" }),
|
|
13872
|
+
/* @__PURE__ */ jsx("span", { className: "broker-tile-saved-user", children: savedConnection.username })
|
|
13873
|
+
] }),
|
|
13874
|
+
/* @__PURE__ */ jsx("span", { className: `broker-card-status broker-card-status--${isConnected ? "connected" : broker.status}`, children: isConnected ? "Connected" : broker.status === "available" ? "Available" : "Coming Soon" }),
|
|
13875
|
+
isConnected ? /* @__PURE__ */ jsx(
|
|
13876
|
+
"button",
|
|
13877
|
+
{
|
|
13878
|
+
className: "broker-tile-connect broker-tile-connect--connected",
|
|
13879
|
+
onClick: savedConnection ? () => onStartTrading(savedConnection.id) : void 0,
|
|
13880
|
+
style: { pointerEvents: savedConnection ? "auto" : "none", cursor: savedConnection ? "pointer" : "default" },
|
|
13881
|
+
title: savedConnection ? `Start trading with ${broker.name}` : `${broker.name} connected`,
|
|
13882
|
+
children: "Start Trading"
|
|
13883
|
+
}
|
|
13884
|
+
) : /* @__PURE__ */ jsx(
|
|
13885
|
+
"button",
|
|
13886
|
+
{
|
|
13887
|
+
className: "broker-tile-connect",
|
|
13888
|
+
disabled: broker.status !== "available",
|
|
13889
|
+
onClick: broker.status === "available" ? onConnect : void 0,
|
|
13890
|
+
title: broker.status === "available" ? `Connect ${broker.name}` : "Coming soon",
|
|
13891
|
+
children: broker.status === "available" ? "Connect" : "Coming Soon"
|
|
13892
|
+
}
|
|
13893
|
+
)
|
|
13894
|
+
] });
|
|
13895
|
+
}
|
|
13896
|
+
function TradeDrawer({ onClose, savedConnections = [], onBrokerConnected, getAuthToken }) {
|
|
13897
|
+
const [connectingBroker, setConnectingBroker] = useState(null);
|
|
13898
|
+
const [connectedBrokers, setConnectedBrokers] = useState(/* @__PURE__ */ new Set());
|
|
13899
|
+
function markConnected(brokerId, connectionId) {
|
|
13900
|
+
setConnectedBrokers((prev) => new Set(prev).add(brokerId));
|
|
13901
|
+
onBrokerConnected?.(brokerId, connectionId);
|
|
13902
|
+
}
|
|
13903
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
13904
|
+
/* @__PURE__ */ jsx("div", { className: "trade-drawer-backdrop", onClick: onClose }),
|
|
13905
|
+
/* @__PURE__ */ jsxs("div", { className: "trade-drawer", children: [
|
|
13906
|
+
/* @__PURE__ */ jsxs("div", { className: "trade-drawer-header", children: [
|
|
13907
|
+
/* @__PURE__ */ jsxs("span", { className: "trade-drawer-title", children: [
|
|
13908
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "14", height: "14", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [
|
|
13909
|
+
/* @__PURE__ */ jsx("rect", { x: "1.5", y: "3", width: "13", height: "10", rx: "1.5" }),
|
|
13910
|
+
/* @__PURE__ */ jsx("line", { x1: "5", y1: "8", x2: "11", y2: "8" }),
|
|
13911
|
+
/* @__PURE__ */ jsx("line", { x1: "8", y1: "5", x2: "8", y2: "11" })
|
|
13912
|
+
] }),
|
|
13913
|
+
"Trade \u2014 Connect a broker"
|
|
13914
|
+
] }),
|
|
13915
|
+
/* @__PURE__ */ jsx("button", { className: "trade-drawer-close", onClick: onClose, title: "Close", children: /* @__PURE__ */ jsx(CloseIcon, {}) })
|
|
13916
|
+
] }),
|
|
13917
|
+
/* @__PURE__ */ jsx("div", { className: "trade-drawer-body", children: /* @__PURE__ */ jsx("div", { className: "broker-grid", children: BROKERS.map((broker) => {
|
|
13918
|
+
const saved = savedConnections.find((c) => c.broker === broker.id);
|
|
13919
|
+
return /* @__PURE__ */ jsx(
|
|
13920
|
+
BrokerCard,
|
|
13921
|
+
{
|
|
13922
|
+
broker,
|
|
13923
|
+
savedConnection: saved,
|
|
13924
|
+
justConnected: connectedBrokers.has(broker.id),
|
|
13925
|
+
onConnect: () => setConnectingBroker(broker.id),
|
|
13926
|
+
onStartTrading: (connId) => {
|
|
13927
|
+
onBrokerConnected?.(broker.id, connId);
|
|
13928
|
+
onClose();
|
|
13929
|
+
}
|
|
13930
|
+
},
|
|
13931
|
+
broker.id
|
|
13932
|
+
);
|
|
13933
|
+
}) }) })
|
|
13934
|
+
] }),
|
|
13935
|
+
connectingBroker === "rithmic" && /* @__PURE__ */ jsx(
|
|
13936
|
+
RithmicConnectDrawer,
|
|
13937
|
+
{
|
|
13938
|
+
onClose: () => setConnectingBroker(null),
|
|
13939
|
+
onConnected: (connId) => markConnected("rithmic", connId),
|
|
13940
|
+
...getAuthToken ? { getAuthToken } : {}
|
|
13941
|
+
}
|
|
13942
|
+
)
|
|
13943
|
+
] });
|
|
13944
|
+
}
|
|
13945
|
+
var ORDER_TYPES = [
|
|
13946
|
+
{ value: "market", label: "Market" },
|
|
13947
|
+
{ value: "limit", label: "Limit" },
|
|
13948
|
+
{ value: "stop_market", label: "Stop Market" },
|
|
13949
|
+
{ value: "stop_limit", label: "Stop Limit" },
|
|
13950
|
+
{ value: "trailing_stop", label: "Trailing Stop" }
|
|
13951
|
+
];
|
|
13952
|
+
var ORDER_TYPE_HINTS = {
|
|
13953
|
+
market: "Executes immediately at best available price",
|
|
13954
|
+
limit: "Executes at your price or better",
|
|
13955
|
+
stop_market: "Market order triggered when stop price is hit",
|
|
13956
|
+
stop_limit: "Limit order triggered when stop price is hit",
|
|
13957
|
+
trailing_stop: "Stop that trails price by the specified number of ticks"
|
|
13958
|
+
};
|
|
13959
|
+
var TIF_OPTIONS = [
|
|
13960
|
+
{ value: "DAY", label: "DAY \u2014 Good for Day" },
|
|
13961
|
+
{ value: "GTC", label: "GTC \u2014 Good till Cancel" },
|
|
13962
|
+
{ value: "IOC", label: "IOC \u2014 Immediate or Cancel" },
|
|
13963
|
+
{ value: "FOK", label: "FOK \u2014 Fill or Kill" }
|
|
13964
|
+
];
|
|
13965
|
+
var TICK_SIZE_FALLBACK = {
|
|
13966
|
+
ES: 0.25,
|
|
13967
|
+
MES: 0.25,
|
|
13968
|
+
NQ: 0.25,
|
|
13969
|
+
MNQ: 0.25,
|
|
13970
|
+
YM: 1,
|
|
13971
|
+
MYM: 1,
|
|
13972
|
+
RTY: 0.1,
|
|
13973
|
+
M2K: 0.1,
|
|
13974
|
+
CL: 0.01,
|
|
13975
|
+
MCL: 0.01,
|
|
13976
|
+
GC: 0.1,
|
|
13977
|
+
MGC: 0.1,
|
|
13978
|
+
SI: 5e-3,
|
|
13979
|
+
ZB: 0.03125,
|
|
13980
|
+
ZN: 0.015625,
|
|
13981
|
+
ZF: 78125e-7,
|
|
13982
|
+
ZT: 390625e-8,
|
|
13983
|
+
ZC: 0.25,
|
|
13984
|
+
ZS: 0.25,
|
|
13985
|
+
ZW: 0.25,
|
|
13986
|
+
NG: 1e-3,
|
|
13987
|
+
HE: 25e-5,
|
|
13988
|
+
LE: 25e-5
|
|
13989
|
+
};
|
|
13990
|
+
function getTickSize(sym) {
|
|
13991
|
+
const base = sym.split(".")[0] ?? sym;
|
|
13992
|
+
const root = base.replace(/[FGHJKMNQUVXZ]\d{1,2}$/, "").toUpperCase();
|
|
13993
|
+
return TICK_SIZE_FALLBACK[root] ?? 0.25;
|
|
13994
|
+
}
|
|
13995
|
+
function stripPrefix(sym) {
|
|
13996
|
+
const idx = sym.indexOf(":");
|
|
13997
|
+
return idx >= 0 ? sym.slice(idx + 1) : sym;
|
|
13998
|
+
}
|
|
13999
|
+
function isFuturesSymbol(sym) {
|
|
14000
|
+
return sym.includes(".");
|
|
14001
|
+
}
|
|
14002
|
+
function OrderEntryPanel({
|
|
14003
|
+
sessionId,
|
|
14004
|
+
brokerSlug,
|
|
14005
|
+
accounts,
|
|
14006
|
+
selectedAccountId,
|
|
14007
|
+
defaultSymbol = "",
|
|
14008
|
+
apiUrl = "",
|
|
14009
|
+
getAuthToken,
|
|
14010
|
+
connecting = false,
|
|
14011
|
+
connectionError,
|
|
14012
|
+
onRetryConnect,
|
|
14013
|
+
onChangeAccount,
|
|
14014
|
+
onClose
|
|
14015
|
+
}) {
|
|
14016
|
+
const [side, setSide] = useState("buy");
|
|
14017
|
+
const [orderType, setOrderType] = useState("market");
|
|
14018
|
+
const [tif, setTif] = useState("DAY");
|
|
14019
|
+
const [symbol, setSymbol] = useState(stripPrefix(defaultSymbol));
|
|
14020
|
+
const [qty, setQty] = useState("1");
|
|
14021
|
+
const [limitPrice, setLimitPrice] = useState("");
|
|
14022
|
+
const [stopPrice, setStopPrice] = useState("");
|
|
14023
|
+
const [trailTicks, setTrailTicks] = useState("10");
|
|
14024
|
+
const [bracketOn, setBracketOn] = useState(false);
|
|
14025
|
+
const [tpTicks, setTpTicks] = useState("20");
|
|
14026
|
+
const [slTicks, setSlTicks] = useState("10");
|
|
14027
|
+
const [submitState, setSubmitState] = useState("idle");
|
|
14028
|
+
const [errorMsg, setErrorMsg] = useState("");
|
|
14029
|
+
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
14030
|
+
const [draftTp, setDraftTp] = useState("20");
|
|
14031
|
+
const [draftSl, setDraftSl] = useState("10");
|
|
14032
|
+
const prevDefaultRef = useRef(defaultSymbol);
|
|
14033
|
+
useEffect(() => {
|
|
14034
|
+
if (defaultSymbol !== prevDefaultRef.current) {
|
|
14035
|
+
prevDefaultRef.current = defaultSymbol;
|
|
14036
|
+
setSymbol(stripPrefix(defaultSymbol));
|
|
14037
|
+
}
|
|
14038
|
+
}, [defaultSymbol]);
|
|
14039
|
+
const futures = isFuturesSymbol(symbol) || isFuturesSymbol(defaultSymbol);
|
|
14040
|
+
const showLimit = orderType === "limit" || orderType === "stop_limit";
|
|
14041
|
+
const showStop = orderType === "stop_market" || orderType === "stop_limit";
|
|
14042
|
+
const showTrailing = orderType === "trailing_stop";
|
|
14043
|
+
const showBracket = orderType === "market" || orderType === "limit";
|
|
14044
|
+
const tickSize = getTickSize(symbol || defaultSymbol);
|
|
14045
|
+
const tpPoints = (parseInt(draftTp, 10) || 0) * tickSize;
|
|
14046
|
+
const slPoints = (parseInt(draftSl, 10) || 0) * tickSize;
|
|
14047
|
+
const handleToggleBracket = useCallback(() => {
|
|
14048
|
+
if (bracketOn) {
|
|
14049
|
+
setBracketOn(false);
|
|
14050
|
+
} else {
|
|
14051
|
+
setDraftTp(tpTicks);
|
|
14052
|
+
setDraftSl(slTicks);
|
|
14053
|
+
setDrawerOpen(true);
|
|
14054
|
+
}
|
|
14055
|
+
}, [bracketOn, tpTicks, slTicks]);
|
|
14056
|
+
const handleOpenBracketSettings = useCallback(() => {
|
|
14057
|
+
setDraftTp(tpTicks);
|
|
14058
|
+
setDraftSl(slTicks);
|
|
14059
|
+
setDrawerOpen(true);
|
|
14060
|
+
}, [tpTicks, slTicks]);
|
|
14061
|
+
const handleDrawerSave = useCallback(() => {
|
|
14062
|
+
setTpTicks(draftTp);
|
|
14063
|
+
setSlTicks(draftSl);
|
|
14064
|
+
setBracketOn(true);
|
|
14065
|
+
setDrawerOpen(false);
|
|
14066
|
+
}, [draftTp, draftSl]);
|
|
14067
|
+
const handleDrawerCancel = useCallback(() => {
|
|
14068
|
+
setDrawerOpen(false);
|
|
14069
|
+
}, []);
|
|
14070
|
+
const handleSubmit = useCallback(async (e) => {
|
|
14071
|
+
e.preventDefault();
|
|
14072
|
+
setErrorMsg("");
|
|
14073
|
+
const qtyNum = futures ? parseInt(qty, 10) : parseFloat(qty);
|
|
14074
|
+
if (!symbol.trim()) {
|
|
14075
|
+
setErrorMsg("Symbol is required");
|
|
14076
|
+
return;
|
|
14077
|
+
}
|
|
14078
|
+
if (!qtyNum || qtyNum <= 0) {
|
|
14079
|
+
setErrorMsg("Quantity must be positive");
|
|
14080
|
+
return;
|
|
14081
|
+
}
|
|
14082
|
+
if (showLimit && !limitPrice) {
|
|
14083
|
+
setErrorMsg("Limit price is required");
|
|
14084
|
+
return;
|
|
14085
|
+
}
|
|
14086
|
+
if (showStop && !stopPrice) {
|
|
14087
|
+
setErrorMsg("Stop price is required");
|
|
14088
|
+
return;
|
|
14089
|
+
}
|
|
14090
|
+
if (showTrailing && parseInt(trailTicks, 10) < 1) {
|
|
14091
|
+
setErrorMsg("Trail ticks must be at least 1");
|
|
14092
|
+
return;
|
|
14093
|
+
}
|
|
14094
|
+
if (bracketOn && showBracket) {
|
|
14095
|
+
if (parseInt(tpTicks, 10) < 1) {
|
|
14096
|
+
setErrorMsg("Take-profit ticks are required");
|
|
14097
|
+
return;
|
|
14098
|
+
}
|
|
14099
|
+
if (parseInt(slTicks, 10) < 1) {
|
|
14100
|
+
setErrorMsg("Stop-loss ticks are required");
|
|
14101
|
+
return;
|
|
14102
|
+
}
|
|
14103
|
+
}
|
|
14104
|
+
setSubmitState("submitting");
|
|
14105
|
+
try {
|
|
14106
|
+
const headers = { "Content-Type": "application/json" };
|
|
14107
|
+
if (getAuthToken) {
|
|
14108
|
+
const token = await getAuthToken();
|
|
14109
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
14110
|
+
}
|
|
14111
|
+
const clientRef = crypto.randomUUID();
|
|
14112
|
+
if (bracketOn && showBracket) {
|
|
14113
|
+
const body = {
|
|
14114
|
+
sessionId,
|
|
14115
|
+
clientRef,
|
|
14116
|
+
symbol,
|
|
14117
|
+
accountId: selectedAccountId,
|
|
14118
|
+
side,
|
|
14119
|
+
type: orderType,
|
|
14120
|
+
qty: qtyNum,
|
|
14121
|
+
timeInForce: tif,
|
|
14122
|
+
...showLimit ? { limitPrice: parseFloat(limitPrice) } : {},
|
|
14123
|
+
takeProfitTicks: parseInt(tpTicks, 10),
|
|
14124
|
+
stopLossTicks: parseInt(slTicks, 10)
|
|
14125
|
+
};
|
|
14126
|
+
const resp = await fetch(`${apiUrl}/api/broker/user-bracket`, {
|
|
14127
|
+
method: "POST",
|
|
14128
|
+
headers,
|
|
14129
|
+
body: JSON.stringify(body)
|
|
14130
|
+
});
|
|
14131
|
+
if (!resp.ok) {
|
|
14132
|
+
const err = await resp.json().catch(() => ({}));
|
|
14133
|
+
throw new Error(err.error ?? `Error ${resp.status}`);
|
|
14134
|
+
}
|
|
14135
|
+
} else {
|
|
14136
|
+
const body = {
|
|
14137
|
+
sessionId,
|
|
14138
|
+
clientRef,
|
|
14139
|
+
symbol,
|
|
14140
|
+
accountId: selectedAccountId,
|
|
14141
|
+
side,
|
|
14142
|
+
type: orderType,
|
|
14143
|
+
qty: qtyNum,
|
|
14144
|
+
timeInForce: tif,
|
|
14145
|
+
...showLimit ? { limitPrice: parseFloat(limitPrice) } : {},
|
|
14146
|
+
...showStop ? { stopPrice: parseFloat(stopPrice) } : {},
|
|
14147
|
+
...showTrailing ? { trailTicks: parseInt(trailTicks, 10) } : {}
|
|
14148
|
+
};
|
|
14149
|
+
const resp = await fetch(`${apiUrl}/api/broker/user-order`, {
|
|
14150
|
+
method: "POST",
|
|
14151
|
+
headers,
|
|
14152
|
+
body: JSON.stringify(body)
|
|
14153
|
+
});
|
|
14154
|
+
if (!resp.ok) {
|
|
14155
|
+
const err = await resp.json().catch(() => ({}));
|
|
14156
|
+
throw new Error(err.error ?? `Error ${resp.status}`);
|
|
14157
|
+
}
|
|
14158
|
+
}
|
|
14159
|
+
setSubmitState("success");
|
|
14160
|
+
setTimeout(() => {
|
|
14161
|
+
setSubmitState("idle");
|
|
14162
|
+
setQty("1");
|
|
14163
|
+
setLimitPrice("");
|
|
14164
|
+
setStopPrice("");
|
|
14165
|
+
setTrailTicks("10");
|
|
14166
|
+
}, 1400);
|
|
14167
|
+
} catch (err) {
|
|
14168
|
+
setSubmitState("error");
|
|
14169
|
+
setErrorMsg(err instanceof Error ? err.message : "Order failed");
|
|
14170
|
+
}
|
|
14171
|
+
}, [
|
|
14172
|
+
symbol,
|
|
14173
|
+
side,
|
|
14174
|
+
orderType,
|
|
14175
|
+
tif,
|
|
14176
|
+
qty,
|
|
14177
|
+
limitPrice,
|
|
14178
|
+
stopPrice,
|
|
14179
|
+
trailTicks,
|
|
14180
|
+
bracketOn,
|
|
14181
|
+
tpTicks,
|
|
14182
|
+
slTicks,
|
|
14183
|
+
showLimit,
|
|
14184
|
+
showStop,
|
|
14185
|
+
showTrailing,
|
|
14186
|
+
showBracket,
|
|
14187
|
+
futures,
|
|
14188
|
+
sessionId,
|
|
14189
|
+
selectedAccountId,
|
|
14190
|
+
apiUrl,
|
|
14191
|
+
getAuthToken
|
|
14192
|
+
]);
|
|
14193
|
+
return /* @__PURE__ */ jsxs("div", { className: "oep-panel", children: [
|
|
14194
|
+
/* @__PURE__ */ jsxs("div", { className: "oep-header", children: [
|
|
14195
|
+
/* @__PURE__ */ jsxs("span", { className: "oep-title", children: [
|
|
14196
|
+
/* @__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: [
|
|
13484
14197
|
/* @__PURE__ */ jsx("rect", { x: "1.5", y: "3", width: "13", height: "10", rx: "1.5" }),
|
|
13485
14198
|
/* @__PURE__ */ jsx("line", { x1: "5", y1: "8", x2: "11", y2: "8" }),
|
|
13486
14199
|
/* @__PURE__ */ jsx("line", { x1: "8", y1: "5", x2: "8", y2: "11" })
|
|
13487
14200
|
] }),
|
|
13488
|
-
"
|
|
14201
|
+
drawerOpen ? "Bracket Order Settings" : "Order Entry"
|
|
13489
14202
|
] }),
|
|
13490
|
-
/* @__PURE__ */
|
|
13491
|
-
/* @__PURE__ */ jsx("
|
|
13492
|
-
/* @__PURE__ */ jsx(
|
|
13493
|
-
|
|
14203
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
|
|
14204
|
+
!drawerOpen && /* @__PURE__ */ jsx("span", { className: "oep-broker-badge", children: brokerSlug || "\u2014" }),
|
|
14205
|
+
/* @__PURE__ */ jsx(
|
|
14206
|
+
"button",
|
|
14207
|
+
{
|
|
14208
|
+
className: "oep-close",
|
|
14209
|
+
onClick: drawerOpen ? handleDrawerCancel : onClose,
|
|
14210
|
+
title: drawerOpen ? "Back" : "Close order panel",
|
|
14211
|
+
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: [
|
|
14212
|
+
/* @__PURE__ */ jsx("line", { x1: "2", y1: "2", x2: "12", y2: "12" }),
|
|
14213
|
+
/* @__PURE__ */ jsx("line", { x1: "12", y1: "2", x2: "2", y2: "12" })
|
|
14214
|
+
] })
|
|
14215
|
+
}
|
|
14216
|
+
)
|
|
14217
|
+
] })
|
|
13494
14218
|
] }),
|
|
13495
|
-
|
|
13496
|
-
/* @__PURE__ */
|
|
13497
|
-
|
|
13498
|
-
|
|
13499
|
-
|
|
14219
|
+
connecting && /* @__PURE__ */ jsxs("div", { className: "oep-body oep-body--status", children: [
|
|
14220
|
+
/* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", style: { animation: "spin 0.9s linear infinite", flexShrink: 0 }, children: [
|
|
14221
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "9", fill: "none", stroke: "var(--border)", strokeWidth: "2.5" }),
|
|
14222
|
+
/* @__PURE__ */ jsx("path", { d: "M12 3 A9 9 0 0 1 21 12", fill: "none", stroke: "var(--primary)", strokeWidth: "2.5", strokeLinecap: "round" })
|
|
14223
|
+
] }),
|
|
14224
|
+
/* @__PURE__ */ jsx("span", { style: { color: "var(--text-muted)", fontSize: 13 }, children: "Connecting to broker\u2026" })
|
|
14225
|
+
] }),
|
|
14226
|
+
!connecting && connectionError && /* @__PURE__ */ jsxs("div", { className: "oep-body oep-body--status", children: [
|
|
14227
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 20 20", width: "22", height: "22", fill: "none", stroke: "#ef4444", strokeWidth: "1.8", strokeLinecap: "round", children: [
|
|
14228
|
+
/* @__PURE__ */ jsx("circle", { cx: "10", cy: "10", r: "8" }),
|
|
14229
|
+
/* @__PURE__ */ jsx("line", { x1: "10", y1: "6", x2: "10", y2: "11" }),
|
|
14230
|
+
/* @__PURE__ */ jsx("circle", { cx: "10", cy: "14", r: "0.8", fill: "#ef4444" })
|
|
14231
|
+
] }),
|
|
14232
|
+
/* @__PURE__ */ jsx("span", { style: { color: "var(--text-muted)", fontSize: 13, textAlign: "center" }, children: connectionError }),
|
|
14233
|
+
onRetryConnect && /* @__PURE__ */ jsx("button", { className: "oep-retry-btn", onClick: onRetryConnect, children: "Retry Connection" })
|
|
14234
|
+
] }),
|
|
14235
|
+
!connecting && !connectionError && drawerOpen && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
14236
|
+
/* @__PURE__ */ jsxs("div", { className: "oep-drawer-body", children: [
|
|
14237
|
+
/* @__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." }),
|
|
14238
|
+
/* @__PURE__ */ jsxs("p", { className: "oep-drawer-ticksize", children: [
|
|
14239
|
+
"Tick size for ",
|
|
14240
|
+
symbol || "\u2014",
|
|
14241
|
+
": ",
|
|
14242
|
+
tickSize.toString()
|
|
14243
|
+
] }),
|
|
14244
|
+
/* @__PURE__ */ jsxs("div", { className: "oep-drawer-field", children: [
|
|
14245
|
+
/* @__PURE__ */ jsx("label", { className: "oep-drawer-label oep-drawer-label--tp", children: "Take Profit (ticks)" }),
|
|
14246
|
+
/* @__PURE__ */ jsx(
|
|
14247
|
+
"input",
|
|
14248
|
+
{
|
|
14249
|
+
className: "oep-input",
|
|
14250
|
+
type: "number",
|
|
14251
|
+
min: 1,
|
|
14252
|
+
step: 1,
|
|
14253
|
+
value: draftTp,
|
|
14254
|
+
onChange: (e) => setDraftTp(e.target.value)
|
|
14255
|
+
}
|
|
14256
|
+
),
|
|
14257
|
+
/* @__PURE__ */ jsx(
|
|
14258
|
+
"input",
|
|
14259
|
+
{
|
|
14260
|
+
className: "oep-slider oep-slider--tp",
|
|
14261
|
+
type: "range",
|
|
14262
|
+
min: 1,
|
|
14263
|
+
max: 100,
|
|
14264
|
+
value: parseInt(draftTp, 10) || 1,
|
|
14265
|
+
onChange: (e) => setDraftTp(e.target.value)
|
|
14266
|
+
}
|
|
14267
|
+
),
|
|
14268
|
+
/* @__PURE__ */ jsxs("span", { className: "oep-tick-convert", children: [
|
|
14269
|
+
parseInt(draftTp, 10) || 0,
|
|
14270
|
+
" ticks = ",
|
|
14271
|
+
tpPoints.toFixed(tickSize < 0.01 ? 6 : tickSize < 0.1 ? 4 : 2),
|
|
14272
|
+
" points"
|
|
14273
|
+
] })
|
|
14274
|
+
] }),
|
|
14275
|
+
/* @__PURE__ */ jsxs("div", { className: "oep-drawer-field", children: [
|
|
14276
|
+
/* @__PURE__ */ jsx("label", { className: "oep-drawer-label oep-drawer-label--sl", children: "Stop Loss (ticks)" }),
|
|
14277
|
+
/* @__PURE__ */ jsx(
|
|
14278
|
+
"input",
|
|
14279
|
+
{
|
|
14280
|
+
className: "oep-input",
|
|
14281
|
+
type: "number",
|
|
14282
|
+
min: 1,
|
|
14283
|
+
step: 1,
|
|
14284
|
+
value: draftSl,
|
|
14285
|
+
onChange: (e) => setDraftSl(e.target.value)
|
|
14286
|
+
}
|
|
14287
|
+
),
|
|
14288
|
+
/* @__PURE__ */ jsx(
|
|
14289
|
+
"input",
|
|
14290
|
+
{
|
|
14291
|
+
className: "oep-slider oep-slider--sl",
|
|
14292
|
+
type: "range",
|
|
14293
|
+
min: 1,
|
|
14294
|
+
max: 100,
|
|
14295
|
+
value: parseInt(draftSl, 10) || 1,
|
|
14296
|
+
onChange: (e) => setDraftSl(e.target.value)
|
|
14297
|
+
}
|
|
14298
|
+
),
|
|
14299
|
+
/* @__PURE__ */ jsxs("span", { className: "oep-tick-convert", children: [
|
|
14300
|
+
parseInt(draftSl, 10) || 0,
|
|
14301
|
+
" ticks = ",
|
|
14302
|
+
slPoints.toFixed(tickSize < 0.01 ? 6 : tickSize < 0.1 ? 4 : 2),
|
|
14303
|
+
" points"
|
|
14304
|
+
] })
|
|
14305
|
+
] })
|
|
14306
|
+
] }),
|
|
14307
|
+
/* @__PURE__ */ jsxs("div", { className: "oep-drawer-footer", children: [
|
|
14308
|
+
/* @__PURE__ */ jsx("button", { className: "oep-drawer-btn oep-drawer-btn--cancel", onClick: handleDrawerCancel, children: "Cancel" }),
|
|
14309
|
+
/* @__PURE__ */ jsx(
|
|
14310
|
+
"button",
|
|
14311
|
+
{
|
|
14312
|
+
className: "oep-drawer-btn oep-drawer-btn--save",
|
|
14313
|
+
onClick: handleDrawerSave,
|
|
14314
|
+
disabled: !draftTp || parseInt(draftTp, 10) < 1 || !draftSl || parseInt(draftSl, 10) < 1,
|
|
14315
|
+
children: "Save"
|
|
14316
|
+
}
|
|
14317
|
+
)
|
|
14318
|
+
] })
|
|
14319
|
+
] }),
|
|
14320
|
+
!connecting && !connectionError && !drawerOpen && /* @__PURE__ */ jsx("div", { className: "oep-body", children: /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, noValidate: true, children: [
|
|
14321
|
+
accounts.length === 1 && /* @__PURE__ */ jsxs("div", { className: "oep-row", children: [
|
|
14322
|
+
/* @__PURE__ */ jsx("label", { className: "oep-label", children: "Account" }),
|
|
14323
|
+
/* @__PURE__ */ jsx("div", { className: "oep-account-chip", children: accounts[0].name })
|
|
14324
|
+
] }),
|
|
14325
|
+
accounts.length > 1 && /* @__PURE__ */ jsxs("div", { className: "oep-row", children: [
|
|
14326
|
+
/* @__PURE__ */ jsx("label", { className: "oep-label", children: "Account" }),
|
|
14327
|
+
/* @__PURE__ */ jsx(
|
|
14328
|
+
"select",
|
|
14329
|
+
{
|
|
14330
|
+
className: "oep-select",
|
|
14331
|
+
value: selectedAccountId,
|
|
14332
|
+
onChange: (e) => onChangeAccount(e.target.value),
|
|
14333
|
+
children: accounts.map((a) => /* @__PURE__ */ jsx("option", { value: a.id, children: a.name }, a.id))
|
|
14334
|
+
}
|
|
14335
|
+
)
|
|
14336
|
+
] }),
|
|
14337
|
+
/* @__PURE__ */ jsxs("div", { className: "oep-row", children: [
|
|
14338
|
+
/* @__PURE__ */ jsx("label", { className: "oep-label", children: futures ? "Contract" : "Symbol" }),
|
|
14339
|
+
/* @__PURE__ */ jsx(
|
|
14340
|
+
"input",
|
|
14341
|
+
{
|
|
14342
|
+
className: "oep-input",
|
|
14343
|
+
type: "text",
|
|
14344
|
+
value: symbol,
|
|
14345
|
+
onChange: (e) => setSymbol(e.target.value.toUpperCase()),
|
|
14346
|
+
placeholder: futures ? "e.g. ESH6" : "e.g. BTCUSDT",
|
|
14347
|
+
autoCapitalize: "characters"
|
|
14348
|
+
}
|
|
14349
|
+
),
|
|
14350
|
+
futures && /* @__PURE__ */ jsx("span", { className: "oep-hint", children: "Include month + year code (e.g. H6 = Mar 2026)" })
|
|
14351
|
+
] }),
|
|
14352
|
+
/* @__PURE__ */ jsx("div", { className: "oep-row", style: { marginTop: 6 }, children: /* @__PURE__ */ jsxs("div", { className: "oep-side-toggle", children: [
|
|
14353
|
+
/* @__PURE__ */ jsx(
|
|
14354
|
+
"button",
|
|
14355
|
+
{
|
|
14356
|
+
type: "button",
|
|
14357
|
+
className: `oep-side-btn oep-side-btn--buy${side === "buy" ? " oep-side-btn--active" : ""}`,
|
|
14358
|
+
onClick: () => setSide("buy"),
|
|
14359
|
+
children: "Buy"
|
|
14360
|
+
}
|
|
14361
|
+
),
|
|
14362
|
+
/* @__PURE__ */ jsx(
|
|
14363
|
+
"button",
|
|
14364
|
+
{
|
|
14365
|
+
type: "button",
|
|
14366
|
+
className: `oep-side-btn oep-side-btn--sell${side === "sell" ? " oep-side-btn--active" : ""}`,
|
|
14367
|
+
onClick: () => setSide("sell"),
|
|
14368
|
+
children: "Sell"
|
|
14369
|
+
}
|
|
14370
|
+
)
|
|
14371
|
+
] }) }),
|
|
14372
|
+
/* @__PURE__ */ jsxs("div", { className: "oep-row", children: [
|
|
14373
|
+
/* @__PURE__ */ jsx("label", { className: "oep-label", children: "TYPE" }),
|
|
14374
|
+
/* @__PURE__ */ jsx(
|
|
14375
|
+
"select",
|
|
14376
|
+
{
|
|
14377
|
+
className: "oep-select",
|
|
14378
|
+
value: orderType,
|
|
14379
|
+
onChange: (e) => setOrderType(e.target.value),
|
|
14380
|
+
children: ORDER_TYPES.map((o) => /* @__PURE__ */ jsx("option", { value: o.value, children: o.label }, o.value))
|
|
14381
|
+
}
|
|
14382
|
+
),
|
|
14383
|
+
ORDER_TYPE_HINTS[orderType] && /* @__PURE__ */ jsx("span", { className: "oep-hint", children: ORDER_TYPE_HINTS[orderType] })
|
|
14384
|
+
] }),
|
|
14385
|
+
/* @__PURE__ */ jsxs("div", { className: "oep-row", children: [
|
|
14386
|
+
/* @__PURE__ */ jsx("label", { className: "oep-label", children: "QUANTITY" }),
|
|
14387
|
+
/* @__PURE__ */ jsx(
|
|
14388
|
+
"input",
|
|
14389
|
+
{
|
|
14390
|
+
className: "oep-input",
|
|
14391
|
+
type: "number",
|
|
14392
|
+
min: 1,
|
|
14393
|
+
step: futures ? 1 : "any",
|
|
14394
|
+
value: qty,
|
|
14395
|
+
onChange: (e) => setQty(e.target.value)
|
|
14396
|
+
}
|
|
14397
|
+
)
|
|
14398
|
+
] }),
|
|
14399
|
+
showLimit && /* @__PURE__ */ jsxs("div", { className: "oep-row", children: [
|
|
14400
|
+
/* @__PURE__ */ jsx("label", { className: "oep-label", children: "Limit Price" }),
|
|
14401
|
+
/* @__PURE__ */ jsx(
|
|
14402
|
+
"input",
|
|
14403
|
+
{
|
|
14404
|
+
className: "oep-input",
|
|
14405
|
+
type: "number",
|
|
14406
|
+
step: "any",
|
|
14407
|
+
value: limitPrice,
|
|
14408
|
+
onChange: (e) => setLimitPrice(e.target.value),
|
|
14409
|
+
placeholder: "0.00"
|
|
14410
|
+
}
|
|
14411
|
+
)
|
|
14412
|
+
] }),
|
|
14413
|
+
showStop && /* @__PURE__ */ jsxs("div", { className: "oep-row", children: [
|
|
14414
|
+
/* @__PURE__ */ jsx("label", { className: "oep-label", children: "Stop Price" }),
|
|
14415
|
+
/* @__PURE__ */ jsx(
|
|
14416
|
+
"input",
|
|
14417
|
+
{
|
|
14418
|
+
className: "oep-input",
|
|
14419
|
+
type: "number",
|
|
14420
|
+
step: "any",
|
|
14421
|
+
value: stopPrice,
|
|
14422
|
+
onChange: (e) => setStopPrice(e.target.value),
|
|
14423
|
+
placeholder: "0.00"
|
|
14424
|
+
}
|
|
14425
|
+
)
|
|
14426
|
+
] }),
|
|
14427
|
+
showTrailing && /* @__PURE__ */ jsxs("div", { className: "oep-row", children: [
|
|
14428
|
+
/* @__PURE__ */ jsx("label", { className: "oep-label", children: "Trail Distance" }),
|
|
14429
|
+
/* @__PURE__ */ jsxs("div", { className: "oep-input-tick-wrap", children: [
|
|
14430
|
+
/* @__PURE__ */ jsx(
|
|
14431
|
+
"input",
|
|
14432
|
+
{
|
|
14433
|
+
className: "oep-input",
|
|
14434
|
+
type: "number",
|
|
14435
|
+
min: 1,
|
|
14436
|
+
step: 1,
|
|
14437
|
+
value: trailTicks,
|
|
14438
|
+
onChange: (e) => setTrailTicks(e.target.value),
|
|
14439
|
+
placeholder: "10"
|
|
14440
|
+
}
|
|
14441
|
+
),
|
|
14442
|
+
/* @__PURE__ */ jsx("span", { className: "oep-tick-unit", children: "ticks" })
|
|
14443
|
+
] }),
|
|
14444
|
+
/* @__PURE__ */ jsx(
|
|
14445
|
+
"input",
|
|
14446
|
+
{
|
|
14447
|
+
className: "oep-slider oep-slider--trail",
|
|
14448
|
+
type: "range",
|
|
14449
|
+
min: 1,
|
|
14450
|
+
max: 100,
|
|
14451
|
+
value: parseInt(trailTicks, 10) || 1,
|
|
14452
|
+
onChange: (e) => setTrailTicks(e.target.value)
|
|
14453
|
+
}
|
|
14454
|
+
)
|
|
14455
|
+
] }),
|
|
14456
|
+
orderType !== "trailing_stop" && /* @__PURE__ */ jsxs("div", { className: "oep-row", children: [
|
|
14457
|
+
/* @__PURE__ */ jsx("label", { className: "oep-label", children: "TIF" }),
|
|
14458
|
+
/* @__PURE__ */ jsx(
|
|
14459
|
+
"select",
|
|
14460
|
+
{
|
|
14461
|
+
className: "oep-select",
|
|
14462
|
+
value: tif,
|
|
14463
|
+
onChange: (e) => setTif(e.target.value),
|
|
14464
|
+
children: TIF_OPTIONS.map((t) => /* @__PURE__ */ jsx("option", { value: t.value, children: t.label }, t.value))
|
|
14465
|
+
}
|
|
14466
|
+
)
|
|
14467
|
+
] }),
|
|
14468
|
+
showBracket && /* @__PURE__ */ jsxs("div", { className: "oep-bracket-row", children: [
|
|
14469
|
+
/* @__PURE__ */ jsx(
|
|
14470
|
+
"button",
|
|
14471
|
+
{
|
|
14472
|
+
type: "button",
|
|
14473
|
+
role: "switch",
|
|
14474
|
+
"aria-checked": bracketOn,
|
|
14475
|
+
className: `oep-toggle${bracketOn ? " oep-toggle--on" : ""}`,
|
|
14476
|
+
onClick: handleToggleBracket,
|
|
14477
|
+
children: /* @__PURE__ */ jsx("span", { className: "oep-toggle-thumb" })
|
|
14478
|
+
}
|
|
14479
|
+
),
|
|
14480
|
+
/* @__PURE__ */ jsx("span", { className: "oep-bracket-label", children: "Bracket (OCO)" }),
|
|
14481
|
+
bracketOn && /* @__PURE__ */ jsx(
|
|
14482
|
+
"button",
|
|
14483
|
+
{
|
|
14484
|
+
type: "button",
|
|
14485
|
+
className: "oep-gear-btn",
|
|
14486
|
+
title: "Edit bracket settings",
|
|
14487
|
+
onClick: handleOpenBracketSettings,
|
|
14488
|
+
children: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "12", height: "12", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [
|
|
14489
|
+
/* @__PURE__ */ jsx("circle", { cx: "8", cy: "8", r: "2.5" }),
|
|
14490
|
+
/* @__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" })
|
|
14491
|
+
] })
|
|
14492
|
+
}
|
|
14493
|
+
)
|
|
14494
|
+
] }),
|
|
14495
|
+
errorMsg && submitState === "error" && /* @__PURE__ */ jsx("div", { className: "oep-error", children: errorMsg }),
|
|
14496
|
+
submitState === "success" && /* @__PURE__ */ jsxs("div", { className: "oep-success", children: [
|
|
14497
|
+
/* @__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" }) }),
|
|
14498
|
+
"Order submitted"
|
|
14499
|
+
] }),
|
|
14500
|
+
/* @__PURE__ */ jsx("div", { className: "oep-actions", children: /* @__PURE__ */ jsx(
|
|
13500
14501
|
"button",
|
|
13501
14502
|
{
|
|
13502
|
-
|
|
13503
|
-
disabled:
|
|
13504
|
-
|
|
13505
|
-
children:
|
|
14503
|
+
type: "submit",
|
|
14504
|
+
disabled: submitState === "submitting",
|
|
14505
|
+
className: `oep-submit oep-submit--${side}`,
|
|
14506
|
+
children: submitState === "submitting" ? "Placing\u2026" : `${side === "buy" ? "Buy" : "Sell"} ${symbol || "\u2014"}`
|
|
13506
14507
|
}
|
|
13507
|
-
)
|
|
13508
|
-
] }
|
|
14508
|
+
) })
|
|
14509
|
+
] }) })
|
|
13509
14510
|
] });
|
|
13510
14511
|
}
|
|
13511
14512
|
function uid() {
|
|
@@ -13572,6 +14573,8 @@ var ManagedAppShell = forwardRef(
|
|
|
13572
14573
|
getAuthToken,
|
|
13573
14574
|
apiUrl,
|
|
13574
14575
|
tradingBridge,
|
|
14576
|
+
onLogout,
|
|
14577
|
+
user,
|
|
13575
14578
|
renderScriptDrawer,
|
|
13576
14579
|
renderWatchlistDrawer,
|
|
13577
14580
|
renderTradeDrawer
|
|
@@ -13591,9 +14594,82 @@ var ManagedAppShell = forwardRef(
|
|
|
13591
14594
|
const [scriptDrawerOpen, setScriptDrawerOpen] = useState(false);
|
|
13592
14595
|
const [watchlistOpen, setWatchlistOpen] = useState(false);
|
|
13593
14596
|
const [tradeDrawerOpen, setTradeDrawerOpen] = useState(false);
|
|
14597
|
+
const [orderPanelOpen, setOrderPanelOpen] = useState(false);
|
|
14598
|
+
const [orderPanelConnecting, setOrderPanelConnecting] = useState(false);
|
|
14599
|
+
const [orderPanelError, setOrderPanelError] = useState("");
|
|
14600
|
+
const [connectedSession, setConnectedSession] = useState(null);
|
|
14601
|
+
const [selectedAccountId, setSelectedAccountId] = useState("");
|
|
14602
|
+
const [savedConnections, setSavedConnections] = useState([]);
|
|
13594
14603
|
const [chartDirty, setChartDirty] = useState(0);
|
|
13595
14604
|
const [isFullscreen, setIsFullscreen] = useState(false);
|
|
13596
14605
|
const capabilities = useChartCapabilities(config);
|
|
14606
|
+
const handleBrokerConnected = useCallback(async (brokerId, connectionId) => {
|
|
14607
|
+
setTradeDrawerOpen(false);
|
|
14608
|
+
setConnectedSession({ brokerId, connectionId, sessionId: "", brokerSlug: brokerId, accounts: [] });
|
|
14609
|
+
setOrderPanelConnecting(true);
|
|
14610
|
+
setOrderPanelError("");
|
|
14611
|
+
setOrderPanelOpen(true);
|
|
14612
|
+
try {
|
|
14613
|
+
const headers = { "Content-Type": "application/json" };
|
|
14614
|
+
if (getAuthToken) {
|
|
14615
|
+
const tok = await getAuthToken();
|
|
14616
|
+
if (tok) headers["Authorization"] = `Bearer ${tok}`;
|
|
14617
|
+
}
|
|
14618
|
+
const resp = await fetch(`${apiUrl ?? ""}/api/broker/user-sessions`, {
|
|
14619
|
+
method: "POST",
|
|
14620
|
+
headers,
|
|
14621
|
+
body: JSON.stringify({ connectionId })
|
|
14622
|
+
});
|
|
14623
|
+
if (resp.ok) {
|
|
14624
|
+
const data = await resp.json();
|
|
14625
|
+
setConnectedSession({ brokerId, connectionId, ...data });
|
|
14626
|
+
setSelectedAccountId(data.accounts[0]?.id ?? "");
|
|
14627
|
+
setOrderPanelConnecting(false);
|
|
14628
|
+
} else {
|
|
14629
|
+
const errData = await resp.json().catch(() => ({}));
|
|
14630
|
+
setOrderPanelError(errData.error ?? "Failed to start broker session");
|
|
14631
|
+
setOrderPanelConnecting(false);
|
|
14632
|
+
}
|
|
14633
|
+
} catch {
|
|
14634
|
+
setOrderPanelError("Network error \u2014 could not connect to broker");
|
|
14635
|
+
setOrderPanelConnecting(false);
|
|
14636
|
+
}
|
|
14637
|
+
}, [apiUrl, getAuthToken]);
|
|
14638
|
+
const handleToggleOrderEntry = useCallback(() => {
|
|
14639
|
+
if (orderPanelOpen && !orderPanelConnecting) {
|
|
14640
|
+
setOrderPanelOpen(false);
|
|
14641
|
+
return;
|
|
14642
|
+
}
|
|
14643
|
+
if (connectedSession) {
|
|
14644
|
+
setOrderPanelOpen((o) => !o);
|
|
14645
|
+
return;
|
|
14646
|
+
}
|
|
14647
|
+
if (savedConnections.length > 0) {
|
|
14648
|
+
const first = savedConnections[0];
|
|
14649
|
+
void handleBrokerConnected(first.broker, first.id);
|
|
14650
|
+
return;
|
|
14651
|
+
}
|
|
14652
|
+
setTradeDrawerOpen(true);
|
|
14653
|
+
}, [orderPanelOpen, orderPanelConnecting, connectedSession, savedConnections, handleBrokerConnected]);
|
|
14654
|
+
useEffect(() => {
|
|
14655
|
+
if (!wsLoaded) return;
|
|
14656
|
+
const fetchConnections = async () => {
|
|
14657
|
+
try {
|
|
14658
|
+
const headers = {};
|
|
14659
|
+
if (getAuthToken) {
|
|
14660
|
+
const tok = await getAuthToken();
|
|
14661
|
+
if (tok) headers["Authorization"] = `Bearer ${tok}`;
|
|
14662
|
+
}
|
|
14663
|
+
const resp = await fetch(`${apiUrl ?? ""}/api/broker/user-connections`, { headers });
|
|
14664
|
+
if (resp.ok) {
|
|
14665
|
+
const data = await resp.json();
|
|
14666
|
+
setSavedConnections(data.connections ?? []);
|
|
14667
|
+
}
|
|
14668
|
+
} catch {
|
|
14669
|
+
}
|
|
14670
|
+
};
|
|
14671
|
+
void fetchConnections();
|
|
14672
|
+
}, [wsLoaded]);
|
|
13597
14673
|
useEffect(() => {
|
|
13598
14674
|
watchlistApi.configure({
|
|
13599
14675
|
apiBase: `${apiUrl ?? ""}/api`,
|
|
@@ -13911,6 +14987,8 @@ var ManagedAppShell = forwardRef(
|
|
|
13911
14987
|
...tab.snapshot ? { layoutToRestore: tab.snapshot } : {},
|
|
13912
14988
|
...getExchangeLogoUrl ? { getExchangeLogoUrl } : {},
|
|
13913
14989
|
...tradingBridge ? { tradingBridge } : {},
|
|
14990
|
+
...onLogout ? { onLogout } : {},
|
|
14991
|
+
...user ? { user } : {},
|
|
13914
14992
|
onIndicatorsChanged: () => setChartDirty((d) => d + 1)
|
|
13915
14993
|
},
|
|
13916
14994
|
`${tab.id}-${tab.remountCount}`
|
|
@@ -13920,64 +14998,90 @@ var ManagedAppShell = forwardRef(
|
|
|
13920
14998
|
)) });
|
|
13921
14999
|
const drawers = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
13922
15000
|
scriptDrawerOpen && (renderScriptDrawer ? renderScriptDrawer({ onClose: () => setScriptDrawerOpen(false), onAddIndicator: handleAddIndicator }) : /* @__PURE__ */ jsx(ScriptDrawer, { onClose: () => setScriptDrawerOpen(false), onAddIndicator: handleAddIndicator })),
|
|
13923
|
-
watchlistOpen && (renderWatchlistDrawer ? renderWatchlistDrawer({ onClose: () => setWatchlistOpen(false), onSelectSymbol: handleSymbolChange }) : /* @__PURE__ */ jsx(WatchlistDrawer, { onClose: () => setWatchlistOpen(false), onSelectSymbol: handleSymbolChange, symbolResolver }))
|
|
13924
|
-
|
|
13925
|
-
|
|
13926
|
-
tradeDrawerOpen && capabilities.renderManagedTradingControls && (renderTradeDrawer ? renderTradeDrawer({ onClose: () => setTradeDrawerOpen(false) }) : /* @__PURE__ */ jsx(TradeDrawer, { onClose: () => setTradeDrawerOpen(false) })),
|
|
13927
|
-
/* @__PURE__ */ jsx(
|
|
13928
|
-
ChartWorkspace,
|
|
15001
|
+
watchlistOpen && (renderWatchlistDrawer ? renderWatchlistDrawer({ onClose: () => setWatchlistOpen(false), onSelectSymbol: handleSymbolChange }) : /* @__PURE__ */ jsx(WatchlistDrawer, { onClose: () => setWatchlistOpen(false), onSelectSymbol: handleSymbolChange, symbolResolver })),
|
|
15002
|
+
tradeDrawerOpen && capabilities.renderManagedTradingControls && (renderTradeDrawer ? renderTradeDrawer({ onClose: () => setTradeDrawerOpen(false) }) : /* @__PURE__ */ jsx(
|
|
15003
|
+
TradeDrawer,
|
|
13929
15004
|
{
|
|
13930
|
-
|
|
13931
|
-
|
|
13932
|
-
|
|
13933
|
-
|
|
13934
|
-
|
|
13935
|
-
|
|
13936
|
-
|
|
13937
|
-
|
|
13938
|
-
|
|
13939
|
-
|
|
13940
|
-
|
|
13941
|
-
|
|
13942
|
-
|
|
13943
|
-
|
|
13944
|
-
|
|
13945
|
-
|
|
13946
|
-
|
|
13947
|
-
|
|
13948
|
-
|
|
13949
|
-
|
|
13950
|
-
|
|
13951
|
-
...activeTab.savedLayoutId ? { currentLayoutName: activeTab.label, currentLayoutId: activeTab.savedLayoutId } : {},
|
|
13952
|
-
autoSave,
|
|
13953
|
-
onFetchLayouts: () => layouts.list(),
|
|
13954
|
-
onSaveLayout: handleSaveLayout,
|
|
13955
|
-
onLoadLayout: handleLoadLayout,
|
|
13956
|
-
onRenameLayout: handleRenameLayout,
|
|
13957
|
-
onCopyLayout: handleCopyLayout,
|
|
13958
|
-
onToggleAutoSave: handleToggleAutoSave,
|
|
13959
|
-
onDeleteLayout: handleDeleteLayout,
|
|
13960
|
-
onOpenLayoutInNewTab: handleOpenLayoutInNewTab,
|
|
13961
|
-
symbolResolver,
|
|
13962
|
-
showTradeButton: capabilities.renderManagedTradingControls,
|
|
13963
|
-
tradeDrawerOpen,
|
|
13964
|
-
onToggleTradeDrawer: () => setTradeDrawerOpen((o) => !o),
|
|
13965
|
-
watchlistOpen,
|
|
13966
|
-
onToggleWatchlist: () => setWatchlistOpen((o) => !o),
|
|
13967
|
-
activeSymbol: activeTab.symbol,
|
|
13968
|
-
timezone,
|
|
13969
|
-
onTimezoneChange: setTimezone,
|
|
13970
|
-
session,
|
|
13971
|
-
onSessionChange: setSession,
|
|
13972
|
-
scriptDrawerOpen,
|
|
13973
|
-
onToggleScriptDrawer: () => setScriptDrawerOpen((o) => !o),
|
|
13974
|
-
isLicensed,
|
|
13975
|
-
wsLoaded,
|
|
13976
|
-
chartSlots,
|
|
13977
|
-
drawers
|
|
15005
|
+
onClose: () => setTradeDrawerOpen(false),
|
|
15006
|
+
savedConnections,
|
|
15007
|
+
onBrokerConnected: handleBrokerConnected,
|
|
15008
|
+
...getAuthToken ? { getAuthToken } : {}
|
|
15009
|
+
}
|
|
15010
|
+
)),
|
|
15011
|
+
orderPanelOpen && connectedSession && capabilities.renderManagedTradingControls && /* @__PURE__ */ jsx(
|
|
15012
|
+
OrderEntryPanel,
|
|
15013
|
+
{
|
|
15014
|
+
sessionId: connectedSession.sessionId,
|
|
15015
|
+
brokerSlug: connectedSession.brokerSlug,
|
|
15016
|
+
accounts: connectedSession.accounts,
|
|
15017
|
+
selectedAccountId,
|
|
15018
|
+
defaultSymbol: activeTab.symbol,
|
|
15019
|
+
apiUrl,
|
|
15020
|
+
getAuthToken,
|
|
15021
|
+
connecting: orderPanelConnecting,
|
|
15022
|
+
connectionError: orderPanelError || void 0,
|
|
15023
|
+
onRetryConnect: () => void handleBrokerConnected(connectedSession.brokerId, connectedSession.connectionId),
|
|
15024
|
+
onChangeAccount: setSelectedAccountId,
|
|
15025
|
+
onClose: () => setOrderPanelOpen(false)
|
|
13978
15026
|
}
|
|
13979
15027
|
)
|
|
13980
15028
|
] });
|
|
15029
|
+
return /* @__PURE__ */ jsx("div", { ref: containerRef, style: { height: "100%", position: "relative" }, children: /* @__PURE__ */ jsx(
|
|
15030
|
+
ChartWorkspace,
|
|
15031
|
+
{
|
|
15032
|
+
tabs: tabs.map((t) => ({ id: t.id, label: t.label, isSaved: !!t.savedLayoutId })),
|
|
15033
|
+
activeTabId,
|
|
15034
|
+
onSelectTab: selectTab,
|
|
15035
|
+
onAddTab: addTab,
|
|
15036
|
+
onCloseTab: closeTab,
|
|
15037
|
+
onRenameTab: renameTab,
|
|
15038
|
+
symbol: activeTab.symbol,
|
|
15039
|
+
timeframe: activeTab.timeframe,
|
|
15040
|
+
theme,
|
|
15041
|
+
customTimeframes,
|
|
15042
|
+
favoriteTfs,
|
|
15043
|
+
onSymbolChange: handleSymbolChange,
|
|
15044
|
+
onTimeframeChange: handleTimeframeChange,
|
|
15045
|
+
onAddCustomTimeframe: handleAddCustomTimeframe,
|
|
15046
|
+
onFavoriteTfsChange: setFavoriteTfs,
|
|
15047
|
+
onAddIndicator: handleAddIndicator,
|
|
15048
|
+
onToggleTheme: toggleTheme,
|
|
15049
|
+
onCopyScreenshot: handleCopyScreenshot,
|
|
15050
|
+
onDownloadScreenshot: handleDownloadScreenshot,
|
|
15051
|
+
onFullscreen: handleFullscreen,
|
|
15052
|
+
isFullscreen,
|
|
15053
|
+
...activeTab.savedLayoutId ? { currentLayoutName: activeTab.label, currentLayoutId: activeTab.savedLayoutId } : {},
|
|
15054
|
+
autoSave,
|
|
15055
|
+
onFetchLayouts: () => layouts.list(),
|
|
15056
|
+
onSaveLayout: handleSaveLayout,
|
|
15057
|
+
onLoadLayout: handleLoadLayout,
|
|
15058
|
+
onRenameLayout: handleRenameLayout,
|
|
15059
|
+
onCopyLayout: handleCopyLayout,
|
|
15060
|
+
onToggleAutoSave: handleToggleAutoSave,
|
|
15061
|
+
onDeleteLayout: handleDeleteLayout,
|
|
15062
|
+
onOpenLayoutInNewTab: handleOpenLayoutInNewTab,
|
|
15063
|
+
symbolResolver,
|
|
15064
|
+
showTradeButton: capabilities.renderManagedTradingControls,
|
|
15065
|
+
tradeDrawerOpen: tradeDrawerOpen || orderPanelOpen,
|
|
15066
|
+
onToggleTradeDrawer: () => setTradeDrawerOpen((o) => !o),
|
|
15067
|
+
watchlistOpen,
|
|
15068
|
+
onToggleWatchlist: () => setWatchlistOpen((o) => !o),
|
|
15069
|
+
orderEntryOpen: orderPanelOpen,
|
|
15070
|
+
onToggleOrderEntry: handleToggleOrderEntry,
|
|
15071
|
+
showOrderEntry: capabilities.renderManagedTradingControls,
|
|
15072
|
+
activeSymbol: activeTab.symbol,
|
|
15073
|
+
timezone,
|
|
15074
|
+
onTimezoneChange: setTimezone,
|
|
15075
|
+
session,
|
|
15076
|
+
onSessionChange: setSession,
|
|
15077
|
+
scriptDrawerOpen,
|
|
15078
|
+
onToggleScriptDrawer: () => setScriptDrawerOpen((o) => !o),
|
|
15079
|
+
isLicensed,
|
|
15080
|
+
wsLoaded,
|
|
15081
|
+
chartSlots,
|
|
15082
|
+
drawers
|
|
15083
|
+
}
|
|
15084
|
+
) });
|
|
13981
15085
|
}
|
|
13982
15086
|
);
|
|
13983
15087
|
function FloatingPanel({
|