@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 +15 -5
- package/dist/index.d.mts +72 -2
- package/dist/index.d.ts +72 -2
- package/dist/index.js +184 -7
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +185 -8
- package/dist/index.mjs.map +1 -1
- package/llms.txt +7 -1
- package/package.json +5 -2
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]
|
|
25
|
-
- [x]
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
/**
|
|
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);
|