@danielgroen/dxtrade-api 1.0.21 → 1.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -21,18 +21,18 @@ npm install dxtrade-api
21
21
 
22
22
  - [x] Authentication & session management
23
23
  - [x] Submit orders (market, limit, stop)
24
- - [x] Positions (get & close)
25
- - [x] Account metrics & trade journal
24
+ - [x] Get & cancel orders
25
+ - [x] Positions (get, close, close all)
26
+ - [x] Position metrics (per-position P&L)
27
+ - [x] Account metrics, trade journal & trade history
26
28
  - [x] Symbol search & instrument info
27
29
  - [x] OHLC / price bar data
28
30
  - [x] PnL assessments
29
31
  - [x] Multi-broker support (FTMO, Eightcap, Lark Funding)
30
32
  - [x] Full TypeScript support
31
33
  - [ ] Batch orders
32
- - [ ] Close whole position helper
33
34
  - [ ] Modify existing orders
34
35
  - [ ] Real-time price streaming
35
- - [ ] Order history
36
36
 
37
37
  ## Quick Start
38
38
 
@@ -105,16 +105,22 @@ BROKER.FTMO // "https://dxtrade.ftmo.com"
105
105
  ### Trading
106
106
 
107
107
  - `client.submitOrder(params)` — Submit an order and wait for WebSocket confirmation
108
+ - `client.getOrders()` — Get all pending/open orders via WebSocket
109
+ - `client.cancelOrder(orderChainId)` — Cancel a single pending order
110
+ - `client.cancelAllOrders()` — Cancel all pending orders
108
111
 
109
112
  ### Positions
110
113
 
111
114
  - `client.getPositions()` — Get all open positions via WebSocket
112
- - `client.closePosition(params)` — Close a position
115
+ - `client.closePosition(params)` — Close a position (supports partial closes via the quantity field)
116
+ - `client.closeAllPositions()` — Close all open positions with market orders
117
+ - `client.getPositionMetrics()` — Get position-level P&L metrics via WebSocket
113
118
 
114
119
  ### Account
115
120
 
116
121
  - `client.getAccountMetrics()` — Get account metrics (equity, balance, margin, open P&L, etc.)
117
122
  - `client.getTradeJournal({ from, to })` — Fetch trade journal entries for a date range (Unix timestamps)
123
+ - `client.getTradeHistory({ from, to })` — Fetch trade history for a date range (Unix timestamps)
118
124
 
119
125
  ### Analytics
120
126
 
