@danielgroen/dxtrade-api 1.0.20 → 1.0.22

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/index.js CHANGED
@@ -34,6 +34,7 @@ __export(index_exports, {
34
34
  BROKER: () => BROKER,
35
35
  DxtradeClient: () => DxtradeClient,
36
36
  DxtradeError: () => DxtradeError,
37
+ ERROR: () => ERROR,
37
38
  ORDER_TYPE: () => ORDER_TYPE,
38
39
  SIDE: () => SIDE,
39
40
  TIF: () => TIF,
@@ -50,7 +51,10 @@ var BROKER = {
50
51
  };
51
52
 
52
53
  // src/constants/endpoints.ts
53
- var websocketQuery = `?X-Atmosphere-tracking-id=0&X-Atmosphere-Framework=2.3.2-javascript&X-Atmosphere-Transport=websocket&X-Atmosphere-TrackMessageSize=true&Content-Type=text/x-gwt-rpc;%20charset=UTF-8&X-atmo-protocol=true&sessionState=dx-new&guest-mode=false`;
54
+ function websocketQuery(atmosphereId) {
55
+ const trackingId = atmosphereId ?? "0";
56
+ return `?X-Atmosphere-tracking-id=${trackingId}&X-Atmosphere-Framework=2.3.2-javascript&X-Atmosphere-Transport=websocket&X-Atmosphere-TrackMessageSize=true&Content-Type=text/x-gwt-rpc;%20charset=UTF-8&X-atmo-protocol=true&sessionState=dx-new&guest-mode=false`;
57
+ }
54
58
  var endpoints = {
55
59
  login: (base) => `${base}/api/auth/login`,
56
60
  switchAccount: (base, id) => `${base}/api/accounts/switch?accountId=${id}`,
@@ -58,9 +62,13 @@ var endpoints = {
58
62
  instrumentInfo: (base, symbol, tzOffset) => `${base}/api/instruments/info?symbol=${symbol}&timezoneOffset=${tzOffset}&withExDividends=true`,
59
63
  submitOrder: (base) => `${base}/api/orders/single`,
60
64
  closePosition: (base) => `${base}/api/positions/close`,
65
+ cancelOrder: (base, accountId, orderChainId) => `${base}/api/orders/cancel?accountId=${accountId}&orderChainId=${orderChainId}`,
61
66
  assessments: (base) => `${base}/api/assessments`,
62
- websocket: (base) => `wss://${base.split("//")[1]}/client/connector` + websocketQuery,
63
- tradeJournal: (base, params) => `${base}/api/tradejournal?from=${params.from}&to=${params.to}`
67
+ websocket: (base, atmosphereId) => `wss://${base.split("//")[1]}/client/connector` + websocketQuery(atmosphereId),
68
+ tradeJournal: (base, params) => `${base}/api/tradejournal?from=${params.from}&to=${params.to}`,
69
+ tradeHistory: (base, params) => `${base}/api/history?from=${params.from}&to=${params.to}&orderId=`,
70
+ subscribeInstruments: (base) => `${base}/api/instruments/subscribeInstrumentSymbols`,
71
+ charts: (base) => `${base}/api/charts`
64
72
  };
65
73
 
66
74
  // src/constants/enums.ts
@@ -86,21 +94,63 @@ var TIF = /* @__PURE__ */ ((TIF2) => {
86
94
  TIF2["GTD"] = "GTD";
87
95
  return TIF2;
88
96
  })(TIF || {});
97
+ var ERROR = /* @__PURE__ */ ((ERROR2) => {
98
+ ERROR2["NO_SESSION"] = "NO_SESSION";
99
+ ERROR2["LOGIN_FAILED"] = "LOGIN_FAILED";
100
+ ERROR2["LOGIN_ERROR"] = "LOGIN_ERROR";
101
+ ERROR2["CSRF_NOT_FOUND"] = "CSRF_NOT_FOUND";
102
+ ERROR2["CSRF_ERROR"] = "CSRF_ERROR";
103
+ ERROR2["ACCOUNT_SWITCH_ERROR"] = "ACCOUNT_SWITCH_ERROR";
104
+ ERROR2["NO_SUGGESTIONS"] = "NO_SUGGESTIONS";
105
+ ERROR2["SUGGEST_ERROR"] = "SUGGEST_ERROR";
106
+ ERROR2["NO_SYMBOL_INFO"] = "NO_SYMBOL_INFO";
107
+ ERROR2["SYMBOL_INFO_ERROR"] = "SYMBOL_INFO_ERROR";
108
+ ERROR2["INSTRUMENTS_TIMEOUT"] = "INSTRUMENTS_TIMEOUT";
109
+ ERROR2["INSTRUMENTS_ERROR"] = "INSTRUMENTS_ERROR";
110
+ ERROR2["LIMITS_TIMEOUT"] = "LIMITS_TIMEOUT";
111
+ ERROR2["LIMITS_ERROR"] = "LIMITS_ERROR";
112
+ ERROR2["OHLC_TIMEOUT"] = "OHLC_TIMEOUT";
113
+ ERROR2["OHLC_ERROR"] = "OHLC_ERROR";
114
+ ERROR2["ORDER_ERROR"] = "ORDER_ERROR";
115
+ ERROR2["ORDERS_TIMEOUT"] = "ORDERS_TIMEOUT";
116
+ ERROR2["ORDERS_ERROR"] = "ORDERS_ERROR";
117
+ ERROR2["CANCEL_ORDER_ERROR"] = "CANCEL_ORDER_ERROR";
118
+ ERROR2["POSITION_CLOSE_ERROR"] = "POSITION_CLOSE_ERROR";
119
+ ERROR2["POSITION_METRICS_TIMEOUT"] = "POSITION_METRICS_TIMEOUT";
120
+ ERROR2["POSITION_METRICS_ERROR"] = "POSITION_METRICS_ERROR";
121
+ ERROR2["ACCOUNT_METRICS_TIMEOUT"] = "ACCOUNT_METRICS_TIMEOUT";
122
+ ERROR2["ACCOUNT_METRICS_ERROR"] = "ACCOUNT_METRICS_ERROR";
123
+ ERROR2["ACCOUNT_POSITIONS_TIMEOUT"] = "ACCOUNT_POSITIONS_TIMEOUT";
124
+ ERROR2["ACCOUNT_POSITIONS_ERROR"] = "ACCOUNT_POSITIONS_ERROR";
125
+ ERROR2["TRADE_JOURNAL_ERROR"] = "TRADE_JOURNAL_ERROR";
126
+ ERROR2["TRADE_HISTORY_ERROR"] = "TRADE_HISTORY_ERROR";
127
+ ERROR2["ASSESSMENTS_ERROR"] = "ASSESSMENTS_ERROR";
128
+ return ERROR2;
129
+ })(ERROR || {});
89
130
  var WS_MESSAGE = /* @__PURE__ */ ((WS_MESSAGE2) => {
90
131
  WS_MESSAGE2["ACCOUNT_METRICS"] = "ACCOUNT_METRICS";
91
132
  WS_MESSAGE2["ACCOUNTS"] = "ACCOUNTS";
92
133
  WS_MESSAGE2["AVAILABLE_WATCHLISTS"] = "AVAILABLE_WATCHLISTS";
134
+ WS_MESSAGE2["CHART_FEED_SUBTOPIC"] = "chartFeedSubtopic";
93
135
  WS_MESSAGE2["INSTRUMENTS"] = "INSTRUMENTS";
94
136
  WS_MESSAGE2["LIMITS"] = "LIMITS";
95
137
  WS_MESSAGE2["MESSAGE"] = "MESSAGE";
96
138
  WS_MESSAGE2["ORDERS"] = "ORDERS";
97
139
  WS_MESSAGE2["POSITIONS"] = "POSITIONS";
140
+ WS_MESSAGE2["POSITION_METRICS"] = "POSITION_METRICS";
98
141
  WS_MESSAGE2["POSITION_CASH_TRANSFERS"] = "POSITION_CASH_TRANSFERS";
99
142
  WS_MESSAGE2["PRIVATE_LAYOUT_NAMES"] = "PRIVATE_LAYOUT_NAMES";
100
143
  WS_MESSAGE2["SHARED_PROPERTIES_MESSAGE"] = "SHARED_PROPERTIES_MESSAGE";
144
+ WS_MESSAGE2["TRADE_STATUSES"] = "TRADE_STATUSES";
101
145
  WS_MESSAGE2["USER_LOGIN_INFO"] = "USER_LOGIN_INFO";
102
146
  return WS_MESSAGE2;
103
147
  })(WS_MESSAGE || {});
148
+ ((WS_MESSAGE2) => {
149
+ let SUBTOPIC;
150
+ ((SUBTOPIC2) => {
151
+ SUBTOPIC2["BIG_CHART_COMPONENT"] = "BigChartComponentPresenter-4";
152
+ })(SUBTOPIC = WS_MESSAGE2.SUBTOPIC || (WS_MESSAGE2.SUBTOPIC = {}));
153
+ })(WS_MESSAGE || (WS_MESSAGE = {}));
104
154
 
105
155
  // src/constants/errors.ts
106
156
  var DxtradeError = class extends Error {
@@ -189,6 +239,14 @@ function debugLog(msg) {
189
239
  function clearDebugLog() {
190
240
  (0, import_fs.writeFileSync)(DEBUG_LOG, "");
191
241
  }
242
+ function parseAtmosphereId(data) {
243
+ const raw = data.toString();
244
+ const parts = raw.split("|");
245
+ if (parts.length >= 2 && /^[0-9a-f-]{36}$/.test(parts[1])) {
246
+ return parts[1];
247
+ }
248
+ return null;
249
+ }
192
250
  function parseWsData(data) {
193
251
  const raw = data.toString();
194
252
  const pipeIndex = raw.indexOf("|");
@@ -203,13 +261,13 @@ function parseWsData(data) {
203
261
  // src/domains/account/account.ts
204
262
  async function getAccountMetrics(ctx, timeout = 3e4) {
205
263
  ctx.ensureSession();
206
- const wsUrl = endpoints.websocket(ctx.broker);
264
+ const wsUrl = endpoints.websocket(ctx.broker, ctx.atmosphereId);
207
265
  const cookieStr = Cookies.serialize(ctx.cookies);
208
266
  return new Promise((resolve, reject) => {
209
267
  const ws = new import_ws.default(wsUrl, { headers: { Cookie: cookieStr } });
210
268
  const timer = setTimeout(() => {
211
269
  ws.close();
212
- reject(new DxtradeError("ACCOUNT_METRICS_TIMEOUT", "Account metrics timed out"));
270
+ reject(new DxtradeError("ACCOUNT_METRICS_TIMEOUT" /* ACCOUNT_METRICS_TIMEOUT */, "Account metrics timed out"));
213
271
  }, timeout);
214
272
  ws.on("message", (data) => {
215
273
  const msg = parseWsData(data);
@@ -225,10 +283,36 @@ async function getAccountMetrics(ctx, timeout = 3e4) {
225
283
  ws.on("error", (error) => {
226
284
  clearTimeout(timer);
227
285
  ws.close();
228
- reject(new DxtradeError("ACCOUNT_METRICS_ERROR", `Account metrics error: ${error.message}`));
286
+ reject(new DxtradeError("ACCOUNT_METRICS_ERROR" /* ACCOUNT_METRICS_ERROR */, `Account metrics error: ${error.message}`));
229
287
  });
230
288
  });
231
289
  }
290
+ async function getTradeHistory(ctx, params) {
291
+ ctx.ensureSession();
292
+ try {
293
+ const cookieStr = Cookies.serialize(ctx.cookies);
294
+ const response = await retryRequest(
295
+ {
296
+ method: "GET",
297
+ url: endpoints.tradeHistory(ctx.broker, params),
298
+ headers: { ...baseHeaders(), Cookie: cookieStr }
299
+ },
300
+ ctx.retries
301
+ );
302
+ if (response.status === 200) {
303
+ const setCookies = response.headers["set-cookie"] ?? [];
304
+ const incoming = Cookies.parse(setCookies);
305
+ ctx.cookies = Cookies.merge(ctx.cookies, incoming);
306
+ return response.data;
307
+ } else {
308
+ ctx.throwError("TRADE_HISTORY_ERROR" /* TRADE_HISTORY_ERROR */, `Trade history failed: ${response.status}`);
309
+ }
310
+ } catch (error) {
311
+ if (error instanceof DxtradeError) throw error;
312
+ const message = error instanceof Error ? error.message : "Unknown error";
313
+ ctx.throwError("TRADE_HISTORY_ERROR" /* TRADE_HISTORY_ERROR */, `Trade history error: ${message}`);
314
+ }
315
+ }
232
316
  async function getTradeJournal(ctx, params) {
233
317
  ctx.ensureSession();
234
318
  try {
@@ -247,12 +331,12 @@ async function getTradeJournal(ctx, params) {
247
331
  ctx.cookies = Cookies.merge(ctx.cookies, incoming);
248
332
  return response.data;
249
333
  } else {
250
- ctx.throwError("TRADE_JOURNAL_ERROR", `Login failed: ${response.status}`);
334
+ ctx.throwError("TRADE_JOURNAL_ERROR" /* TRADE_JOURNAL_ERROR */, `Login failed: ${response.status}`);
251
335
  }
252
336
  } catch (error) {
253
337
  if (error instanceof DxtradeError) throw error;
254
338
  const message = error instanceof Error ? error.message : "Unknown error";
255
- ctx.throwError("TRADE_JOURNAL_ERROR", `Trade journal error: ${message}`);
339
+ ctx.throwError("TRADE_JOURNAL_ERROR" /* TRADE_JOURNAL_ERROR */, `Trade journal error: ${message}`);
256
340
  }
257
341
  }
258
342
 
@@ -278,7 +362,7 @@ async function getAssessments(ctx, params) {
278
362
  } catch (error) {
279
363
  if (error instanceof DxtradeError) throw error;
280
364
  const message = error instanceof Error ? error.message : "Unknown error";
281
- ctx.throwError("ASSESSMENTS_ERROR", `Error fetching assessments: ${message}`);
365
+ ctx.throwError("ASSESSMENTS_ERROR" /* ASSESSMENTS_ERROR */, `Error fetching assessments: ${message}`);
282
366
  }
283
367
  }
284
368
 
@@ -286,13 +370,13 @@ async function getAssessments(ctx, params) {
286
370
  var import_ws2 = __toESM(require("ws"));
287
371
  async function getInstruments(ctx, params = {}, timeout = 3e4) {
288
372
  ctx.ensureSession();
289
- const wsUrl = endpoints.websocket(ctx.broker);
373
+ const wsUrl = endpoints.websocket(ctx.broker, ctx.atmosphereId);
290
374
  const cookieStr = Cookies.serialize(ctx.cookies);
291
375
  return new Promise((resolve, reject) => {
292
376
  const ws = new import_ws2.default(wsUrl, { headers: { Cookie: cookieStr } });
293
377
  const timer = setTimeout(() => {
294
378
  ws.close();
295
- reject(new DxtradeError("INSTRUMENTS_TIMEOUT", "Instruments request timed out"));
379
+ reject(new DxtradeError("INSTRUMENTS_TIMEOUT" /* INSTRUMENTS_TIMEOUT */, "Instruments request timed out"));
296
380
  }, timeout);
297
381
  let instruments = [];
298
382
  let settleTimer = null;
@@ -322,17 +406,115 @@ async function getInstruments(ctx, params = {}, timeout = 3e4) {
322
406
  ws.on("error", (error) => {
323
407
  clearTimeout(timer);
324
408
  ws.close();
325
- reject(new DxtradeError("INSTRUMENTS_ERROR", `Instruments error: ${error.message}`));
409
+ reject(new DxtradeError("INSTRUMENTS_ERROR" /* INSTRUMENTS_ERROR */, `Instruments error: ${error.message}`));
410
+ });
411
+ });
412
+ }
413
+
414
+ // src/domains/ohlc/ohlc.ts
415
+ var import_ws3 = __toESM(require("ws"));
416
+ async function getOHLC(ctx, params, timeout = 3e4) {
417
+ ctx.ensureSession();
418
+ const { symbol, resolution = 60, range = 432e3, maxBars = 3500, priceField = "bid" } = params;
419
+ const wsUrl = endpoints.websocket(ctx.broker, ctx.atmosphereId);
420
+ const cookieStr = Cookies.serialize(ctx.cookies);
421
+ const headers = authHeaders(ctx.csrf, cookieStr);
422
+ return new Promise((resolve, reject) => {
423
+ const ws = new import_ws3.default(wsUrl, { headers: { Cookie: cookieStr } });
424
+ const bars = [];
425
+ let putsSent = false;
426
+ let initSettleTimer = null;
427
+ let barSettleTimer = null;
428
+ const timer = setTimeout(() => {
429
+ ws.close();
430
+ reject(new DxtradeError("OHLC_TIMEOUT" /* OHLC_TIMEOUT */, "OHLC data timed out"));
431
+ }, timeout);
432
+ function cleanup() {
433
+ clearTimeout(timer);
434
+ if (initSettleTimer) clearTimeout(initSettleTimer);
435
+ if (barSettleTimer) clearTimeout(barSettleTimer);
436
+ ws.close();
437
+ }
438
+ async function sendPuts() {
439
+ putsSent = true;
440
+ try {
441
+ await retryRequest(
442
+ {
443
+ method: "PUT",
444
+ url: endpoints.subscribeInstruments(ctx.broker),
445
+ data: { instruments: [symbol] },
446
+ headers
447
+ },
448
+ ctx.retries
449
+ );
450
+ await retryRequest(
451
+ {
452
+ method: "PUT",
453
+ url: endpoints.charts(ctx.broker),
454
+ data: {
455
+ chartIds: [],
456
+ requests: [
457
+ {
458
+ aggregationPeriodSeconds: resolution,
459
+ extendedSession: true,
460
+ forexPriceField: priceField,
461
+ id: 0,
462
+ maxBarsCount: maxBars,
463
+ range,
464
+ studySubscription: [],
465
+ subtopic: WS_MESSAGE.SUBTOPIC.BIG_CHART_COMPONENT,
466
+ symbol
467
+ }
468
+ ]
469
+ },
470
+ headers
471
+ },
472
+ ctx.retries
473
+ );
474
+ } catch (error) {
475
+ cleanup();
476
+ const message = error instanceof Error ? error.message : "Unknown error";
477
+ reject(new DxtradeError("OHLC_ERROR" /* OHLC_ERROR */, `Error fetching OHLC data: ${message}`));
478
+ }
479
+ }
480
+ ws.on("message", (data) => {
481
+ const msg = parseWsData(data);
482
+ if (shouldLog(msg, ctx.debug)) debugLog(msg);
483
+ if (typeof msg === "string") return;
484
+ if (!putsSent) {
485
+ if (initSettleTimer) clearTimeout(initSettleTimer);
486
+ initSettleTimer = setTimeout(() => sendPuts(), 1e3);
487
+ return;
488
+ }
489
+ const body = msg.body;
490
+ if (body?.subtopic !== WS_MESSAGE.SUBTOPIC.BIG_CHART_COMPONENT) return;
491
+ if (Array.isArray(body.data)) {
492
+ bars.push(...body.data);
493
+ }
494
+ if (barSettleTimer) clearTimeout(barSettleTimer);
495
+ if (body.snapshotEnd) {
496
+ cleanup();
497
+ resolve(bars);
498
+ } else {
499
+ barSettleTimer = setTimeout(() => {
500
+ cleanup();
501
+ resolve(bars);
502
+ }, 2e3);
503
+ }
504
+ });
505
+ ws.on("error", (error) => {
506
+ cleanup();
507
+ reject(new DxtradeError("OHLC_ERROR" /* OHLC_ERROR */, `OHLC WebSocket error: ${error.message}`));
326
508
  });
327
509
  });
328
510
  }
329
511
 
330
512
  // src/domains/order/order.ts
331
513
  var import_crypto = __toESM(require("crypto"));
332
- var import_ws4 = __toESM(require("ws"));
514
+ var import_ws5 = __toESM(require("ws"));
333
515
 
334
516
  // src/domains/symbol/symbol.ts
335
- var import_ws3 = __toESM(require("ws"));
517
+ var import_ws4 = __toESM(require("ws"));
336
518
  async function getSymbolSuggestions(ctx, text) {
337
519
  ctx.ensureSession();
338
520
  try {
@@ -347,13 +529,13 @@ async function getSymbolSuggestions(ctx, text) {
347
529
  );
348
530
  const suggests = response.data?.suggests;
349
531
  if (!suggests?.length) {
350
- ctx.throwError("NO_SUGGESTIONS", "No symbol suggestions found");
532
+ ctx.throwError("NO_SUGGESTIONS" /* NO_SUGGESTIONS */, "No symbol suggestions found");
351
533
  }
352
534
  return suggests;
353
535
  } catch (error) {
354
536
  if (error instanceof DxtradeError) throw error;
355
537
  const message = error instanceof Error ? error.message : "Unknown error";
356
- ctx.throwError("SUGGEST_ERROR", `Error getting symbol suggestions: ${message}`);
538
+ ctx.throwError("SUGGEST_ERROR" /* SUGGEST_ERROR */, `Error getting symbol suggestions: ${message}`);
357
539
  }
358
540
  }
359
541
  async function getSymbolInfo(ctx, symbol) {
@@ -370,24 +552,24 @@ async function getSymbolInfo(ctx, symbol) {
370
552
  ctx.retries
371
553
  );
372
554
  if (!response.data) {
373
- ctx.throwError("NO_SYMBOL_INFO", "No symbol info returned");
555
+ ctx.throwError("NO_SYMBOL_INFO" /* NO_SYMBOL_INFO */, "No symbol info returned");
374
556
  }
375
557
  return response.data;
376
558
  } catch (error) {
377
559
  if (error instanceof DxtradeError) throw error;
378
560
  const message = error instanceof Error ? error.message : "Unknown error";
379
- ctx.throwError("SYMBOL_INFO_ERROR", `Error getting symbol info: ${message}`);
561
+ ctx.throwError("SYMBOL_INFO_ERROR" /* SYMBOL_INFO_ERROR */, `Error getting symbol info: ${message}`);
380
562
  }
381
563
  }
382
564
  async function getSymbolLimits(ctx, timeout = 3e4) {
383
565
  ctx.ensureSession();
384
- const wsUrl = endpoints.websocket(ctx.broker);
566
+ const wsUrl = endpoints.websocket(ctx.broker, ctx.atmosphereId);
385
567
  const cookieStr = Cookies.serialize(ctx.cookies);
386
568
  return new Promise((resolve, reject) => {
387
- const ws = new import_ws3.default(wsUrl, { headers: { Cookie: cookieStr } });
569
+ const ws = new import_ws4.default(wsUrl, { headers: { Cookie: cookieStr } });
388
570
  const timer = setTimeout(() => {
389
571
  ws.close();
390
- reject(new DxtradeError("LIMITS_TIMEOUT", "Symbol limits request timed out"));
572
+ reject(new DxtradeError("LIMITS_TIMEOUT" /* LIMITS_TIMEOUT */, "Symbol limits request timed out"));
391
573
  }, timeout);
392
574
  let limits = [];
393
575
  let settleTimer = null;
@@ -410,14 +592,14 @@ async function getSymbolLimits(ctx, timeout = 3e4) {
410
592
  ws.on("error", (error) => {
411
593
  clearTimeout(timer);
412
594
  ws.close();
413
- reject(new DxtradeError("LIMITS_ERROR", `Symbol limits error: ${error.message}`));
595
+ reject(new DxtradeError("LIMITS_ERROR" /* LIMITS_ERROR */, `Symbol limits error: ${error.message}`));
414
596
  });
415
597
  });
416
598
  }
417
599
 
418
600
  // src/domains/order/order.ts
419
601
  function createOrderListener(wsUrl, cookieStr, timeout = 3e4, debug = false) {
420
- const ws = new import_ws4.default(wsUrl, { headers: { Cookie: cookieStr } });
602
+ const ws = new import_ws5.default(wsUrl, { headers: { Cookie: cookieStr } });
421
603
  let settled = false;
422
604
  const ready = new Promise((resolve) => {
423
605
  ws.on("open", resolve);
@@ -482,6 +664,61 @@ function createOrderListener(wsUrl, cookieStr, timeout = 3e4, debug = false) {
482
664
  });
483
665
  return { promise, ready };
484
666
  }
667
+ async function getOrders(ctx, timeout = 3e4) {
668
+ ctx.ensureSession();
669
+ const wsUrl = endpoints.websocket(ctx.broker, ctx.atmosphereId);
670
+ const cookieStr = Cookies.serialize(ctx.cookies);
671
+ return new Promise((resolve, reject) => {
672
+ const ws = new import_ws5.default(wsUrl, { headers: { Cookie: cookieStr } });
673
+ const timer = setTimeout(() => {
674
+ ws.close();
675
+ reject(new DxtradeError("ORDERS_TIMEOUT" /* ORDERS_TIMEOUT */, "Orders request timed out"));
676
+ }, timeout);
677
+ ws.on("message", (data) => {
678
+ const msg = parseWsData(data);
679
+ if (shouldLog(msg, ctx.debug)) debugLog(msg);
680
+ if (typeof msg === "string") return;
681
+ if (msg.type === "ORDERS" /* ORDERS */) {
682
+ clearTimeout(timer);
683
+ ws.close();
684
+ resolve(msg.body);
685
+ }
686
+ });
687
+ ws.on("error", (error) => {
688
+ clearTimeout(timer);
689
+ ws.close();
690
+ reject(new DxtradeError("ORDERS_ERROR" /* ORDERS_ERROR */, `Orders error: ${error.message}`));
691
+ });
692
+ });
693
+ }
694
+ async function cancelOrder(ctx, orderChainId) {
695
+ ctx.ensureSession();
696
+ const accountId = ctx.accountId ?? ctx.config.accountId;
697
+ if (!accountId) {
698
+ ctx.throwError("CANCEL_ORDER_ERROR" /* CANCEL_ORDER_ERROR */, "accountId is required to cancel an order");
699
+ }
700
+ try {
701
+ await retryRequest(
702
+ {
703
+ method: "DELETE",
704
+ url: endpoints.cancelOrder(ctx.broker, accountId, orderChainId),
705
+ headers: authHeaders(ctx.csrf, Cookies.serialize(ctx.cookies))
706
+ },
707
+ ctx.retries
708
+ );
709
+ } catch (error) {
710
+ if (error instanceof DxtradeError) throw error;
711
+ const message = error instanceof Error ? error.response?.data?.message ?? error.message : "Unknown error";
712
+ ctx.throwError("CANCEL_ORDER_ERROR" /* CANCEL_ORDER_ERROR */, `Cancel order error: ${message}`);
713
+ }
714
+ }
715
+ async function cancelAllOrders(ctx) {
716
+ const orders = await getOrders(ctx);
717
+ const pending = orders.filter((o) => !o.finalStatus);
718
+ for (const order of pending) {
719
+ await cancelOrder(ctx, order.orderId);
720
+ }
721
+ }
485
722
  async function submitOrder(ctx, params) {
486
723
  ctx.ensureSession();
487
724
  const {
@@ -551,7 +788,7 @@ async function submitOrder(ctx, params) {
551
788
  };
552
789
  }
553
790
  try {
554
- const wsUrl = endpoints.websocket(ctx.broker);
791
+ const wsUrl = endpoints.websocket(ctx.broker, ctx.atmosphereId);
555
792
  const cookieStr = Cookies.serialize(ctx.cookies);
556
793
  const listener = createOrderListener(wsUrl, cookieStr, 3e4, ctx.debug);
557
794
  await listener.ready;
@@ -571,21 +808,21 @@ async function submitOrder(ctx, params) {
571
808
  } catch (error) {
572
809
  if (error instanceof DxtradeError) throw error;
573
810
  const message = error instanceof Error ? error.response?.data?.message ?? error.message : "Unknown error";
574
- ctx.throwError("ORDER_ERROR", `Error submitting order: ${message}`);
811
+ ctx.throwError("ORDER_ERROR" /* ORDER_ERROR */, `Error submitting order: ${message}`);
575
812
  }
576
813
  }
577
814
 
578
815
  // src/domains/position/position.ts
579
- var import_ws5 = __toESM(require("ws"));
816
+ var import_ws6 = __toESM(require("ws"));
580
817
  async function getPositions(ctx) {
581
818
  ctx.ensureSession();
582
- const wsUrl = endpoints.websocket(ctx.broker);
819
+ const wsUrl = endpoints.websocket(ctx.broker, ctx.atmosphereId);
583
820
  const cookieStr = Cookies.serialize(ctx.cookies);
584
821
  return new Promise((resolve, reject) => {
585
- const ws = new import_ws5.default(wsUrl, { headers: { Cookie: cookieStr } });
822
+ const ws = new import_ws6.default(wsUrl, { headers: { Cookie: cookieStr } });
586
823
  const timer = setTimeout(() => {
587
824
  ws.close();
588
- reject(new DxtradeError("ACCOUNT_POSITIONS_TIMEOUT", "Account positions timed out"));
825
+ reject(new DxtradeError("ACCOUNT_POSITIONS_TIMEOUT" /* ACCOUNT_POSITIONS_TIMEOUT */, "Account positions timed out"));
589
826
  }, 3e4);
590
827
  ws.on("message", (data) => {
591
828
  const msg = parseWsData(data);
@@ -600,10 +837,58 @@ async function getPositions(ctx) {
600
837
  ws.on("error", (error) => {
601
838
  clearTimeout(timer);
602
839
  ws.close();
603
- reject(new DxtradeError("ACCOUNT_POSITIONS_ERROR", `Account positions error: ${error.message}`));
840
+ reject(new DxtradeError("ACCOUNT_POSITIONS_ERROR" /* ACCOUNT_POSITIONS_ERROR */, `Account positions error: ${error.message}`));
604
841
  });
605
842
  });
606
843
  }
844
+ async function getPositionMetrics(ctx, timeout = 3e4) {
845
+ ctx.ensureSession();
846
+ const wsUrl = endpoints.websocket(ctx.broker, ctx.atmosphereId);
847
+ const cookieStr = Cookies.serialize(ctx.cookies);
848
+ return new Promise((resolve, reject) => {
849
+ const ws = new import_ws6.default(wsUrl, { headers: { Cookie: cookieStr } });
850
+ const timer = setTimeout(() => {
851
+ ws.close();
852
+ reject(new DxtradeError("POSITION_METRICS_TIMEOUT" /* POSITION_METRICS_TIMEOUT */, "Position metrics timed out"));
853
+ }, timeout);
854
+ ws.on("message", (data) => {
855
+ const msg = parseWsData(data);
856
+ if (shouldLog(msg, ctx.debug)) debugLog(msg);
857
+ if (typeof msg === "string") return;
858
+ if (msg.type === "POSITION_METRICS" /* POSITION_METRICS */) {
859
+ clearTimeout(timer);
860
+ ws.close();
861
+ resolve(msg.body);
862
+ }
863
+ });
864
+ ws.on("error", (error) => {
865
+ clearTimeout(timer);
866
+ ws.close();
867
+ reject(new DxtradeError("POSITION_METRICS_ERROR" /* POSITION_METRICS_ERROR */, `Position metrics error: ${error.message}`));
868
+ });
869
+ });
870
+ }
871
+ async function closeAllPositions(ctx) {
872
+ const positions = await getPositions(ctx);
873
+ for (const pos of positions) {
874
+ const closeData = {
875
+ legs: [
876
+ {
877
+ instrumentId: pos.positionKey.instrumentId,
878
+ positionCode: pos.positionKey.positionCode,
879
+ positionEffect: "CLOSING",
880
+ ratioQuantity: 1,
881
+ symbol: pos.positionKey.positionCode
882
+ }
883
+ ],
884
+ limitPrice: 0,
885
+ orderType: "MARKET",
886
+ quantity: -pos.quantity,
887
+ timeInForce: "GTC"
888
+ };
889
+ await closePosition(ctx, closeData);
890
+ }
891
+ }
607
892
  async function closePosition(ctx, data) {
608
893
  try {
609
894
  await retryRequest(
@@ -618,27 +903,31 @@ async function closePosition(ctx, data) {
618
903
  } catch (error) {
619
904
  if (error instanceof DxtradeError) throw error;
620
905
  const message = error instanceof Error ? error.response?.data?.message ?? error.message : "Unknown error";
621
- ctx.throwError("POSITION_CLOSE_ERROR", `Position close error: ${message}`);
906
+ ctx.throwError("POSITION_CLOSE_ERROR" /* POSITION_CLOSE_ERROR */, `Position close error: ${message}`);
622
907
  }
623
908
  }
624
909
 
625
910
  // src/domains/session/session.ts
626
- var import_ws6 = __toESM(require("ws"));
911
+ var import_ws7 = __toESM(require("ws"));
627
912
  function waitForHandshake(wsUrl, cookieStr, timeout = 3e4, debug = false) {
628
913
  return new Promise((resolve, reject) => {
629
- const ws = new import_ws6.default(wsUrl, { headers: { Cookie: cookieStr } });
914
+ const ws = new import_ws7.default(wsUrl, { headers: { Cookie: cookieStr } });
915
+ let atmosphereId = null;
630
916
  const timer = setTimeout(() => {
631
917
  ws.close();
632
918
  reject(new Error("[dxtrade-api] Handshake timed out"));
633
919
  }, timeout);
634
920
  ws.on("message", (data) => {
921
+ if (!atmosphereId) {
922
+ atmosphereId = parseAtmosphereId(data);
923
+ }
635
924
  const msg = parseWsData(data);
636
925
  if (shouldLog(msg, debug)) debugLog(msg);
637
926
  if (typeof msg === "string") return;
638
927
  if (msg.accountId) {
639
928
  clearTimeout(timer);
640
929
  ws.close();
641
- resolve();
930
+ resolve({ atmosphereId, accountId: msg.accountId });
642
931
  }
643
932
  });
644
933
  ws.on("error", (error) => {
@@ -669,12 +958,12 @@ async function login(ctx) {
669
958
  ctx.cookies = Cookies.merge(ctx.cookies, incoming);
670
959
  ctx.callbacks.onLogin?.();
671
960
  } else {
672
- ctx.throwError("LOGIN_FAILED", `Login failed: ${response.status}`);
961
+ ctx.throwError("LOGIN_FAILED" /* LOGIN_FAILED */, `Login failed: ${response.status}`);
673
962
  }
674
963
  } catch (error) {
675
964
  if (error instanceof DxtradeError) throw error;
676
965
  const message = error instanceof Error ? error.message : "Unknown error";
677
- ctx.throwError("LOGIN_ERROR", `Login error: ${message}`);
966
+ ctx.throwError("LOGIN_ERROR" /* LOGIN_ERROR */, `Login error: ${message}`);
678
967
  }
679
968
  }
680
969
  async function fetchCsrf(ctx) {
@@ -692,12 +981,12 @@ async function fetchCsrf(ctx) {
692
981
  if (csrfMatch) {
693
982
  ctx.csrf = csrfMatch[1];
694
983
  } else {
695
- ctx.throwError("CSRF_NOT_FOUND", "CSRF token not found");
984
+ ctx.throwError("CSRF_NOT_FOUND" /* CSRF_NOT_FOUND */, "CSRF token not found");
696
985
  }
697
986
  } catch (error) {
698
987
  if (error instanceof DxtradeError) throw error;
699
988
  const message = error instanceof Error ? error.message : "Unknown error";
700
- ctx.throwError("CSRF_ERROR", `CSRF fetch error: ${message}`);
989
+ ctx.throwError("CSRF_ERROR" /* CSRF_ERROR */, `CSRF fetch error: ${message}`);
701
990
  }
702
991
  }
703
992
  async function switchAccount(ctx, accountId) {
@@ -715,19 +1004,27 @@ async function switchAccount(ctx, accountId) {
715
1004
  } catch (error) {
716
1005
  if (error instanceof DxtradeError) throw error;
717
1006
  const message = error instanceof Error ? error.message : "Unknown error";
718
- ctx.throwError("ACCOUNT_SWITCH_ERROR", `Error switching account: ${message}`);
1007
+ ctx.throwError("ACCOUNT_SWITCH_ERROR" /* ACCOUNT_SWITCH_ERROR */, `Error switching account: ${message}`);
719
1008
  }
720
1009
  }
721
1010
  async function connect(ctx) {
722
1011
  await login(ctx);
723
1012
  await fetchCsrf(ctx);
724
1013
  if (ctx.debug) clearDebugLog();
725
- const wsUrl = endpoints.websocket(ctx.broker);
726
1014
  const cookieStr = Cookies.serialize(ctx.cookies);
727
- await waitForHandshake(wsUrl, cookieStr, 3e4, ctx.debug);
1015
+ const handshake = await waitForHandshake(endpoints.websocket(ctx.broker), cookieStr, 3e4, ctx.debug);
1016
+ ctx.atmosphereId = handshake.atmosphereId;
1017
+ ctx.accountId = handshake.accountId;
728
1018
  if (ctx.config.accountId) {
729
1019
  await switchAccount(ctx, ctx.config.accountId);
730
- await waitForHandshake(endpoints.websocket(ctx.broker), Cookies.serialize(ctx.cookies), 3e4, ctx.debug);
1020
+ const reconnect = await waitForHandshake(
1021
+ endpoints.websocket(ctx.broker, ctx.atmosphereId),
1022
+ Cookies.serialize(ctx.cookies),
1023
+ 3e4,
1024
+ ctx.debug
1025
+ );
1026
+ ctx.atmosphereId = reconnect.atmosphereId;
1027
+ ctx.accountId = reconnect.accountId;
731
1028
  }
732
1029
  }
733
1030
 
@@ -741,12 +1038,17 @@ var DxtradeClient = class {
741
1038
  callbacks,
742
1039
  cookies: {},
743
1040
  csrf: null,
1041
+ accountId: config.accountId ?? null,
1042
+ atmosphereId: null,
744
1043
  broker: config.broker,
745
1044
  retries: config.retries ?? 3,
746
1045
  debug: config.debug ?? false,
747
1046
  ensureSession() {
748
1047
  if (!this.csrf) {
749
- throw new DxtradeError("NO_SESSION", "No active session. Call login() and fetchCsrf() or connect() first.");
1048
+ throw new DxtradeError(
1049
+ "NO_SESSION" /* NO_SESSION */,
1050
+ "No active session. Call login() and fetchCsrf() or connect() first."
1051
+ );
750
1052
  }
751
1053
  },
752
1054
  throwError(code, message) {
@@ -791,6 +1093,18 @@ var DxtradeClient = class {
791
1093
  async submitOrder(params) {
792
1094
  return submitOrder(this._ctx, params);
793
1095
  }
1096
+ /** Get all pending/open orders via WebSocket. */
1097
+ async getOrders() {
1098
+ return getOrders(this._ctx);
1099
+ }
1100
+ /** Cancel a single pending order by its order chain ID. */
1101
+ async cancelOrder(orderChainId) {
1102
+ return cancelOrder(this._ctx, orderChainId);
1103
+ }
1104
+ /** Cancel all pending orders. */
1105
+ async cancelAllOrders() {
1106
+ return cancelAllOrders(this._ctx);
1107
+ }
794
1108
  /** Get account metrics including equity, balance, margin, and open P&L. */
795
1109
  async getAccountMetrics() {
796
1110
  return getAccountMetrics(this._ctx);
@@ -799,10 +1113,20 @@ var DxtradeClient = class {
799
1113
  async getPositions() {
800
1114
  return getPositions(this._ctx);
801
1115
  }
802
- /** Close a position. */
1116
+ /**
1117
+ * Close a position. Supports partial closes by specifying a quantity smaller than the full position size.
1118
+ */
803
1119
  async closePosition(position) {
804
1120
  return closePosition(this._ctx, position);
805
1121
  }
1122
+ /** Close all open positions with market orders. */
1123
+ async closeAllPositions() {
1124
+ return closeAllPositions(this._ctx);
1125
+ }
1126
+ /** Get position-level P&L metrics via WebSocket. */
1127
+ async getPositionMetrics() {
1128
+ return getPositionMetrics(this._ctx);
1129
+ }
806
1130
  /**
807
1131
  * Fetch trade journal entries for a date range.
808
1132
  * @param params.from - Start timestamp (Unix ms)
@@ -811,6 +1135,14 @@ var DxtradeClient = class {
811
1135
  async getTradeJournal(params) {
812
1136
  return getTradeJournal(this._ctx, params);
813
1137
  }
1138
+ /**
1139
+ * Fetch trade history for a date range.
1140
+ * @param params.from - Start timestamp (Unix ms)
1141
+ * @param params.to - End timestamp (Unix ms)
1142
+ */
1143
+ async getTradeHistory(params) {
1144
+ return getTradeHistory(this._ctx, params);
1145
+ }
814
1146
  /** Get all available instruments, optionally filtered by partial match (e.g. `{ type: "FOREX" }`). */
815
1147
  async getInstruments(params = {}) {
816
1148
  return getInstruments(this._ctx, params);
@@ -819,6 +1151,17 @@ var DxtradeClient = class {
819
1151
  async getAssessments(params) {
820
1152
  return getAssessments(this._ctx, params);
821
1153
  }
1154
+ /**
1155
+ * Fetch OHLC price bars for a symbol.
1156
+ * @param params.symbol - Instrument symbol (e.g. "EURUSD")
1157
+ * @param params.resolution - Bar period in seconds (default: 60 = 1 min)
1158
+ * @param params.range - Lookback window in seconds (default: 432000 = 5 days)
1159
+ * @param params.maxBars - Maximum bars to return (default: 3500)
1160
+ * @param params.priceField - "bid" or "ask" (default: "bid")
1161
+ */
1162
+ async getOHLC(params) {
1163
+ return getOHLC(this._ctx, params);
1164
+ }
822
1165
  };
823
1166
  // Annotate the CommonJS export names for ESM import in node:
824
1167
  0 && (module.exports = {
@@ -826,6 +1169,7 @@ var DxtradeClient = class {
826
1169
  BROKER,
827
1170
  DxtradeClient,
828
1171
  DxtradeError,
1172
+ ERROR,
829
1173
  ORDER_TYPE,
830
1174
  SIDE,
831
1175
  TIF,