@forgecharts/sdk 1.3.2 → 1.3.4

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.
Files changed (33) hide show
  1. package/dist/api/TChart.d.ts +11 -2
  2. package/dist/api/TChart.d.ts.map +1 -1
  3. package/dist/index.js +187 -39
  4. package/dist/index.js.map +1 -1
  5. package/dist/internal.js +187 -39
  6. package/dist/internal.js.map +1 -1
  7. package/dist/licensing/ChartRuntimeResolver.d.ts +22 -0
  8. package/dist/licensing/ChartRuntimeResolver.d.ts.map +1 -1
  9. package/dist/licensing/__tests__/chartFeatureParity.test.d.ts +2 -0
  10. package/dist/licensing/__tests__/chartFeatureParity.test.d.ts.map +1 -0
  11. package/dist/licensing/licenseTypes.d.ts +9 -1
  12. package/dist/licensing/licenseTypes.d.ts.map +1 -1
  13. package/dist/react/canvas/ChartCanvas.d.ts.map +1 -1
  14. package/dist/react/canvas/toolbars/LeftToolbar.d.ts +3 -1
  15. package/dist/react/canvas/toolbars/LeftToolbar.d.ts.map +1 -1
  16. package/dist/react/index.d.ts +2 -0
  17. package/dist/react/index.d.ts.map +1 -1
  18. package/dist/react/index.js +1437 -117
  19. package/dist/react/index.js.map +1 -1
  20. package/dist/react/internal.js +6615 -6304
  21. package/dist/react/internal.js.map +1 -1
  22. package/dist/react/shell/OrderEntryPanel.d.ts +5 -1
  23. package/dist/react/shell/OrderEntryPanel.d.ts.map +1 -1
  24. package/dist/react/trading/OrderTicket.d.ts +38 -0
  25. package/dist/react/trading/OrderTicket.d.ts.map +1 -0
  26. package/dist/react/workspace/BottomTradingPanel.d.ts +14 -3
  27. package/dist/react/workspace/BottomTradingPanel.d.ts.map +1 -1
  28. package/dist/react/workspace/ChartWorkspace.d.ts +24 -1
  29. package/dist/react/workspace/ChartWorkspace.d.ts.map +1 -1
  30. package/dist/react/workspace/IndicatorsDialog.d.ts.map +1 -1
  31. package/dist/react/workspace/toolbars/BottomToolbar.d.ts +3 -1
  32. package/dist/react/workspace/toolbars/BottomToolbar.d.ts.map +1 -1
  33. package/package.json +1 -1
@@ -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
- const finalConfig = _TChart.OVERLAY_ONLY.has(config.type) && config.overlay !== true ? { ...config, overlay: true } : config;
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__ */ jsx(
19106
- ToolFlyoutGroup,
19107
- {
19108
- items: FORECAST_ITEMS,
19109
- toolSet: FORECAST_TOOLS,
19110
- itemMap: FORECAST_ITEM_MAP,
19111
- defaultTool: "longPosition",
19112
- groupTitle: "Forecasting",
19113
- activeTool,
19114
- favorites,
19115
- onSelectTool,
19116
- onFavoritesChange: handleFavoritesChange
19117
- }
19118
- ),
19119
- /* @__PURE__ */ jsx(
19120
- ToolFlyoutGroup,
19121
- {
19122
- items: MEASURER_ITEMS,
19123
- toolSet: MEASURER_TOOLS,
19124
- itemMap: MEASURER_ITEM_MAP,
19125
- defaultTool: "priceRange",
19126
- groupTitle: "Measurers",
19127
- activeTool,
19128
- favorites,
19129
- onSelectTool,
19130
- onFavoritesChange: handleFavoritesChange
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?.(orderId, newPrice);
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?.(entryOrderId, kind, price);
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
- ...showMultipleTabs !== void 0 ? { showAddTab: showMultipleTabs } : {}
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
- ...showTradeButton !== void 0 ? { showTradeButton } : {},
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
- ...showIndicators !== void 0 ? { showIndicators } : {},
26902
- ...showSavedLayouts !== void 0 ? { showSavedLayouts } : {},
26903
- ...showCustomTimeframes !== void 0 ? { showCustomTimeframes } : {},
26904
- ...showDataExport !== void 0 ? { showDataExport } : {},
26905
- ...showMultiChartLayout !== void 0 ? { showMultiChartLayout } : {}
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: orderEntryOpen ?? false,
26937
- onToggleOrderEntry: onToggleOrderEntry ?? (() => {
28295
+ orderEntryOpen: effOrderEntryOpen,
28296
+ onToggleOrderEntry: effOnToggleOrderEntry ?? (() => {
26938
28297
  }),
26939
- showOrderEntry,
26940
- ...showWatchlist !== void 0 ? { showWatchlist } : {},
26941
- ...showAgent !== void 0 ? { showAgent } : {},
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
- ...showForgeScript !== void 0 ? { showScriptButton: showForgeScript } : {},
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