@@ -152,8 +158,11 @@ const client = new DxtradeClient({
152
158
  cp .env.example .env # fill in credentials
153
159
  npm run example:connect
154
160
  npm run example:order
161
+ npm run example:orders
155
162
  npm run example:positions
156
163
  npm run example:close-position
164
+ npm run example:close-all-positions
165
+ npm run example:position-metrics
157
166
  npm run example:assessments
158
167
  npm run example:assessments:btc
159
168
  npm run example:account
@@ -163,6 +172,7 @@ npm run example:instruments:forex
163
172
  npm run example:symbol
164
173
  npm run example:symbol:btc
165
174
  npm run example:trade-journal
175
+ npm run example:trade-history
166
176
  npm run example:debug
167
177
  ```
168
178
 
package/dist/index.d.mts CHANGED
@@ -11,12 +11,17 @@ declare const endpoints: {
11
11
  instrumentInfo: (base: string, symbol: string, tzOffset: number) => string;
12
12
  submitOrder: (base: string) => string;
13
13
  closePosition: (base: string) => string;
14
+ cancelOrder: (base: string, accountId: string, orderChainId: number) => string;
14
15
  assessments: (base: string) => string;
15
16
  websocket: (base: string, atmosphereId?: string | null) => string;
16
17
  tradeJournal: (base: string, params: {
17
18
  from: number;
18
19
  to: number;
19
20
  }) => string;
21
+ tradeHistory: (base: string, params: {
22
+ from: number;
23
+ to: number;
24
+ }) => string;
20
25
  subscribeInstruments: (base: string) => string;
21
26
  charts: (base: string) => string;
22
27
  };
@@ -57,12 +62,18 @@ declare enum ERROR {
57
62
  OHLC_TIMEOUT = "OHLC_TIMEOUT",
58
63
  OHLC_ERROR = "OHLC_ERROR",
59
64
  ORDER_ERROR = "ORDER_ERROR",
65
+ ORDERS_TIMEOUT = "ORDERS_TIMEOUT",
66
+ ORDERS_ERROR = "ORDERS_ERROR",
67
+ CANCEL_ORDER_ERROR = "CANCEL_ORDER_ERROR",
60
68
  POSITION_CLOSE_ERROR = "POSITION_CLOSE_ERROR",
69
+ POSITION_METRICS_TIMEOUT = "POSITION_METRICS_TIMEOUT",
70
+ POSITION_METRICS_ERROR = "POSITION_METRICS_ERROR",
61
71
  ACCOUNT_METRICS_TIMEOUT = "ACCOUNT_METRICS_TIMEOUT",
62
72
  ACCOUNT_METRICS_ERROR = "ACCOUNT_METRICS_ERROR",
63
73
  ACCOUNT_POSITIONS_TIMEOUT = "ACCOUNT_POSITIONS_TIMEOUT",
64
74
  ACCOUNT_POSITIONS_ERROR = "ACCOUNT_POSITIONS_ERROR",
65
75
  TRADE_JOURNAL_ERROR = "TRADE_JOURNAL_ERROR",
76
+ TRADE_HISTORY_ERROR = "TRADE_HISTORY_ERROR",
66
77
  ASSESSMENTS_ERROR = "ASSESSMENTS_ERROR"
67
78
  }
68
79
  declare enum WS_MESSAGE {
@@ -71,11 +82,11 @@ declare enum WS_MESSAGE {
71
82
  AVAILABLE_WATCHLISTS = "AVAILABLE_WATCHLISTS",
72
83
  CHART_FEED_SUBTOPIC = "chartFeedSubtopic",
73
84
  INSTRUMENTS = "INSTRUMENTS",
74
- INSTRUMENT_METRICS = "INSTRUMENT_METRICS",
75
85
  LIMITS = "LIMITS",
76
86
  MESSAGE = "MESSAGE",
77
87
  ORDERS = "ORDERS",
78
88
  POSITIONS = "POSITIONS",
89
+ POSITION_METRICS = "POSITION_METRICS",
79
90
  POSITION_CASH_TRANSFERS = "POSITION_CASH_TRANSFERS",
80
91
  PRIVATE_LAYOUT_NAMES = "PRIVATE_LAYOUT_NAMES",
81
92
  SHARED_PROPERTIES_MESSAGE = "SHARED_PROPERTIES_MESSAGE",
@@ -94,6 +105,22 @@ declare class DxtradeError extends Error {
94
105
  }
95
106
 
96
107
  declare namespace Order {
108
+ interface Get {
109
+ account: string;
110
+ orderId: number;
111
+ orderCode: string;
112
+ version: number;
113
+ type: ORDER_TYPE;
114
+ instrument: string;
115
+ status: string;
116
+ finalStatus: boolean;
117
+ side: SIDE;
118
+ tif: TIF;
119
+ legs: Leg[];
120
+ issueTime: string;
121
+ transactionTime: string;
122
+ [key: string]: unknown;
123
+ }
97
124
  interface SubmitParams {
98
125
  symbol: string;
99
126
  side: SIDE;
@@ -207,6 +234,20 @@ interface WsPayload {
207
234
  }
208
235
 
209
236
  declare namespace Account {
237
+ interface TradeHistory {
238
+ orderId: number;
239
+ orderCode: string;
240
+ instrument: string;
241
+ side: string;
242
+ type: string;
243
+ status: string;
244
+ quantity: number;
245
+ filledQuantity: number;
246
+ price: number;
247
+ averagePrice: number;
248
+ time: string;
249
+ [key: string]: unknown;
250
+ }
210
251
  interface Metrics {
211
252
  availableFunds: number;
212
253
  marginCallLevel: number | string;
@@ -318,6 +359,14 @@ declare namespace Position {
318
359
  takeProfit: number | null;
319
360
  stopLoss: number | null;
320
361
  }
362
+ interface Metrics {
363
+ positionCode: string;
364
+ openPl: number;
365
+ openPlPerLot: number;
366
+ currentPrice: number;
367
+ convertedOpenPl: number;
368
+ [key: string]: unknown;
369
+ }
321
370
  interface Close {
322
371
  legs: {
323
372
  instrumentId: number;
@@ -398,12 +447,24 @@ declare class DxtradeClient {
398
447
  * Supports market, limit, and stop orders with optional stop loss and take profit.
399
448
  */
400
449
  submitOrder(params: Order.SubmitParams): Promise<Order.Update>;
450
+ /** Get all pending/open orders via WebSocket. */
451
+ getOrders(): Promise<Order.Get[]>;
452
+ /** Cancel a single pending order by its order chain ID. */
453
+ cancelOrder(orderChainId: number): Promise<void>;
454
+ /** Cancel all pending orders. */
455
+ cancelAllOrders(): Promise<void>;
401
456
  /** Get account metrics including equity, balance, margin, and open P&L. */
402
457
  getAccountMetrics(): Promise<Account.Metrics>;
403
458
  /** Get all open positions via WebSocket. */
404
459
  getPositions(): Promise<Position.Get[]>;
405
- /** Close a position. */
460
+ /**
461
+ * Close a position. Supports partial closes by specifying a quantity smaller than the full position size.
462
+ */
406
463
  closePosition(position: Position.Close): Promise<void>;
464
+ /** Close all open positions with market orders. */
465
+ closeAllPositions(): Promise<void>;
466
+ /** Get position-level P&L metrics via WebSocket. */
467
+ getPositionMetrics(): Promise<Position.Metrics[]>;
407
468
  /**
408
469
  * Fetch trade journal entries for a date range.
409
470
  * @param params.from - Start timestamp (Unix ms)
@@ -413,6 +474,15 @@ declare class DxtradeClient {
413
474
  from: number;
414
475
  to: number;
415
476
  }): Promise<any>;
477
+ /**
478
+ * Fetch trade history for a date range.
479
+ * @param params.from - Start timestamp (Unix ms)
480
+ * @param params.to - End timestamp (Unix ms)
481
+ */
482
+ getTradeHistory(params: {
483
+ from: number;
484
+ to: number;
485
+ }): Promise<Account.TradeHistory[]>;
416
486
  /** Get all available instruments, optionally filtered by partial match (e.g. `{ type: "FOREX" }`). */
417
487
  getInstruments(params?: Partial<Instrument.Info>): Promise<Instrument.Info[]>;
418
488
  /** Fetch PnL assessments for an instrument within a date range. */
package/dist/index.d.ts CHANGED
@@ -11,12 +11,17 @@ declare const endpoints: {
11
11
  instrumentInfo: (base: string, symbol: string, tzOffset: number) => string;
12
12
  submitOrder: (base: string) => string;
13
13
  closePosition: (base: string) => string;
14
+ cancelOrder: (base: string, accountId: string, orderChainId: number) => string;
14
15
  assessments: (base: string) => string;
15
16
  websocket: (base: string, atmosphereId?: string | null) => string;
16
17
  tradeJournal: (base: string, params: {
17
18
  from: number;
18
19
  to: number;
19
20
  }) => string;
21
+ tradeHistory: (base: string, params: {
22
+ from: number;
23
+ to: number;
24
+ }) => string;
20
25
  subscribeInstruments: (base: string) => string;
21
26
  charts: (base: string) => string;
22
27
  };
@@ -57,12 +62,18 @@ declare enum ERROR {
57
62
  OHLC_TIMEOUT = "OHLC_TIMEOUT",
58
63
  OHLC_ERROR = "OHLC_ERROR",
59
64
  ORDER_ERROR = "ORDER_ERROR",
65
+ ORDERS_TIMEOUT = "ORDERS_TIMEOUT",
66
+ ORDERS_ERROR = "ORDERS_ERROR",
67
+ CANCEL_ORDER_ERROR = "CANCEL_ORDER_ERROR",
60
68
  POSITION_CLOSE_ERROR = "POSITION_CLOSE_ERROR",
69
+ POSITION_METRICS_TIMEOUT = "POSITION_METRICS_TIMEOUT",
70
+ POSITION_METRICS_ERROR = "POSITION_METRICS_ERROR",
61
71
  ACCOUNT_METRICS_TIMEOUT = "ACCOUNT_METRICS_TIMEOUT",
62
72
  ACCOUNT_METRICS_ERROR = "ACCOUNT_METRICS_ERROR",
63
73
  ACCOUNT_POSITIONS_TIMEOUT = "ACCOUNT_POSITIONS_TIMEOUT",
64
74
  ACCOUNT_POSITIONS_ERROR = "ACCOUNT_POSITIONS_ERROR",
65
75
  TRADE_JOURNAL_ERROR = "TRADE_JOURNAL_ERROR",
76
+ TRADE_HISTORY_ERROR = "TRADE_HISTORY_ERROR",
66
77
  ASSESSMENTS_ERROR = "ASSESSMENTS_ERROR"
67
78
  }
68
79
  declare enum WS_MESSAGE {
@@ -71,11 +82,11 @@ declare enum WS_MESSAGE {
71
82
  AVAILABLE_WATCHLISTS = "AVAILABLE_WATCHLISTS",
72
83
  CHART_FEED_SUBTOPIC = "chartFeedSubtopic",
73
84
  INSTRUMENTS = "INSTRUMENTS",
74
- INSTRUMENT_METRICS = "INSTRUMENT_METRICS",
75
85
  LIMITS = "LIMITS",
76
86
  MESSAGE = "MESSAGE",
77
87
  ORDERS = "ORDERS",
78
88
  POSITIONS = "POSITIONS",
89
+ POSITION_METRICS = "POSITION_METRICS",
79
90
  POSITION_CASH_TRANSFERS = "POSITION_CASH_TRANSFERS",
80
91
  PRIVATE_LAYOUT_NAMES = "PRIVATE_LAYOUT_NAMES",
81
92
  SHARED_PROPERTIES_MESSAGE = "SHARED_PROPERTIES_MESSAGE",
@@ -94,6 +105,22 @@ declare class DxtradeError extends Error {
94
105
  }
95
106
 
96
107
  declare namespace Order {
108
+ interface Get {
109
+ account: string;
110
+ orderId: number;
111
+ orderCode: string;
112
+ version: number;
113
+ type: ORDER_TYPE;
114
+ instrument: string;
115
+ status: string;
116
+ finalStatus: boolean;
117
+ side: SIDE;
118
+ tif: TIF;
119
+ legs: Leg[];
120
+ issueTime: string;
121
+ transactionTime: string;
122
+ [key: string]: unknown;
123
+ }
97
124
  interface SubmitParams {
98
125
  symbol: string;
99
126
  side: SIDE;
@@ -207,6 +234,20 @@ interface WsPayload {
207
234
  }
208
235
 
209
236
  declare namespace Account {
237
+ interface TradeHistory {
238
+ orderId: number;
239
+ orderCode: string;
240
+ instrument: string;
241
+ side: string;
242
+ type: string;
243
+ status: string;
244
+ quantity: number;
245
+ filledQuantity: number;
246
+ price: number;
247
+ averagePrice: number;
248
+ time: string;
249
+ [key: string]: unknown;
250
+ }
210
251
  interface Metrics {
211
252
  availableFunds: number;
212
253
  marginCallLevel: number | string;
@@ -318,6 +359,14 @@ declare namespace Position {
318
359
  takeProfit: number | null;
319
360
  stopLoss: number | null;
320
361
  }
362
+ interface Metrics {
363
+ positionCode: string;
364
+ openPl: number;
365
+ openPlPerLot: number;
366
+ currentPrice: number;
367
+ convertedOpenPl: number;
368
+ [key: string]: unknown;
369
+ }
321
370
  interface Close {
322
371
  legs: {
323
372
  instrumentId: number;
@@ -398,12 +447,24 @@ declare class DxtradeClient {
398
447
  * Supports market, limit, and stop orders with optional stop loss and take profit.
399
448
  */
400
449
  submitOrder(params: Order.SubmitParams): Promise<Order.Update>;
450
+ /** Get all pending/open orders via WebSocket. */
451
+ getOrders(): Promise<Order.Get[]>;
452
+ /** Cancel a single pending order by its order chain ID. */
453
+ cancelOrder(orderChainId: number): Promise<void>;
454
+ /** Cancel all pending orders. */
455
+ cancelAllOrders(): Promise<void>;
401
456
  /** Get account metrics including equity, balance, margin, and open P&L. */
402
457
  getAccountMetrics(): Promise<Account.Metrics>;
403
458
  /** Get all open positions via WebSocket. */
404
459
  getPositions(): Promise<Position.Get[]>;
405
- /** Close a position. */
460
+ /**
461
+ * Close a position. Supports partial closes by specifying a quantity smaller than the full position size.
462
+ */
406
463
  closePosition(position: Position.Close): Promise<void>;
464
+ /** Close all open positions with market orders. */
465
+ closeAllPositions(): Promise<void>;
466
+ /** Get position-level P&L metrics via WebSocket. */
467
+ getPositionMetrics(): Promise<Position.Metrics[]>;
407
468
  /**
408
469
  * Fetch trade journal entries for a date range.
409
470
  * @param params.from - Start timestamp (Unix ms)
@@ -413,6 +474,15 @@ declare class DxtradeClient {
413
474
  from: number;
414
475
  to: number;
415
476
  }): Promise<any>;
477
+ /**
478
+ * Fetch trade history for a date range.
479
+ * @param params.from - Start timestamp (Unix ms)
480
+ * @param params.to - End timestamp (Unix ms)
481
+ */
482
+ getTradeHistory(params: {
483
+ from: number;
484
+ to: number;
485
+ }): Promise<Account.TradeHistory[]>;
416
486
  /** Get all available instruments, optionally filtered by partial match (e.g. `{ type: "FOREX" }`). */
417
487
  getInstruments(params?: Partial<Instrument.Info>): Promise<Instrument.Info[]>;
418
488
  /** Fetch PnL assessments for an instrument within a date range. */
package/dist/index.js CHANGED
@@ -62,9 +62,11 @@ var endpoints = {
62
62
  instrumentInfo: (base, symbol, tzOffset) => `${base}/api/instruments/info?symbol=${symbol}&timezoneOffset=${tzOffset}&withExDividends=true`,
63
63
  submitOrder: (base) => `${base}/api/orders/single`,
64
64
  closePosition: (base) => `${base}/api/positions/close`,
65
+ cancelOrder: (base, accountId, orderChainId) => `${base}/api/orders/cancel?accountId=${accountId}&orderChainId=${orderChainId}`,
65
66
  assessments: (base) => `${base}/api/assessments`,
66
67
  websocket: (base, atmosphereId) => `wss://${base.split("//")[1]}/client/connector` + websocketQuery(atmosphereId),
67
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=`,
68
70
  subscribeInstruments: (base) => `${base}/api/instruments/subscribeInstrumentSymbols`,
69
71
  charts: (base) => `${base}/api/charts`
70
72
  };
@@ -110,12 +112,18 @@ var ERROR = /* @__PURE__ */ ((ERROR2) => {
110
112
  ERROR2["OHLC_TIMEOUT"] = "OHLC_TIMEOUT";
111
113
  ERROR2["OHLC_ERROR"] = "OHLC_ERROR";
112
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";
113
118
  ERROR2["POSITION_CLOSE_ERROR"] = "POSITION_CLOSE_ERROR";
119
+ ERROR2["POSITION_METRICS_TIMEOUT"] = "POSITION_METRICS_TIMEOUT";
120
+ ERROR2["POSITION_METRICS_ERROR"] = "POSITION_METRICS_ERROR";
114
121
  ERROR2["ACCOUNT_METRICS_TIMEOUT"] = "ACCOUNT_METRICS_TIMEOUT";
115
122
  ERROR2["ACCOUNT_METRICS_ERROR"] = "ACCOUNT_METRICS_ERROR";
116
123
  ERROR2["ACCOUNT_POSITIONS_TIMEOUT"] = "ACCOUNT_POSITIONS_TIMEOUT";
117
124
  ERROR2["ACCOUNT_POSITIONS_ERROR"] = "ACCOUNT_POSITIONS_ERROR";
118
125
  ERROR2["TRADE_JOURNAL_ERROR"] = "TRADE_JOURNAL_ERROR";
126
+ ERROR2["TRADE_HISTORY_ERROR"] = "TRADE_HISTORY_ERROR";
119
127
  ERROR2["ASSESSMENTS_ERROR"] = "ASSESSMENTS_ERROR";
120
128
  return ERROR2;
121
129
  })(ERROR || {});
@@ -125,11 +133,11 @@ var WS_MESSAGE = /* @__PURE__ */ ((WS_MESSAGE2) => {
125
133
  WS_MESSAGE2["AVAILABLE_WATCHLISTS"] = "AVAILABLE_WATCHLISTS";
126
134
  WS_MESSAGE2["CHART_FEED_SUBTOPIC"] = "chartFeedSubtopic";
127
135
  WS_MESSAGE2["INSTRUMENTS"] = "INSTRUMENTS";
128
- WS_MESSAGE2["INSTRUMENT_METRICS"] = "INSTRUMENT_METRICS";
129
136
  WS_MESSAGE2["LIMITS"] = "LIMITS";
130
137
  WS_MESSAGE2["MESSAGE"] = "MESSAGE";
131
138
  WS_MESSAGE2["ORDERS"] = "ORDERS";
132
139
  WS_MESSAGE2["POSITIONS"] = "POSITIONS";
140
+ WS_MESSAGE2["POSITION_METRICS"] = "POSITION_METRICS";
133
141
  WS_MESSAGE2["POSITION_CASH_TRANSFERS"] = "POSITION_CASH_TRANSFERS";
134
142
  WS_MESSAGE2["PRIVATE_LAYOUT_NAMES"] = "PRIVATE_LAYOUT_NAMES";
135
143
  WS_MESSAGE2["SHARED_PROPERTIES_MESSAGE"] = "SHARED_PROPERTIES_MESSAGE";
@@ -207,7 +215,8 @@ async function retryRequest(config, retries = 3) {
207
215
  return await (0, import_axios.default)(config);
208
216
  } catch (error) {
209
217
  const message = error instanceof Error ? error.message : "Unknown error";
210
- console.warn(`[dxtrade-api] Attempt ${attempt} failed: ${message}`);
218
+ console.warn(`[dxtrade-api] Attempt ${attempt} failed: ${message}`, config.url);
219
+ if ((0, import_axios.isAxiosError)(error) && error.response?.status === 429) throw error;
211
220
  if (attempt === retries) throw error;
212
221
  await new Promise((res) => setTimeout(res, 1e3 * attempt));
213
222
  }
@@ -279,6 +288,32 @@ async function getAccountMetrics(ctx, timeout = 3e4) {
279
288
  });
280
289
  });
281
290
  }
291
+ async function getTradeHistory(ctx, params) {
292
+ ctx.ensureSession();
293
+ try {
294
+ const cookieStr = Cookies.serialize(ctx.cookies);
295
+ const response = await retryRequest(
296
+ {
297
+ method: "GET",
298
+ url: endpoints.tradeHistory(ctx.broker, params),
299
+ headers: { ...baseHeaders(), Cookie: cookieStr }
300
+ },
301
+ ctx.retries
302
+ );
303
+ if (response.status === 200) {
304
+ const setCookies = response.headers["set-cookie"] ?? [];
305
+ const incoming = Cookies.parse(setCookies);
306
+ ctx.cookies = Cookies.merge(ctx.cookies, incoming);
307
+ return response.data;
308
+ } else {
309
+ ctx.throwError("TRADE_HISTORY_ERROR" /* TRADE_HISTORY_ERROR */, `Trade history failed: ${response.status}`);
310
+ }
311
+ } catch (error) {
312
+ if (error instanceof DxtradeError) throw error;
313
+ const message = error instanceof Error ? error.message : "Unknown error";
314
+ ctx.throwError("TRADE_HISTORY_ERROR" /* TRADE_HISTORY_ERROR */, `Trade history error: ${message}`);
315
+ }
316
+ }
282
317
  async function getTradeJournal(ctx, params) {
283
318
  ctx.ensureSession();
284
319
  try {
@@ -630,6 +665,61 @@ function createOrderListener(wsUrl, cookieStr, timeout = 3e4, debug = false) {
630
665
  });
631
666
  return { promise, ready };
632
667
  }
668
+ async function getOrders(ctx, timeout = 3e4) {
669
+ ctx.ensureSession();
670
+ const wsUrl = endpoints.websocket(ctx.broker, ctx.atmosphereId);
671
+ const cookieStr = Cookies.serialize(ctx.cookies);
672
+ return new Promise((resolve, reject) => {
673
+ const ws = new import_ws5.default(wsUrl, { headers: { Cookie: cookieStr } });
674
+ const timer = setTimeout(() => {
675
+ ws.close();
676
+ reject(new DxtradeError("ORDERS_TIMEOUT" /* ORDERS_TIMEOUT */, "Orders request timed out"));
677
+ }, timeout);
678
+ ws.on("message", (data) => {
679
+ const msg = parseWsData(data);
680
+ if (shouldLog(msg, ctx.debug)) debugLog(msg);
681
+ if (typeof msg === "string") return;
682
+ if (msg.type === "ORDERS" /* ORDERS */) {
683
+ clearTimeout(timer);
684
+ ws.close();
685
+ resolve(msg.body);
686
+ }
687
+ });
688
+ ws.on("error", (error) => {
689
+ clearTimeout(timer);
690
+ ws.close();
691
+ reject(new DxtradeError("ORDERS_ERROR" /* ORDERS_ERROR */, `Orders error: ${error.message}`));
692
+ });
693
+ });
694
+ }
695
+ async function cancelOrder(ctx, orderChainId) {
696
+ ctx.ensureSession();
697
+ const accountId = ctx.accountId ?? ctx.config.accountId;
698
+ if (!accountId) {
699
+ ctx.throwError("CANCEL_ORDER_ERROR" /* CANCEL_ORDER_ERROR */, "accountId is required to cancel an order");
700
+ }
701
+ try {
702
+ await retryRequest(
703
+ {
704
+ method: "DELETE",
705
+ url: endpoints.cancelOrder(ctx.broker, accountId, orderChainId),
706
+ headers: authHeaders(ctx.csrf, Cookies.serialize(ctx.cookies))
707
+ },
708
+ ctx.retries
709
+ );
710
+ } catch (error) {
711
+ if (error instanceof DxtradeError) throw error;
712
+ const message = error instanceof Error ? error.response?.data?.message ?? error.message : "Unknown error";
713
+ ctx.throwError("CANCEL_ORDER_ERROR" /* CANCEL_ORDER_ERROR */, `Cancel order error: ${message}`);
714
+ }
715
+ }
716
+ async function cancelAllOrders(ctx) {
717
+ const orders = await getOrders(ctx);
718
+ const pending = orders.filter((o) => !o.finalStatus);
719
+ for (const order of pending) {
720
+ await cancelOrder(ctx, order.orderId);
721
+ }
722
+ }
633
723
  async function submitOrder(ctx, params) {
634
724
  ctx.ensureSession();
635
725
  const {
@@ -752,6 +842,54 @@ async function getPositions(ctx) {
752
842
  });
753
843
  });
754
844
  }
845
+ async function getPositionMetrics(ctx, timeout = 3e4) {
846
+ ctx.ensureSession();
847
+ const wsUrl = endpoints.websocket(ctx.broker, ctx.atmosphereId);
848
+ const cookieStr = Cookies.serialize(ctx.cookies);
849
+ return new Promise((resolve, reject) => {
850
+ const ws = new import_ws6.default(wsUrl, { headers: { Cookie: cookieStr } });
851
+ const timer = setTimeout(() => {
852
+ ws.close();
853
+ reject(new DxtradeError("POSITION_METRICS_TIMEOUT" /* POSITION_METRICS_TIMEOUT */, "Position metrics timed out"));
854
+ }, timeout);
855
+ ws.on("message", (data) => {
856
+ const msg = parseWsData(data);
857
+ if (shouldLog(msg, ctx.debug)) debugLog(msg);
858
+ if (typeof msg === "string") return;
859
+ if (msg.type === "POSITION_METRICS" /* POSITION_METRICS */) {
860
+ clearTimeout(timer);
861
+ ws.close();
862
+ resolve(msg.body);
863
+ }
864
+ });
865
+ ws.on("error", (error) => {
866
+ clearTimeout(timer);
867
+ ws.close();
868
+ reject(new DxtradeError("POSITION_METRICS_ERROR" /* POSITION_METRICS_ERROR */, `Position metrics error: ${error.message}`));
869
+ });
870
+ });
871
+ }
872
+ async function closeAllPositions(ctx) {
873
+ const positions = await getPositions(ctx);
874
+ for (const pos of positions) {
875
+ const closeData = {
876
+ legs: [
877
+ {
878
+ instrumentId: pos.positionKey.instrumentId,
879
+ positionCode: pos.positionKey.positionCode,
880
+ positionEffect: "CLOSING",
881
+ ratioQuantity: 1,
882
+ symbol: pos.positionKey.positionCode
883
+ }
884
+ ],
885
+ limitPrice: 0,
886
+ orderType: "MARKET",
887
+ quantity: -pos.quantity,
888
+ timeInForce: "GTC"
889
+ };
890
+ await closePosition(ctx, closeData);
891
+ }
892
+ }
755
893
  async function closePosition(ctx, data) {
756
894
  try {
757
895
  await retryRequest(
@@ -790,7 +928,7 @@ function waitForHandshake(wsUrl, cookieStr, timeout = 3e4, debug = false) {
790
928
  if (msg.accountId) {
791
929
  clearTimeout(timer);
792
930
  ws.close();
793
- resolve(atmosphereId);
931
+ resolve({ atmosphereId, accountId: msg.accountId });
794
932
  }
795
933
  });
796
934
  ws.on("error", (error) => {
@@ -809,7 +947,11 @@ async function login(ctx) {
809
947
  data: {
810
948
  username: ctx.config.username,
811
949
  password: ctx.config.password,
812
- domain: ctx.config.broker
950
+ // TODO:: take a look at this below, domain nor vendor seems required. it works if i comment out both.
951
+ // however i still use it since i see brokers use it as well in the login endpoint.
952
+ // domain: ctx.config.broker,
953
+ vendor: ctx.config.broker
954
+ // END TODO::
813
955
  },
814
956
  headers: { "Content-Type": "application/json" }
815
957
  },
@@ -875,15 +1017,19 @@ async function connect(ctx) {
875
1017
  await fetchCsrf(ctx);
876
1018
  if (ctx.debug) clearDebugLog();
877
1019
  const cookieStr = Cookies.serialize(ctx.cookies);
878
- ctx.atmosphereId = await waitForHandshake(endpoints.websocket(ctx.broker), cookieStr, 3e4, ctx.debug);
1020
+ const handshake = await waitForHandshake(endpoints.websocket(ctx.broker), cookieStr, 3e4, ctx.debug);
1021
+ ctx.atmosphereId = handshake.atmosphereId;
1022
+ ctx.accountId = handshake.accountId;
879
1023
  if (ctx.config.accountId) {
880
1024
  await switchAccount(ctx, ctx.config.accountId);
881
- ctx.atmosphereId = await waitForHandshake(
1025
+ const reconnect = await waitForHandshake(
882
1026
  endpoints.websocket(ctx.broker, ctx.atmosphereId),
883
1027
  Cookies.serialize(ctx.cookies),
884
1028
  3e4,
885
1029
  ctx.debug
886
1030
  );
1031
+ ctx.atmosphereId = reconnect.atmosphereId;
1032
+ ctx.accountId = reconnect.accountId;
887
1033
  }
888
1034
  }
889
1035
 
@@ -897,6 +1043,7 @@ var DxtradeClient = class {
897
1043
  callbacks,
898
1044
  cookies: {},
899
1045
  csrf: null,
1046
+ accountId: config.accountId ?? null,
900
1047
  atmosphereId: null,
901
1048
  broker: config.broker,
902
1049
  retries: config.retries ?? 3,
@@ -951,6 +1098,18 @@ var DxtradeClient = class {
951
1098
  async submitOrder(params) {
952
1099
  return submitOrder(this._ctx, params);
953
1100
  }
1101
+ /** Get all pending/open orders via WebSocket. */
1102
+ async getOrders() {
1103
+ return getOrders(this._ctx);
1104
+ }
1105
+ /** Cancel a single pending order by its order chain ID. */
1106
+ async cancelOrder(orderChainId) {
1107
+ return cancelOrder(this._ctx, orderChainId);
1108
+ }
1109
+ /** Cancel all pending orders. */
1110
+ async cancelAllOrders() {
1111
+ return cancelAllOrders(this._ctx);
1112
+ }
954
1113
  /** Get account metrics including equity, balance, margin, and open P&L. */
955
1114
  async getAccountMetrics() {
956
1115
  return getAccountMetrics(this._ctx);
@@ -959,10 +1118,20 @@ var DxtradeClient = class {
959
1118
  async getPositions() {
960
1119
  return getPositions(this._ctx);
961
1120
  }
962
- /** Close a position. */
1121
+ /**
1122
+ * Close a position. Supports partial closes by specifying a quantity smaller than the full position size.
1123
+ */
963
1124
  async closePosition(position) {
964
1125
  return closePosition(this._ctx, position);
965
1126
  }
1127
+ /** Close all open positions with market orders. */
1128
+ async closeAllPositions() {
1129
+ return closeAllPositions(this._ctx);
1130
+ }
1131
+ /** Get position-level P&L metrics via WebSocket. */
1132
+ async getPositionMetrics() {
1133
+ return getPositionMetrics(this._ctx);
1134
+ }
966
1135
  /**
967
1136
  * Fetch trade journal entries for a date range.
968
1137
  * @param params.from - Start timestamp (Unix ms)
@@ -971,6 +1140,14 @@ var DxtradeClient = class {
971
1140
  async getTradeJournal(params) {
972
1141
  return getTradeJournal(this._ctx, params);
973
1142
  }
1143
+ /**
1144
+ * Fetch trade history for a date range.
1145
+ * @param params.from - Start timestamp (Unix ms)
1146
+ * @param params.to - End timestamp (Unix ms)
1147
+ */
1148
+ async getTradeHistory(params) {
1149
+ return getTradeHistory(this._ctx, params);
1150
+ }
974
1151
  /** Get all available instruments, optionally filtered by partial match (e.g. `{ type: "FOREX" }`). */
975
1152
  async getInstruments(params = {}) {
976
1153
  return getInstruments(this._ctx, params);