@danielgroen/dxtrade-api 1.0.20 → 1.0.21
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 +3 -0
- package/dist/index.d.mts +72 -2
- package/dist/index.d.ts +72 -2
- package/dist/index.js +215 -43
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +214 -43
- package/dist/index.mjs.map +1 -1
- package/llms.txt +3 -0
- package/package.json +2 -1
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
|
-
|
|
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}`,
|
|
@@ -59,8 +63,10 @@ var endpoints = {
|
|
|
59
63
|
submitOrder: (base) => `${base}/api/orders/single`,
|
|
60
64
|
closePosition: (base) => `${base}/api/positions/close`,
|
|
61
65
|
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}
|
|
66
|
+
websocket: (base, atmosphereId) => `wss://${base.split("//")[1]}/client/connector` + websocketQuery(atmosphereId),
|
|
67
|
+
tradeJournal: (base, params) => `${base}/api/tradejournal?from=${params.from}&to=${params.to}`,
|
|
68
|
+
subscribeInstruments: (base) => `${base}/api/instruments/subscribeInstrumentSymbols`,
|
|
69
|
+
charts: (base) => `${base}/api/charts`
|
|
64
70
|
};
|
|
65
71
|
|
|
66
72
|
// src/constants/enums.ts
|
|
@@ -86,11 +92,40 @@ var TIF = /* @__PURE__ */ ((TIF2) => {
|
|
|
86
92
|
TIF2["GTD"] = "GTD";
|
|
87
93
|
return TIF2;
|
|
88
94
|
})(TIF || {});
|
|
95
|
+
var ERROR = /* @__PURE__ */ ((ERROR2) => {
|
|
96
|
+
ERROR2["NO_SESSION"] = "NO_SESSION";
|
|
97
|
+
ERROR2["LOGIN_FAILED"] = "LOGIN_FAILED";
|
|
98
|
+
ERROR2["LOGIN_ERROR"] = "LOGIN_ERROR";
|
|
99
|
+
ERROR2["CSRF_NOT_FOUND"] = "CSRF_NOT_FOUND";
|
|
100
|
+
ERROR2["CSRF_ERROR"] = "CSRF_ERROR";
|
|
101
|
+
ERROR2["ACCOUNT_SWITCH_ERROR"] = "ACCOUNT_SWITCH_ERROR";
|
|
102
|
+
ERROR2["NO_SUGGESTIONS"] = "NO_SUGGESTIONS";
|
|
103
|
+
ERROR2["SUGGEST_ERROR"] = "SUGGEST_ERROR";
|
|
104
|
+
ERROR2["NO_SYMBOL_INFO"] = "NO_SYMBOL_INFO";
|
|
105
|
+
ERROR2["SYMBOL_INFO_ERROR"] = "SYMBOL_INFO_ERROR";
|
|
106
|
+
ERROR2["INSTRUMENTS_TIMEOUT"] = "INSTRUMENTS_TIMEOUT";
|
|
107
|
+
ERROR2["INSTRUMENTS_ERROR"] = "INSTRUMENTS_ERROR";
|
|
108
|
+
ERROR2["LIMITS_TIMEOUT"] = "LIMITS_TIMEOUT";
|
|
109
|
+
ERROR2["LIMITS_ERROR"] = "LIMITS_ERROR";
|
|
110
|
+
ERROR2["OHLC_TIMEOUT"] = "OHLC_TIMEOUT";
|
|
111
|
+
ERROR2["OHLC_ERROR"] = "OHLC_ERROR";
|
|
112
|
+
ERROR2["ORDER_ERROR"] = "ORDER_ERROR";
|
|
113
|
+
ERROR2["POSITION_CLOSE_ERROR"] = "POSITION_CLOSE_ERROR";
|
|
114
|
+
ERROR2["ACCOUNT_METRICS_TIMEOUT"] = "ACCOUNT_METRICS_TIMEOUT";
|
|
115
|
+
ERROR2["ACCOUNT_METRICS_ERROR"] = "ACCOUNT_METRICS_ERROR";
|
|
116
|
+
ERROR2["ACCOUNT_POSITIONS_TIMEOUT"] = "ACCOUNT_POSITIONS_TIMEOUT";
|
|
117
|
+
ERROR2["ACCOUNT_POSITIONS_ERROR"] = "ACCOUNT_POSITIONS_ERROR";
|
|
118
|
+
ERROR2["TRADE_JOURNAL_ERROR"] = "TRADE_JOURNAL_ERROR";
|
|
119
|
+
ERROR2["ASSESSMENTS_ERROR"] = "ASSESSMENTS_ERROR";
|
|
120
|
+
return ERROR2;
|
|
121
|
+
})(ERROR || {});
|
|
89
122
|
var WS_MESSAGE = /* @__PURE__ */ ((WS_MESSAGE2) => {
|
|
90
123
|
WS_MESSAGE2["ACCOUNT_METRICS"] = "ACCOUNT_METRICS";
|
|
91
124
|
WS_MESSAGE2["ACCOUNTS"] = "ACCOUNTS";
|
|
92
125
|
WS_MESSAGE2["AVAILABLE_WATCHLISTS"] = "AVAILABLE_WATCHLISTS";
|
|
126
|
+
WS_MESSAGE2["CHART_FEED_SUBTOPIC"] = "chartFeedSubtopic";
|
|
93
127
|
WS_MESSAGE2["INSTRUMENTS"] = "INSTRUMENTS";
|
|
128
|
+
WS_MESSAGE2["INSTRUMENT_METRICS"] = "INSTRUMENT_METRICS";
|
|
94
129
|
WS_MESSAGE2["LIMITS"] = "LIMITS";
|
|
95
130
|
WS_MESSAGE2["MESSAGE"] = "MESSAGE";
|
|
96
131
|
WS_MESSAGE2["ORDERS"] = "ORDERS";
|
|
@@ -98,9 +133,16 @@ var WS_MESSAGE = /* @__PURE__ */ ((WS_MESSAGE2) => {
|
|
|
98
133
|
WS_MESSAGE2["POSITION_CASH_TRANSFERS"] = "POSITION_CASH_TRANSFERS";
|
|
99
134
|
WS_MESSAGE2["PRIVATE_LAYOUT_NAMES"] = "PRIVATE_LAYOUT_NAMES";
|
|
100
135
|
WS_MESSAGE2["SHARED_PROPERTIES_MESSAGE"] = "SHARED_PROPERTIES_MESSAGE";
|
|
136
|
+
WS_MESSAGE2["TRADE_STATUSES"] = "TRADE_STATUSES";
|
|
101
137
|
WS_MESSAGE2["USER_LOGIN_INFO"] = "USER_LOGIN_INFO";
|
|
102
138
|
return WS_MESSAGE2;
|
|
103
139
|
})(WS_MESSAGE || {});
|
|
140
|
+
((WS_MESSAGE2) => {
|
|
141
|
+
let SUBTOPIC;
|
|
142
|
+
((SUBTOPIC2) => {
|
|
143
|
+
SUBTOPIC2["BIG_CHART_COMPONENT"] = "BigChartComponentPresenter-4";
|
|
144
|
+
})(SUBTOPIC = WS_MESSAGE2.SUBTOPIC || (WS_MESSAGE2.SUBTOPIC = {}));
|
|
145
|
+
})(WS_MESSAGE || (WS_MESSAGE = {}));
|
|
104
146
|
|
|
105
147
|
// src/constants/errors.ts
|
|
106
148
|
var DxtradeError = class extends Error {
|
|
@@ -189,6 +231,14 @@ function debugLog(msg) {
|
|
|
189
231
|
function clearDebugLog() {
|
|
190
232
|
(0, import_fs.writeFileSync)(DEBUG_LOG, "");
|
|
191
233
|
}
|
|
234
|
+
function parseAtmosphereId(data) {
|
|
235
|
+
const raw = data.toString();
|
|
236
|
+
const parts = raw.split("|");
|
|
237
|
+
if (parts.length >= 2 && /^[0-9a-f-]{36}$/.test(parts[1])) {
|
|
238
|
+
return parts[1];
|
|
239
|
+
}
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
192
242
|
function parseWsData(data) {
|
|
193
243
|
const raw = data.toString();
|
|
194
244
|
const pipeIndex = raw.indexOf("|");
|
|
@@ -203,13 +253,13 @@ function parseWsData(data) {
|
|
|
203
253
|
// src/domains/account/account.ts
|
|
204
254
|
async function getAccountMetrics(ctx, timeout = 3e4) {
|
|
205
255
|
ctx.ensureSession();
|
|
206
|
-
const wsUrl = endpoints.websocket(ctx.broker);
|
|
256
|
+
const wsUrl = endpoints.websocket(ctx.broker, ctx.atmosphereId);
|
|
207
257
|
const cookieStr = Cookies.serialize(ctx.cookies);
|
|
208
258
|
return new Promise((resolve, reject) => {
|
|
209
259
|
const ws = new import_ws.default(wsUrl, { headers: { Cookie: cookieStr } });
|
|
210
260
|
const timer = setTimeout(() => {
|
|
211
261
|
ws.close();
|
|
212
|
-
reject(new DxtradeError("ACCOUNT_METRICS_TIMEOUT"
|
|
262
|
+
reject(new DxtradeError("ACCOUNT_METRICS_TIMEOUT" /* ACCOUNT_METRICS_TIMEOUT */, "Account metrics timed out"));
|
|
213
263
|
}, timeout);
|
|
214
264
|
ws.on("message", (data) => {
|
|
215
265
|
const msg = parseWsData(data);
|
|
@@ -225,7 +275,7 @@ async function getAccountMetrics(ctx, timeout = 3e4) {
|
|
|
225
275
|
ws.on("error", (error) => {
|
|
226
276
|
clearTimeout(timer);
|
|
227
277
|
ws.close();
|
|
228
|
-
reject(new DxtradeError("ACCOUNT_METRICS_ERROR"
|
|
278
|
+
reject(new DxtradeError("ACCOUNT_METRICS_ERROR" /* ACCOUNT_METRICS_ERROR */, `Account metrics error: ${error.message}`));
|
|
229
279
|
});
|
|
230
280
|
});
|
|
231
281
|
}
|
|
@@ -247,12 +297,12 @@ async function getTradeJournal(ctx, params) {
|
|
|
247
297
|
ctx.cookies = Cookies.merge(ctx.cookies, incoming);
|
|
248
298
|
return response.data;
|
|
249
299
|
} else {
|
|
250
|
-
ctx.throwError("TRADE_JOURNAL_ERROR"
|
|
300
|
+
ctx.throwError("TRADE_JOURNAL_ERROR" /* TRADE_JOURNAL_ERROR */, `Login failed: ${response.status}`);
|
|
251
301
|
}
|
|
252
302
|
} catch (error) {
|
|
253
303
|
if (error instanceof DxtradeError) throw error;
|
|
254
304
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
255
|
-
ctx.throwError("TRADE_JOURNAL_ERROR"
|
|
305
|
+
ctx.throwError("TRADE_JOURNAL_ERROR" /* TRADE_JOURNAL_ERROR */, `Trade journal error: ${message}`);
|
|
256
306
|
}
|
|
257
307
|
}
|
|
258
308
|
|
|
@@ -278,7 +328,7 @@ async function getAssessments(ctx, params) {
|
|
|
278
328
|
} catch (error) {
|
|
279
329
|
if (error instanceof DxtradeError) throw error;
|
|
280
330
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
281
|
-
ctx.throwError("ASSESSMENTS_ERROR"
|
|
331
|
+
ctx.throwError("ASSESSMENTS_ERROR" /* ASSESSMENTS_ERROR */, `Error fetching assessments: ${message}`);
|
|
282
332
|
}
|
|
283
333
|
}
|
|
284
334
|
|
|
@@ -286,13 +336,13 @@ async function getAssessments(ctx, params) {
|
|
|
286
336
|
var import_ws2 = __toESM(require("ws"));
|
|
287
337
|
async function getInstruments(ctx, params = {}, timeout = 3e4) {
|
|
288
338
|
ctx.ensureSession();
|
|
289
|
-
const wsUrl = endpoints.websocket(ctx.broker);
|
|
339
|
+
const wsUrl = endpoints.websocket(ctx.broker, ctx.atmosphereId);
|
|
290
340
|
const cookieStr = Cookies.serialize(ctx.cookies);
|
|
291
341
|
return new Promise((resolve, reject) => {
|
|
292
342
|
const ws = new import_ws2.default(wsUrl, { headers: { Cookie: cookieStr } });
|
|
293
343
|
const timer = setTimeout(() => {
|
|
294
344
|
ws.close();
|
|
295
|
-
reject(new DxtradeError("INSTRUMENTS_TIMEOUT"
|
|
345
|
+
reject(new DxtradeError("INSTRUMENTS_TIMEOUT" /* INSTRUMENTS_TIMEOUT */, "Instruments request timed out"));
|
|
296
346
|
}, timeout);
|
|
297
347
|
let instruments = [];
|
|
298
348
|
let settleTimer = null;
|
|
@@ -322,17 +372,115 @@ async function getInstruments(ctx, params = {}, timeout = 3e4) {
|
|
|
322
372
|
ws.on("error", (error) => {
|
|
323
373
|
clearTimeout(timer);
|
|
324
374
|
ws.close();
|
|
325
|
-
reject(new DxtradeError("INSTRUMENTS_ERROR"
|
|
375
|
+
reject(new DxtradeError("INSTRUMENTS_ERROR" /* INSTRUMENTS_ERROR */, `Instruments error: ${error.message}`));
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// src/domains/ohlc/ohlc.ts
|
|
381
|
+
var import_ws3 = __toESM(require("ws"));
|
|
382
|
+
async function getOHLC(ctx, params, timeout = 3e4) {
|
|
383
|
+
ctx.ensureSession();
|
|
384
|
+
const { symbol, resolution = 60, range = 432e3, maxBars = 3500, priceField = "bid" } = params;
|
|
385
|
+
const wsUrl = endpoints.websocket(ctx.broker, ctx.atmosphereId);
|
|
386
|
+
const cookieStr = Cookies.serialize(ctx.cookies);
|
|
387
|
+
const headers = authHeaders(ctx.csrf, cookieStr);
|
|
388
|
+
return new Promise((resolve, reject) => {
|
|
389
|
+
const ws = new import_ws3.default(wsUrl, { headers: { Cookie: cookieStr } });
|
|
390
|
+
const bars = [];
|
|
391
|
+
let putsSent = false;
|
|
392
|
+
let initSettleTimer = null;
|
|
393
|
+
let barSettleTimer = null;
|
|
394
|
+
const timer = setTimeout(() => {
|
|
395
|
+
ws.close();
|
|
396
|
+
reject(new DxtradeError("OHLC_TIMEOUT" /* OHLC_TIMEOUT */, "OHLC data timed out"));
|
|
397
|
+
}, timeout);
|
|
398
|
+
function cleanup() {
|
|
399
|
+
clearTimeout(timer);
|
|
400
|
+
if (initSettleTimer) clearTimeout(initSettleTimer);
|
|
401
|
+
if (barSettleTimer) clearTimeout(barSettleTimer);
|
|
402
|
+
ws.close();
|
|
403
|
+
}
|
|
404
|
+
async function sendPuts() {
|
|
405
|
+
putsSent = true;
|
|
406
|
+
try {
|
|
407
|
+
await retryRequest(
|
|
408
|
+
{
|
|
409
|
+
method: "PUT",
|
|
410
|
+
url: endpoints.subscribeInstruments(ctx.broker),
|
|
411
|
+
data: { instruments: [symbol] },
|
|
412
|
+
headers
|
|
413
|
+
},
|
|
414
|
+
ctx.retries
|
|
415
|
+
);
|
|
416
|
+
await retryRequest(
|
|
417
|
+
{
|
|
418
|
+
method: "PUT",
|
|
419
|
+
url: endpoints.charts(ctx.broker),
|
|
420
|
+
data: {
|
|
421
|
+
chartIds: [],
|
|
422
|
+
requests: [
|
|
423
|
+
{
|
|
424
|
+
aggregationPeriodSeconds: resolution,
|
|
425
|
+
extendedSession: true,
|
|
426
|
+
forexPriceField: priceField,
|
|
427
|
+
id: 0,
|
|
428
|
+
maxBarsCount: maxBars,
|
|
429
|
+
range,
|
|
430
|
+
studySubscription: [],
|
|
431
|
+
subtopic: WS_MESSAGE.SUBTOPIC.BIG_CHART_COMPONENT,
|
|
432
|
+
symbol
|
|
433
|
+
}
|
|
434
|
+
]
|
|
435
|
+
},
|
|
436
|
+
headers
|
|
437
|
+
},
|
|
438
|
+
ctx.retries
|
|
439
|
+
);
|
|
440
|
+
} catch (error) {
|
|
441
|
+
cleanup();
|
|
442
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
443
|
+
reject(new DxtradeError("OHLC_ERROR" /* OHLC_ERROR */, `Error fetching OHLC data: ${message}`));
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
ws.on("message", (data) => {
|
|
447
|
+
const msg = parseWsData(data);
|
|
448
|
+
if (shouldLog(msg, ctx.debug)) debugLog(msg);
|
|
449
|
+
if (typeof msg === "string") return;
|
|
450
|
+
if (!putsSent) {
|
|
451
|
+
if (initSettleTimer) clearTimeout(initSettleTimer);
|
|
452
|
+
initSettleTimer = setTimeout(() => sendPuts(), 1e3);
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
const body = msg.body;
|
|
456
|
+
if (body?.subtopic !== WS_MESSAGE.SUBTOPIC.BIG_CHART_COMPONENT) return;
|
|
457
|
+
if (Array.isArray(body.data)) {
|
|
458
|
+
bars.push(...body.data);
|
|
459
|
+
}
|
|
460
|
+
if (barSettleTimer) clearTimeout(barSettleTimer);
|
|
461
|
+
if (body.snapshotEnd) {
|
|
462
|
+
cleanup();
|
|
463
|
+
resolve(bars);
|
|
464
|
+
} else {
|
|
465
|
+
barSettleTimer = setTimeout(() => {
|
|
466
|
+
cleanup();
|
|
467
|
+
resolve(bars);
|
|
468
|
+
}, 2e3);
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
ws.on("error", (error) => {
|
|
472
|
+
cleanup();
|
|
473
|
+
reject(new DxtradeError("OHLC_ERROR" /* OHLC_ERROR */, `OHLC WebSocket error: ${error.message}`));
|
|
326
474
|
});
|
|
327
475
|
});
|
|
328
476
|
}
|
|
329
477
|
|
|
330
478
|
// src/domains/order/order.ts
|
|
331
479
|
var import_crypto = __toESM(require("crypto"));
|
|
332
|
-
var
|
|
480
|
+
var import_ws5 = __toESM(require("ws"));
|
|
333
481
|
|
|
334
482
|
// src/domains/symbol/symbol.ts
|
|
335
|
-
var
|
|
483
|
+
var import_ws4 = __toESM(require("ws"));
|
|
336
484
|
async function getSymbolSuggestions(ctx, text) {
|
|
337
485
|
ctx.ensureSession();
|
|
338
486
|
try {
|
|
@@ -347,13 +495,13 @@ async function getSymbolSuggestions(ctx, text) {
|
|
|
347
495
|
);
|
|
348
496
|
const suggests = response.data?.suggests;
|
|
349
497
|
if (!suggests?.length) {
|
|
350
|
-
ctx.throwError("NO_SUGGESTIONS"
|
|
498
|
+
ctx.throwError("NO_SUGGESTIONS" /* NO_SUGGESTIONS */, "No symbol suggestions found");
|
|
351
499
|
}
|
|
352
500
|
return suggests;
|
|
353
501
|
} catch (error) {
|
|
354
502
|
if (error instanceof DxtradeError) throw error;
|
|
355
503
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
356
|
-
ctx.throwError("SUGGEST_ERROR"
|
|
504
|
+
ctx.throwError("SUGGEST_ERROR" /* SUGGEST_ERROR */, `Error getting symbol suggestions: ${message}`);
|
|
357
505
|
}
|
|
358
506
|
}
|
|
359
507
|
async function getSymbolInfo(ctx, symbol) {
|
|
@@ -370,24 +518,24 @@ async function getSymbolInfo(ctx, symbol) {
|
|
|
370
518
|
ctx.retries
|
|
371
519
|
);
|
|
372
520
|
if (!response.data) {
|
|
373
|
-
ctx.throwError("NO_SYMBOL_INFO"
|
|
521
|
+
ctx.throwError("NO_SYMBOL_INFO" /* NO_SYMBOL_INFO */, "No symbol info returned");
|
|
374
522
|
}
|
|
375
523
|
return response.data;
|
|
376
524
|
} catch (error) {
|
|
377
525
|
if (error instanceof DxtradeError) throw error;
|
|
378
526
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
379
|
-
ctx.throwError("SYMBOL_INFO_ERROR"
|
|
527
|
+
ctx.throwError("SYMBOL_INFO_ERROR" /* SYMBOL_INFO_ERROR */, `Error getting symbol info: ${message}`);
|
|
380
528
|
}
|
|
381
529
|
}
|
|
382
530
|
async function getSymbolLimits(ctx, timeout = 3e4) {
|
|
383
531
|
ctx.ensureSession();
|
|
384
|
-
const wsUrl = endpoints.websocket(ctx.broker);
|
|
532
|
+
const wsUrl = endpoints.websocket(ctx.broker, ctx.atmosphereId);
|
|
385
533
|
const cookieStr = Cookies.serialize(ctx.cookies);
|
|
386
534
|
return new Promise((resolve, reject) => {
|
|
387
|
-
const ws = new
|
|
535
|
+
const ws = new import_ws4.default(wsUrl, { headers: { Cookie: cookieStr } });
|
|
388
536
|
const timer = setTimeout(() => {
|
|
389
537
|
ws.close();
|
|
390
|
-
reject(new DxtradeError("LIMITS_TIMEOUT"
|
|
538
|
+
reject(new DxtradeError("LIMITS_TIMEOUT" /* LIMITS_TIMEOUT */, "Symbol limits request timed out"));
|
|
391
539
|
}, timeout);
|
|
392
540
|
let limits = [];
|
|
393
541
|
let settleTimer = null;
|
|
@@ -410,14 +558,14 @@ async function getSymbolLimits(ctx, timeout = 3e4) {
|
|
|
410
558
|
ws.on("error", (error) => {
|
|
411
559
|
clearTimeout(timer);
|
|
412
560
|
ws.close();
|
|
413
|
-
reject(new DxtradeError("LIMITS_ERROR"
|
|
561
|
+
reject(new DxtradeError("LIMITS_ERROR" /* LIMITS_ERROR */, `Symbol limits error: ${error.message}`));
|
|
414
562
|
});
|
|
415
563
|
});
|
|
416
564
|
}
|
|
417
565
|
|
|
418
566
|
// src/domains/order/order.ts
|
|
419
567
|
function createOrderListener(wsUrl, cookieStr, timeout = 3e4, debug = false) {
|
|
420
|
-
const ws = new
|
|
568
|
+
const ws = new import_ws5.default(wsUrl, { headers: { Cookie: cookieStr } });
|
|
421
569
|
let settled = false;
|
|
422
570
|
const ready = new Promise((resolve) => {
|
|
423
571
|
ws.on("open", resolve);
|
|
@@ -551,7 +699,7 @@ async function submitOrder(ctx, params) {
|
|
|
551
699
|
};
|
|
552
700
|
}
|
|
553
701
|
try {
|
|
554
|
-
const wsUrl = endpoints.websocket(ctx.broker);
|
|
702
|
+
const wsUrl = endpoints.websocket(ctx.broker, ctx.atmosphereId);
|
|
555
703
|
const cookieStr = Cookies.serialize(ctx.cookies);
|
|
556
704
|
const listener = createOrderListener(wsUrl, cookieStr, 3e4, ctx.debug);
|
|
557
705
|
await listener.ready;
|
|
@@ -571,21 +719,21 @@ async function submitOrder(ctx, params) {
|
|
|
571
719
|
} catch (error) {
|
|
572
720
|
if (error instanceof DxtradeError) throw error;
|
|
573
721
|
const message = error instanceof Error ? error.response?.data?.message ?? error.message : "Unknown error";
|
|
574
|
-
ctx.throwError("ORDER_ERROR"
|
|
722
|
+
ctx.throwError("ORDER_ERROR" /* ORDER_ERROR */, `Error submitting order: ${message}`);
|
|
575
723
|
}
|
|
576
724
|
}
|
|
577
725
|
|
|
578
726
|
// src/domains/position/position.ts
|
|
579
|
-
var
|
|
727
|
+
var import_ws6 = __toESM(require("ws"));
|
|
580
728
|
async function getPositions(ctx) {
|
|
581
729
|
ctx.ensureSession();
|
|
582
|
-
const wsUrl = endpoints.websocket(ctx.broker);
|
|
730
|
+
const wsUrl = endpoints.websocket(ctx.broker, ctx.atmosphereId);
|
|
583
731
|
const cookieStr = Cookies.serialize(ctx.cookies);
|
|
584
732
|
return new Promise((resolve, reject) => {
|
|
585
|
-
const ws = new
|
|
733
|
+
const ws = new import_ws6.default(wsUrl, { headers: { Cookie: cookieStr } });
|
|
586
734
|
const timer = setTimeout(() => {
|
|
587
735
|
ws.close();
|
|
588
|
-
reject(new DxtradeError("ACCOUNT_POSITIONS_TIMEOUT"
|
|
736
|
+
reject(new DxtradeError("ACCOUNT_POSITIONS_TIMEOUT" /* ACCOUNT_POSITIONS_TIMEOUT */, "Account positions timed out"));
|
|
589
737
|
}, 3e4);
|
|
590
738
|
ws.on("message", (data) => {
|
|
591
739
|
const msg = parseWsData(data);
|
|
@@ -600,7 +748,7 @@ async function getPositions(ctx) {
|
|
|
600
748
|
ws.on("error", (error) => {
|
|
601
749
|
clearTimeout(timer);
|
|
602
750
|
ws.close();
|
|
603
|
-
reject(new DxtradeError("ACCOUNT_POSITIONS_ERROR"
|
|
751
|
+
reject(new DxtradeError("ACCOUNT_POSITIONS_ERROR" /* ACCOUNT_POSITIONS_ERROR */, `Account positions error: ${error.message}`));
|
|
604
752
|
});
|
|
605
753
|
});
|
|
606
754
|
}
|
|
@@ -618,27 +766,31 @@ async function closePosition(ctx, data) {
|
|
|
618
766
|
} catch (error) {
|
|
619
767
|
if (error instanceof DxtradeError) throw error;
|
|
620
768
|
const message = error instanceof Error ? error.response?.data?.message ?? error.message : "Unknown error";
|
|
621
|
-
ctx.throwError("POSITION_CLOSE_ERROR"
|
|
769
|
+
ctx.throwError("POSITION_CLOSE_ERROR" /* POSITION_CLOSE_ERROR */, `Position close error: ${message}`);
|
|
622
770
|
}
|
|
623
771
|
}
|
|
624
772
|
|
|
625
773
|
// src/domains/session/session.ts
|
|
626
|
-
var
|
|
774
|
+
var import_ws7 = __toESM(require("ws"));
|
|
627
775
|
function waitForHandshake(wsUrl, cookieStr, timeout = 3e4, debug = false) {
|
|
628
776
|
return new Promise((resolve, reject) => {
|
|
629
|
-
const ws = new
|
|
777
|
+
const ws = new import_ws7.default(wsUrl, { headers: { Cookie: cookieStr } });
|
|
778
|
+
let atmosphereId = null;
|
|
630
779
|
const timer = setTimeout(() => {
|
|
631
780
|
ws.close();
|
|
632
781
|
reject(new Error("[dxtrade-api] Handshake timed out"));
|
|
633
782
|
}, timeout);
|
|
634
783
|
ws.on("message", (data) => {
|
|
784
|
+
if (!atmosphereId) {
|
|
785
|
+
atmosphereId = parseAtmosphereId(data);
|
|
786
|
+
}
|
|
635
787
|
const msg = parseWsData(data);
|
|
636
788
|
if (shouldLog(msg, debug)) debugLog(msg);
|
|
637
789
|
if (typeof msg === "string") return;
|
|
638
790
|
if (msg.accountId) {
|
|
639
791
|
clearTimeout(timer);
|
|
640
792
|
ws.close();
|
|
641
|
-
resolve();
|
|
793
|
+
resolve(atmosphereId);
|
|
642
794
|
}
|
|
643
795
|
});
|
|
644
796
|
ws.on("error", (error) => {
|
|
@@ -669,12 +821,12 @@ async function login(ctx) {
|
|
|
669
821
|
ctx.cookies = Cookies.merge(ctx.cookies, incoming);
|
|
670
822
|
ctx.callbacks.onLogin?.();
|
|
671
823
|
} else {
|
|
672
|
-
ctx.throwError("LOGIN_FAILED"
|
|
824
|
+
ctx.throwError("LOGIN_FAILED" /* LOGIN_FAILED */, `Login failed: ${response.status}`);
|
|
673
825
|
}
|
|
674
826
|
} catch (error) {
|
|
675
827
|
if (error instanceof DxtradeError) throw error;
|
|
676
828
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
677
|
-
ctx.throwError("LOGIN_ERROR"
|
|
829
|
+
ctx.throwError("LOGIN_ERROR" /* LOGIN_ERROR */, `Login error: ${message}`);
|
|
678
830
|
}
|
|
679
831
|
}
|
|
680
832
|
async function fetchCsrf(ctx) {
|
|
@@ -692,12 +844,12 @@ async function fetchCsrf(ctx) {
|
|
|
692
844
|
if (csrfMatch) {
|
|
693
845
|
ctx.csrf = csrfMatch[1];
|
|
694
846
|
} else {
|
|
695
|
-
ctx.throwError("CSRF_NOT_FOUND"
|
|
847
|
+
ctx.throwError("CSRF_NOT_FOUND" /* CSRF_NOT_FOUND */, "CSRF token not found");
|
|
696
848
|
}
|
|
697
849
|
} catch (error) {
|
|
698
850
|
if (error instanceof DxtradeError) throw error;
|
|
699
851
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
700
|
-
ctx.throwError("CSRF_ERROR"
|
|
852
|
+
ctx.throwError("CSRF_ERROR" /* CSRF_ERROR */, `CSRF fetch error: ${message}`);
|
|
701
853
|
}
|
|
702
854
|
}
|
|
703
855
|
async function switchAccount(ctx, accountId) {
|
|
@@ -715,19 +867,23 @@ async function switchAccount(ctx, accountId) {
|
|
|
715
867
|
} catch (error) {
|
|
716
868
|
if (error instanceof DxtradeError) throw error;
|
|
717
869
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
718
|
-
ctx.throwError("ACCOUNT_SWITCH_ERROR"
|
|
870
|
+
ctx.throwError("ACCOUNT_SWITCH_ERROR" /* ACCOUNT_SWITCH_ERROR */, `Error switching account: ${message}`);
|
|
719
871
|
}
|
|
720
872
|
}
|
|
721
873
|
async function connect(ctx) {
|
|
722
874
|
await login(ctx);
|
|
723
875
|
await fetchCsrf(ctx);
|
|
724
876
|
if (ctx.debug) clearDebugLog();
|
|
725
|
-
const wsUrl = endpoints.websocket(ctx.broker);
|
|
726
877
|
const cookieStr = Cookies.serialize(ctx.cookies);
|
|
727
|
-
await waitForHandshake(
|
|
878
|
+
ctx.atmosphereId = await waitForHandshake(endpoints.websocket(ctx.broker), cookieStr, 3e4, ctx.debug);
|
|
728
879
|
if (ctx.config.accountId) {
|
|
729
880
|
await switchAccount(ctx, ctx.config.accountId);
|
|
730
|
-
|
|
881
|
+
ctx.atmosphereId = await waitForHandshake(
|
|
882
|
+
endpoints.websocket(ctx.broker, ctx.atmosphereId),
|
|
883
|
+
Cookies.serialize(ctx.cookies),
|
|
884
|
+
3e4,
|
|
885
|
+
ctx.debug
|
|
886
|
+
);
|
|
731
887
|
}
|
|
732
888
|
}
|
|
733
889
|
|
|
@@ -741,12 +897,16 @@ var DxtradeClient = class {
|
|
|
741
897
|
callbacks,
|
|
742
898
|
cookies: {},
|
|
743
899
|
csrf: null,
|
|
900
|
+
atmosphereId: null,
|
|
744
901
|
broker: config.broker,
|
|
745
902
|
retries: config.retries ?? 3,
|
|
746
903
|
debug: config.debug ?? false,
|
|
747
904
|
ensureSession() {
|
|
748
905
|
if (!this.csrf) {
|
|
749
|
-
throw new DxtradeError(
|
|
906
|
+
throw new DxtradeError(
|
|
907
|
+
"NO_SESSION" /* NO_SESSION */,
|
|
908
|
+
"No active session. Call login() and fetchCsrf() or connect() first."
|
|
909
|
+
);
|
|
750
910
|
}
|
|
751
911
|
},
|
|
752
912
|
throwError(code, message) {
|
|
@@ -819,6 +979,17 @@ var DxtradeClient = class {
|
|
|
819
979
|
async getAssessments(params) {
|
|
820
980
|
return getAssessments(this._ctx, params);
|
|
821
981
|
}
|
|
982
|
+
/**
|
|
983
|
+
* Fetch OHLC price bars for a symbol.
|
|
984
|
+
* @param params.symbol - Instrument symbol (e.g. "EURUSD")
|
|
985
|
+
* @param params.resolution - Bar period in seconds (default: 60 = 1 min)
|
|
986
|
+
* @param params.range - Lookback window in seconds (default: 432000 = 5 days)
|
|
987
|
+
* @param params.maxBars - Maximum bars to return (default: 3500)
|
|
988
|
+
* @param params.priceField - "bid" or "ask" (default: "bid")
|
|
989
|
+
*/
|
|
990
|
+
async getOHLC(params) {
|
|
991
|
+
return getOHLC(this._ctx, params);
|
|
992
|
+
}
|
|
822
993
|
};
|
|
823
994
|
// Annotate the CommonJS export names for ESM import in node:
|
|
824
995
|
0 && (module.exports = {
|
|
@@ -826,6 +997,7 @@ var DxtradeClient = class {
|
|
|
826
997
|
BROKER,
|
|
827
998
|
DxtradeClient,
|
|
828
999
|
DxtradeError,
|
|
1000
|
+
ERROR,
|
|
829
1001
|
ORDER_TYPE,
|
|
830
1002
|
SIDE,
|
|
831
1003
|
TIF,
|