@danielgroen/dxtrade-api 1.0.7 → 1.0.9
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 +8 -8
- package/dist/index.d.mts +215 -65
- package/dist/index.d.ts +215 -65
- package/dist/index.js +539 -328
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +537 -328
- package/dist/index.mjs.map +1 -1
- package/package.json +25 -7
package/dist/index.js
CHANGED
|
@@ -36,6 +36,8 @@ __export(index_exports, {
|
|
|
36
36
|
DxtradeError: () => DxtradeError,
|
|
37
37
|
ORDER_TYPE: () => ORDER_TYPE,
|
|
38
38
|
SIDE: () => SIDE,
|
|
39
|
+
TIF: () => TIF,
|
|
40
|
+
WS_MESSAGE: () => WS_MESSAGE,
|
|
39
41
|
endpoints: () => endpoints,
|
|
40
42
|
resolveBrokerUrl: () => resolveBrokerUrl
|
|
41
43
|
});
|
|
@@ -43,13 +45,11 @@ module.exports = __toCommonJS(index_exports);
|
|
|
43
45
|
|
|
44
46
|
// src/constants/brokers.ts
|
|
45
47
|
var BROKER = {
|
|
46
|
-
|
|
48
|
+
LARKFUNDING: "https://trade.gooeytrade.com",
|
|
47
49
|
EIGHTCAP: "https://trader.dx-eightcap.com"
|
|
48
50
|
};
|
|
49
51
|
function resolveBrokerUrl(broker, customUrls) {
|
|
50
|
-
if (customUrls?.[broker])
|
|
51
|
-
return customUrls[broker];
|
|
52
|
-
}
|
|
52
|
+
if (customUrls?.[broker]) return customUrls[broker];
|
|
53
53
|
const key = broker.toUpperCase();
|
|
54
54
|
if (BROKER[key]) {
|
|
55
55
|
return BROKER[key];
|
|
@@ -58,6 +58,7 @@ function resolveBrokerUrl(broker, customUrls) {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
// src/constants/endpoints.ts
|
|
61
|
+
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`;
|
|
61
62
|
var endpoints = {
|
|
62
63
|
login: (base) => `${base}/api/auth/login`,
|
|
63
64
|
switchAccount: (base, id) => `${base}/api/accounts/switch?accountId=${id}`,
|
|
@@ -65,7 +66,7 @@ var endpoints = {
|
|
|
65
66
|
instrumentInfo: (base, symbol, tzOffset) => `${base}/api/instruments/info?symbol=${symbol}&timezoneOffset=${tzOffset}&withExDividends=true`,
|
|
66
67
|
submitOrder: (base) => `${base}/api/orders/single`,
|
|
67
68
|
assessments: (base) => `${base}/api/assessments`,
|
|
68
|
-
websocket: (base) => `wss://${base.split("//")[1]}/client/connector
|
|
69
|
+
websocket: (base) => `wss://${base.split("//")[1]}/client/connector` + websocketQuery
|
|
69
70
|
};
|
|
70
71
|
|
|
71
72
|
// src/constants/enums.ts
|
|
@@ -85,6 +86,27 @@ var ACTION = /* @__PURE__ */ ((ACTION2) => {
|
|
|
85
86
|
ACTION2["CLOSING"] = "CLOSING";
|
|
86
87
|
return ACTION2;
|
|
87
88
|
})(ACTION || {});
|
|
89
|
+
var TIF = /* @__PURE__ */ ((TIF2) => {
|
|
90
|
+
TIF2["GTC"] = "GTC";
|
|
91
|
+
TIF2["DAY"] = "DAY";
|
|
92
|
+
TIF2["GTD"] = "GTD";
|
|
93
|
+
return TIF2;
|
|
94
|
+
})(TIF || {});
|
|
95
|
+
var WS_MESSAGE = /* @__PURE__ */ ((WS_MESSAGE2) => {
|
|
96
|
+
WS_MESSAGE2["ACCOUNT_METRICS"] = "ACCOUNT_METRICS";
|
|
97
|
+
WS_MESSAGE2["ACCOUNTS"] = "ACCOUNTS";
|
|
98
|
+
WS_MESSAGE2["AVAILABLE_WATCHLISTS"] = "AVAILABLE_WATCHLISTS";
|
|
99
|
+
WS_MESSAGE2["INSTRUMENTS"] = "INSTRUMENTS";
|
|
100
|
+
WS_MESSAGE2["LIMITS"] = "LIMITS";
|
|
101
|
+
WS_MESSAGE2["MESSAGE"] = "MESSAGE";
|
|
102
|
+
WS_MESSAGE2["ORDERS"] = "ORDERS";
|
|
103
|
+
WS_MESSAGE2["POSITIONS"] = "POSITIONS";
|
|
104
|
+
WS_MESSAGE2["POSITION_CASH_TRANSFERS"] = "POSITION_CASH_TRANSFERS";
|
|
105
|
+
WS_MESSAGE2["PRIVATE_LAYOUT_NAMES"] = "PRIVATE_LAYOUT_NAMES";
|
|
106
|
+
WS_MESSAGE2["SHARED_PROPERTIES_MESSAGE"] = "SHARED_PROPERTIES_MESSAGE";
|
|
107
|
+
WS_MESSAGE2["USER_LOGIN_INFO"] = "USER_LOGIN_INFO";
|
|
108
|
+
return WS_MESSAGE2;
|
|
109
|
+
})(WS_MESSAGE || {});
|
|
88
110
|
|
|
89
111
|
// src/constants/errors.ts
|
|
90
112
|
var DxtradeError = class extends Error {
|
|
@@ -96,28 +118,30 @@ var DxtradeError = class extends Error {
|
|
|
96
118
|
}
|
|
97
119
|
};
|
|
98
120
|
|
|
99
|
-
// src/
|
|
100
|
-
var
|
|
121
|
+
// src/domains/account/account.ts
|
|
122
|
+
var import_ws = __toESM(require("ws"));
|
|
101
123
|
|
|
102
124
|
// src/utils/cookies.ts
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
125
|
+
var Cookies = class {
|
|
126
|
+
static parse(setCookieHeaders) {
|
|
127
|
+
const cookies = {};
|
|
128
|
+
for (const cookie of setCookieHeaders) {
|
|
129
|
+
const [nameValue] = cookie.split(";");
|
|
130
|
+
const eqIndex = nameValue.indexOf("=");
|
|
131
|
+
if (eqIndex === -1) continue;
|
|
132
|
+
const name = nameValue.slice(0, eqIndex).trim();
|
|
133
|
+
const value = nameValue.slice(eqIndex + 1).trim();
|
|
134
|
+
cookies[name] = value;
|
|
135
|
+
}
|
|
136
|
+
return cookies;
|
|
112
137
|
}
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
138
|
+
static serialize(cookies) {
|
|
139
|
+
return Object.entries(cookies).map(([key, value]) => `${key}=${value}`).join("; ");
|
|
140
|
+
}
|
|
141
|
+
static merge(existing, incoming) {
|
|
142
|
+
return { ...existing, ...incoming };
|
|
143
|
+
}
|
|
144
|
+
};
|
|
121
145
|
|
|
122
146
|
// src/utils/headers.ts
|
|
123
147
|
function baseHeaders() {
|
|
@@ -156,362 +180,547 @@ async function retryRequest(config, retries = 3) {
|
|
|
156
180
|
}
|
|
157
181
|
|
|
158
182
|
// src/utils/websocket.ts
|
|
159
|
-
var
|
|
160
|
-
|
|
183
|
+
var import_fs = require("fs");
|
|
184
|
+
var DEBUG_LOG = "debug.log";
|
|
185
|
+
function shouldLog(msg, debug) {
|
|
186
|
+
if (!debug) return false;
|
|
187
|
+
if (debug === true || debug === "true") return true;
|
|
188
|
+
if (typeof msg === "string") return false;
|
|
189
|
+
const filters = debug.split(",").map((s) => s.trim().toUpperCase());
|
|
190
|
+
return filters.includes(msg.type);
|
|
191
|
+
}
|
|
192
|
+
function debugLog(msg) {
|
|
193
|
+
(0, import_fs.appendFileSync)(DEBUG_LOG, JSON.stringify(msg) + "\n");
|
|
194
|
+
}
|
|
195
|
+
function clearDebugLog() {
|
|
196
|
+
(0, import_fs.writeFileSync)(DEBUG_LOG, "");
|
|
197
|
+
}
|
|
198
|
+
function parseWsData(data) {
|
|
199
|
+
const raw = data.toString();
|
|
200
|
+
const pipeIndex = raw.indexOf("|");
|
|
201
|
+
if (pipeIndex === -1) return raw;
|
|
202
|
+
try {
|
|
203
|
+
return JSON.parse(raw.slice(pipeIndex + 1));
|
|
204
|
+
} catch {
|
|
205
|
+
return raw;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// src/domains/account/account.ts
|
|
210
|
+
async function getAccountMetrics(ctx, timeout = 3e4) {
|
|
211
|
+
ctx.ensureSession();
|
|
212
|
+
const wsUrl = endpoints.websocket(ctx.baseUrl);
|
|
213
|
+
const cookieStr = Cookies.serialize(ctx.cookies);
|
|
161
214
|
return new Promise((resolve, reject) => {
|
|
162
215
|
const ws = new import_ws.default(wsUrl, { headers: { Cookie: cookieStr } });
|
|
163
216
|
const timer = setTimeout(() => {
|
|
164
217
|
ws.close();
|
|
165
|
-
reject(new
|
|
218
|
+
reject(new DxtradeError("ACCOUNT_METRICS_TIMEOUT", "Account metrics timed out"));
|
|
166
219
|
}, timeout);
|
|
167
220
|
ws.on("message", (data) => {
|
|
168
|
-
const
|
|
169
|
-
if (debug)
|
|
170
|
-
if (
|
|
221
|
+
const msg = parseWsData(data);
|
|
222
|
+
if (shouldLog(msg, ctx.debug)) debugLog(msg);
|
|
223
|
+
if (typeof msg === "string") return;
|
|
224
|
+
if (msg.type === "ACCOUNT_METRICS" /* ACCOUNT_METRICS */) {
|
|
171
225
|
clearTimeout(timer);
|
|
172
226
|
ws.close();
|
|
173
|
-
|
|
227
|
+
const body = msg.body;
|
|
228
|
+
resolve(body.allMetrics);
|
|
174
229
|
}
|
|
175
230
|
});
|
|
176
231
|
ws.on("error", (error) => {
|
|
177
232
|
clearTimeout(timer);
|
|
178
233
|
ws.close();
|
|
179
|
-
reject(new
|
|
234
|
+
reject(new DxtradeError("ACCOUNT_METRICS_ERROR", `Account metrics error: ${error.message}`));
|
|
180
235
|
});
|
|
181
236
|
});
|
|
182
237
|
}
|
|
183
|
-
|
|
238
|
+
|
|
239
|
+
// src/domains/assessments/assessments.ts
|
|
240
|
+
async function getAssessments(ctx, params) {
|
|
241
|
+
ctx.ensureSession();
|
|
242
|
+
try {
|
|
243
|
+
const response = await retryRequest(
|
|
244
|
+
{
|
|
245
|
+
method: "POST",
|
|
246
|
+
url: endpoints.assessments(ctx.baseUrl),
|
|
247
|
+
data: {
|
|
248
|
+
from: params.from,
|
|
249
|
+
instrument: params.instrument,
|
|
250
|
+
subtype: params.subtype ?? null,
|
|
251
|
+
to: params.to
|
|
252
|
+
},
|
|
253
|
+
headers: authHeaders(ctx.csrf, Cookies.serialize(ctx.cookies))
|
|
254
|
+
},
|
|
255
|
+
ctx.retries
|
|
256
|
+
);
|
|
257
|
+
return response.data;
|
|
258
|
+
} catch (error) {
|
|
259
|
+
if (error instanceof DxtradeError) throw error;
|
|
260
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
261
|
+
ctx.throwError("ASSESSMENTS_ERROR", `Error fetching assessments: ${message}`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// src/domains/instrument/instrument.ts
|
|
266
|
+
var import_ws2 = __toESM(require("ws"));
|
|
267
|
+
async function getInstruments(ctx, params = {}, timeout = 3e4) {
|
|
268
|
+
ctx.ensureSession();
|
|
269
|
+
const wsUrl = endpoints.websocket(ctx.baseUrl);
|
|
270
|
+
const cookieStr = Cookies.serialize(ctx.cookies);
|
|
184
271
|
return new Promise((resolve, reject) => {
|
|
185
|
-
const ws = new
|
|
272
|
+
const ws = new import_ws2.default(wsUrl, { headers: { Cookie: cookieStr } });
|
|
186
273
|
const timer = setTimeout(() => {
|
|
274
|
+
ws.close();
|
|
275
|
+
reject(new DxtradeError("INSTRUMENTS_TIMEOUT", "Instruments request timed out"));
|
|
276
|
+
}, timeout);
|
|
277
|
+
let instruments = [];
|
|
278
|
+
let settleTimer = null;
|
|
279
|
+
ws.on("message", (data) => {
|
|
280
|
+
const msg = parseWsData(data);
|
|
281
|
+
if (shouldLog(msg, ctx.debug)) debugLog(msg);
|
|
282
|
+
if (typeof msg === "string") return;
|
|
283
|
+
if (msg.type === "INSTRUMENTS" /* INSTRUMENTS */) {
|
|
284
|
+
instruments.push(...msg.body);
|
|
285
|
+
if (settleTimer) clearTimeout(settleTimer);
|
|
286
|
+
settleTimer = setTimeout(() => {
|
|
287
|
+
clearTimeout(timer);
|
|
288
|
+
ws.close();
|
|
289
|
+
resolve(
|
|
290
|
+
instruments.filter((instrument) => {
|
|
291
|
+
for (const key in params) {
|
|
292
|
+
if (params[key] !== instrument[key]) {
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return true;
|
|
297
|
+
})
|
|
298
|
+
);
|
|
299
|
+
}, 0);
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
ws.on("error", (error) => {
|
|
303
|
+
clearTimeout(timer);
|
|
304
|
+
ws.close();
|
|
305
|
+
reject(new DxtradeError("INSTRUMENTS_ERROR", `Instruments error: ${error.message}`));
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// src/domains/order/order.ts
|
|
311
|
+
var import_crypto = __toESM(require("crypto"));
|
|
312
|
+
var import_ws4 = __toESM(require("ws"));
|
|
313
|
+
|
|
314
|
+
// src/domains/symbol/symbol.ts
|
|
315
|
+
var import_ws3 = __toESM(require("ws"));
|
|
316
|
+
async function getSymbolSuggestions(ctx, text) {
|
|
317
|
+
ctx.ensureSession();
|
|
318
|
+
try {
|
|
319
|
+
const cookieStr = Cookies.serialize(ctx.cookies);
|
|
320
|
+
const response = await retryRequest(
|
|
321
|
+
{
|
|
322
|
+
method: "GET",
|
|
323
|
+
url: endpoints.suggest(ctx.baseUrl, text),
|
|
324
|
+
headers: { ...baseHeaders(), Cookie: cookieStr }
|
|
325
|
+
},
|
|
326
|
+
ctx.retries
|
|
327
|
+
);
|
|
328
|
+
const suggests = response.data?.suggests;
|
|
329
|
+
if (!suggests?.length) {
|
|
330
|
+
ctx.throwError("NO_SUGGESTIONS", "No symbol suggestions found");
|
|
331
|
+
}
|
|
332
|
+
return suggests;
|
|
333
|
+
} catch (error) {
|
|
334
|
+
if (error instanceof DxtradeError) throw error;
|
|
335
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
336
|
+
ctx.throwError("SUGGEST_ERROR", `Error getting symbol suggestions: ${message}`);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
async function getSymbolInfo(ctx, symbol) {
|
|
340
|
+
ctx.ensureSession();
|
|
341
|
+
try {
|
|
342
|
+
const offsetMinutes = Math.abs((/* @__PURE__ */ new Date()).getTimezoneOffset());
|
|
343
|
+
const cookieStr = Cookies.serialize(ctx.cookies);
|
|
344
|
+
const response = await retryRequest(
|
|
345
|
+
{
|
|
346
|
+
method: "GET",
|
|
347
|
+
url: endpoints.instrumentInfo(ctx.baseUrl, symbol, offsetMinutes),
|
|
348
|
+
headers: { ...baseHeaders(), Cookie: cookieStr }
|
|
349
|
+
},
|
|
350
|
+
ctx.retries
|
|
351
|
+
);
|
|
352
|
+
if (!response.data) {
|
|
353
|
+
ctx.throwError("NO_SYMBOL_INFO", "No symbol info returned");
|
|
354
|
+
}
|
|
355
|
+
return response.data;
|
|
356
|
+
} catch (error) {
|
|
357
|
+
if (error instanceof DxtradeError) throw error;
|
|
358
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
359
|
+
ctx.throwError("SYMBOL_INFO_ERROR", `Error getting symbol info: ${message}`);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
async function getSymbolLimits(ctx, timeout = 3e4) {
|
|
363
|
+
ctx.ensureSession();
|
|
364
|
+
const wsUrl = endpoints.websocket(ctx.baseUrl);
|
|
365
|
+
const cookieStr = Cookies.serialize(ctx.cookies);
|
|
366
|
+
return new Promise((resolve, reject) => {
|
|
367
|
+
const ws = new import_ws3.default(wsUrl, { headers: { Cookie: cookieStr } });
|
|
368
|
+
const timer = setTimeout(() => {
|
|
369
|
+
ws.close();
|
|
370
|
+
reject(new DxtradeError("LIMITS_TIMEOUT", "Symbol limits request timed out"));
|
|
371
|
+
}, timeout);
|
|
372
|
+
let limits = [];
|
|
373
|
+
let settleTimer = null;
|
|
374
|
+
ws.on("message", (data) => {
|
|
375
|
+
const msg = parseWsData(data);
|
|
376
|
+
if (shouldLog(msg, ctx.debug)) debugLog(msg);
|
|
377
|
+
if (typeof msg === "string") return;
|
|
378
|
+
if (msg.type === "LIMITS" /* LIMITS */) {
|
|
379
|
+
const batch = msg.body;
|
|
380
|
+
if (batch.length === 0) return;
|
|
381
|
+
limits.push(...batch);
|
|
382
|
+
if (settleTimer) clearTimeout(settleTimer);
|
|
383
|
+
settleTimer = setTimeout(() => {
|
|
384
|
+
clearTimeout(timer);
|
|
385
|
+
ws.close();
|
|
386
|
+
resolve(limits);
|
|
387
|
+
}, 0);
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
ws.on("error", (error) => {
|
|
391
|
+
clearTimeout(timer);
|
|
392
|
+
ws.close();
|
|
393
|
+
reject(new DxtradeError("LIMITS_ERROR", `Symbol limits error: ${error.message}`));
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// src/domains/order/order.ts
|
|
399
|
+
function createOrderListener(wsUrl, cookieStr, timeout = 3e4, debug = false) {
|
|
400
|
+
const ws = new import_ws4.default(wsUrl, { headers: { Cookie: cookieStr } });
|
|
401
|
+
let settled = false;
|
|
402
|
+
const ready = new Promise((resolve) => {
|
|
403
|
+
ws.on("open", resolve);
|
|
404
|
+
});
|
|
405
|
+
const promise = new Promise((resolve, reject) => {
|
|
406
|
+
const timer = setTimeout(() => {
|
|
407
|
+
if (settled) return;
|
|
408
|
+
settled = true;
|
|
187
409
|
ws.close();
|
|
188
410
|
reject(new Error("[dxtrade-api] Order update timed out"));
|
|
189
411
|
}, timeout);
|
|
412
|
+
function done(err, result) {
|
|
413
|
+
if (settled) return;
|
|
414
|
+
settled = true;
|
|
415
|
+
clearTimeout(timer);
|
|
416
|
+
ws.close();
|
|
417
|
+
if (err) reject(err);
|
|
418
|
+
else resolve(result);
|
|
419
|
+
}
|
|
190
420
|
ws.on("message", (data) => {
|
|
191
|
-
const
|
|
192
|
-
if (debug)
|
|
193
|
-
if (
|
|
194
|
-
if (
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
421
|
+
const msg = parseWsData(data);
|
|
422
|
+
if (shouldLog(msg, debug)) debugLog(msg);
|
|
423
|
+
if (typeof msg === "string") return;
|
|
424
|
+
if (msg.type === "MESSAGE" /* MESSAGE */) {
|
|
425
|
+
const messages = msg.body;
|
|
426
|
+
const orderMsg = messages?.findLast?.(
|
|
427
|
+
(m) => m.messageCategory === "TRADE_LOG" && m.messageType === "ORDER" && !m.historyMessage
|
|
428
|
+
);
|
|
429
|
+
if (!orderMsg) return;
|
|
430
|
+
const params = orderMsg.parametersTO;
|
|
431
|
+
if (params.orderStatus === "REJECTED") {
|
|
432
|
+
const reason = params.rejectReason?.key ?? "Unknown reason";
|
|
433
|
+
done(new Error(`[dxtrade-api] Order rejected: ${reason}`));
|
|
434
|
+
} else if (params.orderStatus === "FILLED") {
|
|
435
|
+
done(null, {
|
|
436
|
+
orderId: params.orderKey,
|
|
437
|
+
status: params.orderStatus,
|
|
438
|
+
symbol: params.symbol,
|
|
439
|
+
filledQuantity: params.filledQuantity,
|
|
440
|
+
filledPrice: params.filledPrice
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
if (msg.type === "ORDERS" /* ORDERS */) {
|
|
446
|
+
const body = msg.body?.[0];
|
|
447
|
+
if (!body?.orderId) return;
|
|
203
448
|
if (body.status === "REJECTED") {
|
|
204
|
-
|
|
205
|
-
} else {
|
|
206
|
-
|
|
449
|
+
done(new Error(`[dxtrade-api] Order rejected: ${body.statusDescription ?? "Unknown reason"}`));
|
|
450
|
+
} else if (body.status === "FILLED") {
|
|
451
|
+
done(null, body);
|
|
207
452
|
}
|
|
208
|
-
} catch {
|
|
209
453
|
}
|
|
210
454
|
});
|
|
211
455
|
ws.on("error", (error) => {
|
|
456
|
+
if (settled) return;
|
|
457
|
+
settled = true;
|
|
212
458
|
clearTimeout(timer);
|
|
213
459
|
ws.close();
|
|
214
460
|
reject(new Error(`[dxtrade-api] WebSocket order listener error: ${error.message}`));
|
|
215
461
|
});
|
|
216
462
|
});
|
|
463
|
+
return { promise, ready };
|
|
464
|
+
}
|
|
465
|
+
async function submitOrder(ctx, params) {
|
|
466
|
+
ctx.ensureSession();
|
|
467
|
+
const {
|
|
468
|
+
symbol,
|
|
469
|
+
side,
|
|
470
|
+
quantity,
|
|
471
|
+
orderType,
|
|
472
|
+
orderCode,
|
|
473
|
+
price,
|
|
474
|
+
instrumentId,
|
|
475
|
+
stopLoss,
|
|
476
|
+
takeProfit,
|
|
477
|
+
positionEffect = "OPENING" /* OPENING */,
|
|
478
|
+
positionCode,
|
|
479
|
+
tif = "GTC",
|
|
480
|
+
expireDate,
|
|
481
|
+
metadata
|
|
482
|
+
} = params;
|
|
483
|
+
const info = await getSymbolInfo(ctx, symbol);
|
|
484
|
+
const units = Math.round(quantity * info.lotSize);
|
|
485
|
+
const qty = side === "BUY" /* BUY */ ? units : -units;
|
|
486
|
+
const priceParam = orderType === "STOP" /* STOP */ ? "stopPrice" : "limitPrice";
|
|
487
|
+
const orderData = {
|
|
488
|
+
directExchange: false,
|
|
489
|
+
legs: [
|
|
490
|
+
{
|
|
491
|
+
...instrumentId != null && { instrumentId },
|
|
492
|
+
...positionCode != null && { positionCode },
|
|
493
|
+
positionEffect,
|
|
494
|
+
ratioQuantity: 1,
|
|
495
|
+
symbol
|
|
496
|
+
}
|
|
497
|
+
],
|
|
498
|
+
orderSide: side,
|
|
499
|
+
orderType,
|
|
500
|
+
quantity: qty,
|
|
501
|
+
requestId: orderCode ?? `gwt-uid-931-${import_crypto.default.randomUUID()}`,
|
|
502
|
+
timeInForce: tif,
|
|
503
|
+
...expireDate != null && { expireDate },
|
|
504
|
+
...metadata != null && { metadata }
|
|
505
|
+
};
|
|
506
|
+
if (price != null && orderType !== "MARKET" /* MARKET */) {
|
|
507
|
+
orderData[priceParam] = price;
|
|
508
|
+
}
|
|
509
|
+
if (stopLoss) {
|
|
510
|
+
orderData.stopLoss = {
|
|
511
|
+
...stopLoss.offset != null && { fixedOffset: stopLoss.offset },
|
|
512
|
+
...stopLoss.price != null && { fixedPrice: stopLoss.price },
|
|
513
|
+
priceFixed: stopLoss.price != null,
|
|
514
|
+
orderChainId: 0,
|
|
515
|
+
orderId: 0,
|
|
516
|
+
orderType: "STOP" /* STOP */,
|
|
517
|
+
quantityForProtection: qty,
|
|
518
|
+
removed: false
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
if (takeProfit) {
|
|
522
|
+
orderData.takeProfit = {
|
|
523
|
+
...takeProfit.offset != null && { fixedOffset: takeProfit.offset },
|
|
524
|
+
...takeProfit.price != null && { fixedPrice: takeProfit.price },
|
|
525
|
+
priceFixed: takeProfit.price != null,
|
|
526
|
+
orderChainId: 0,
|
|
527
|
+
orderId: 0,
|
|
528
|
+
orderType: "LIMIT" /* LIMIT */,
|
|
529
|
+
quantityForProtection: qty,
|
|
530
|
+
removed: false
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
try {
|
|
534
|
+
const wsUrl = endpoints.websocket(ctx.baseUrl);
|
|
535
|
+
const cookieStr = Cookies.serialize(ctx.cookies);
|
|
536
|
+
const listener = createOrderListener(wsUrl, cookieStr, 3e4, ctx.debug);
|
|
537
|
+
await listener.ready;
|
|
538
|
+
const response = await retryRequest(
|
|
539
|
+
{
|
|
540
|
+
method: "POST",
|
|
541
|
+
url: endpoints.submitOrder(ctx.baseUrl),
|
|
542
|
+
data: orderData,
|
|
543
|
+
headers: authHeaders(ctx.csrf, Cookies.serialize(ctx.cookies))
|
|
544
|
+
},
|
|
545
|
+
ctx.retries
|
|
546
|
+
);
|
|
547
|
+
ctx.callbacks.onOrderPlaced?.(response.data);
|
|
548
|
+
const orderUpdate = await listener.promise;
|
|
549
|
+
ctx.callbacks.onOrderUpdate?.(orderUpdate);
|
|
550
|
+
return orderUpdate;
|
|
551
|
+
} catch (error) {
|
|
552
|
+
if (error instanceof DxtradeError) throw error;
|
|
553
|
+
const message = error instanceof Error ? error.response?.data?.message ?? error.message : "Unknown error";
|
|
554
|
+
ctx.throwError("ORDER_ERROR", `Error submitting order: ${message}`);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// src/domains/session/session.ts
|
|
559
|
+
var import_ws5 = __toESM(require("ws"));
|
|
560
|
+
function waitForHandshake(wsUrl, cookieStr, timeout = 3e4, debug = false) {
|
|
561
|
+
return new Promise((resolve, reject) => {
|
|
562
|
+
const ws = new import_ws5.default(wsUrl, { headers: { Cookie: cookieStr } });
|
|
563
|
+
const timer = setTimeout(() => {
|
|
564
|
+
ws.close();
|
|
565
|
+
reject(new Error("[dxtrade-api] Handshake timed out"));
|
|
566
|
+
}, timeout);
|
|
567
|
+
ws.on("message", (data) => {
|
|
568
|
+
const msg = parseWsData(data);
|
|
569
|
+
if (shouldLog(msg, debug)) debugLog(msg);
|
|
570
|
+
if (typeof msg === "string") return;
|
|
571
|
+
if (msg.type === "POSITIONS" /* POSITIONS */) {
|
|
572
|
+
clearTimeout(timer);
|
|
573
|
+
ws.close();
|
|
574
|
+
resolve();
|
|
575
|
+
}
|
|
576
|
+
});
|
|
577
|
+
ws.on("error", (error) => {
|
|
578
|
+
clearTimeout(timer);
|
|
579
|
+
ws.close();
|
|
580
|
+
reject(new Error(`[dxtrade-api] WebSocket handshake error: ${error.message}`));
|
|
581
|
+
});
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
async function login(ctx) {
|
|
585
|
+
try {
|
|
586
|
+
const response = await retryRequest(
|
|
587
|
+
{
|
|
588
|
+
method: "POST",
|
|
589
|
+
url: endpoints.login(ctx.baseUrl),
|
|
590
|
+
data: {
|
|
591
|
+
username: ctx.config.username,
|
|
592
|
+
password: ctx.config.password,
|
|
593
|
+
domain: ctx.config.broker
|
|
594
|
+
},
|
|
595
|
+
headers: { "Content-Type": "application/json" }
|
|
596
|
+
},
|
|
597
|
+
ctx.retries
|
|
598
|
+
);
|
|
599
|
+
if (response.status === 200) {
|
|
600
|
+
const setCookies = response.headers["set-cookie"] ?? [];
|
|
601
|
+
const incoming = Cookies.parse(setCookies);
|
|
602
|
+
ctx.cookies = Cookies.merge(ctx.cookies, incoming);
|
|
603
|
+
ctx.callbacks.onLogin?.();
|
|
604
|
+
} else {
|
|
605
|
+
ctx.throwError("LOGIN_FAILED", `Login failed: ${response.status}`);
|
|
606
|
+
}
|
|
607
|
+
} catch (error) {
|
|
608
|
+
if (error instanceof DxtradeError) throw error;
|
|
609
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
610
|
+
ctx.throwError("LOGIN_ERROR", `Login error: ${message}`);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
async function fetchCsrf(ctx) {
|
|
614
|
+
try {
|
|
615
|
+
const cookieStr = Cookies.serialize(ctx.cookies);
|
|
616
|
+
const response = await retryRequest(
|
|
617
|
+
{
|
|
618
|
+
method: "GET",
|
|
619
|
+
url: ctx.baseUrl,
|
|
620
|
+
headers: { ...cookieOnlyHeaders(cookieStr), Referer: ctx.baseUrl }
|
|
621
|
+
},
|
|
622
|
+
ctx.retries
|
|
623
|
+
);
|
|
624
|
+
const csrfMatch = response.data?.match(/name="csrf" content="([^"]+)"/);
|
|
625
|
+
if (csrfMatch) {
|
|
626
|
+
ctx.csrf = csrfMatch[1];
|
|
627
|
+
} else {
|
|
628
|
+
ctx.throwError("CSRF_NOT_FOUND", "CSRF token not found");
|
|
629
|
+
}
|
|
630
|
+
} catch (error) {
|
|
631
|
+
if (error instanceof DxtradeError) throw error;
|
|
632
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
633
|
+
ctx.throwError("CSRF_ERROR", `CSRF fetch error: ${message}`);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
async function switchAccount(ctx, accountId) {
|
|
637
|
+
ctx.ensureSession();
|
|
638
|
+
try {
|
|
639
|
+
await retryRequest(
|
|
640
|
+
{
|
|
641
|
+
method: "POST",
|
|
642
|
+
url: endpoints.switchAccount(ctx.baseUrl, accountId),
|
|
643
|
+
headers: authHeaders(ctx.csrf, Cookies.serialize(ctx.cookies))
|
|
644
|
+
},
|
|
645
|
+
ctx.retries
|
|
646
|
+
);
|
|
647
|
+
ctx.callbacks.onAccountSwitch?.(accountId);
|
|
648
|
+
} catch (error) {
|
|
649
|
+
if (error instanceof DxtradeError) throw error;
|
|
650
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
651
|
+
ctx.throwError("ACCOUNT_SWITCH_ERROR", `Error switching account: ${message}`);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
async function connect(ctx) {
|
|
655
|
+
await login(ctx);
|
|
656
|
+
await fetchCsrf(ctx);
|
|
657
|
+
if (ctx.debug) clearDebugLog();
|
|
658
|
+
const wsUrl = endpoints.websocket(ctx.baseUrl);
|
|
659
|
+
const cookieStr = Cookies.serialize(ctx.cookies);
|
|
660
|
+
await waitForHandshake(wsUrl, cookieStr, 3e4, ctx.debug);
|
|
661
|
+
if (ctx.config.accountId) {
|
|
662
|
+
await switchAccount(ctx, ctx.config.accountId);
|
|
663
|
+
await waitForHandshake(endpoints.websocket(ctx.baseUrl), Cookies.serialize(ctx.cookies), 3e4, ctx.debug);
|
|
664
|
+
}
|
|
217
665
|
}
|
|
218
666
|
|
|
219
667
|
// src/client.ts
|
|
220
668
|
var DxtradeClient = class {
|
|
221
|
-
|
|
222
|
-
callbacks;
|
|
223
|
-
cookies = {};
|
|
224
|
-
csrf = null;
|
|
225
|
-
baseUrl;
|
|
226
|
-
retries;
|
|
227
|
-
debug;
|
|
669
|
+
_ctx;
|
|
228
670
|
constructor(config) {
|
|
229
|
-
|
|
230
|
-
this.
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
671
|
+
const callbacks = config.callbacks ?? {};
|
|
672
|
+
this._ctx = {
|
|
673
|
+
config,
|
|
674
|
+
callbacks,
|
|
675
|
+
cookies: {},
|
|
676
|
+
csrf: null,
|
|
677
|
+
baseUrl: resolveBrokerUrl(config.broker, config.brokerUrls),
|
|
678
|
+
retries: config.retries ?? 3,
|
|
679
|
+
debug: config.debug ?? false,
|
|
680
|
+
ensureSession() {
|
|
681
|
+
if (!this.csrf) {
|
|
682
|
+
throw new DxtradeError("NO_SESSION", "No active session. Call login() and fetchCsrf() or connect() first.");
|
|
683
|
+
}
|
|
684
|
+
},
|
|
685
|
+
throwError(code, message) {
|
|
686
|
+
const error = new DxtradeError(code, message);
|
|
687
|
+
callbacks.onError?.(error);
|
|
688
|
+
throw error;
|
|
689
|
+
}
|
|
690
|
+
};
|
|
234
691
|
}
|
|
235
|
-
// ── Session ─────────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
236
692
|
async login() {
|
|
237
|
-
|
|
238
|
-
const response = await retryRequest(
|
|
239
|
-
{
|
|
240
|
-
method: "POST",
|
|
241
|
-
url: endpoints.login(this.baseUrl),
|
|
242
|
-
data: {
|
|
243
|
-
username: this.config.username,
|
|
244
|
-
password: this.config.password,
|
|
245
|
-
domain: this.config.broker
|
|
246
|
-
},
|
|
247
|
-
headers: { "Content-Type": "application/json" }
|
|
248
|
-
},
|
|
249
|
-
this.retries
|
|
250
|
-
);
|
|
251
|
-
if (response.status === 200) {
|
|
252
|
-
const setCookies = response.headers["set-cookie"] ?? [];
|
|
253
|
-
const incoming = parseCookies(setCookies);
|
|
254
|
-
this.cookies = mergeCookies(this.cookies, incoming);
|
|
255
|
-
this.callbacks.onLogin?.();
|
|
256
|
-
} else {
|
|
257
|
-
this.throwError("LOGIN_FAILED", `Login failed: ${response.status}`);
|
|
258
|
-
}
|
|
259
|
-
} catch (error) {
|
|
260
|
-
if (error instanceof DxtradeError) throw error;
|
|
261
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
262
|
-
this.throwError("LOGIN_ERROR", `Login error: ${message}`);
|
|
263
|
-
}
|
|
693
|
+
return login(this._ctx);
|
|
264
694
|
}
|
|
265
695
|
async fetchCsrf() {
|
|
266
|
-
|
|
267
|
-
const cookieStr = serializeCookies(this.cookies);
|
|
268
|
-
const response = await retryRequest(
|
|
269
|
-
{
|
|
270
|
-
method: "GET",
|
|
271
|
-
url: this.baseUrl,
|
|
272
|
-
headers: { ...cookieOnlyHeaders(cookieStr), Referer: this.baseUrl }
|
|
273
|
-
},
|
|
274
|
-
this.retries
|
|
275
|
-
);
|
|
276
|
-
const csrfMatch = response.data?.match(/name="csrf" content="([^"]+)"/);
|
|
277
|
-
if (csrfMatch) {
|
|
278
|
-
this.csrf = csrfMatch[1];
|
|
279
|
-
} else {
|
|
280
|
-
this.throwError("CSRF_NOT_FOUND", "CSRF token not found");
|
|
281
|
-
}
|
|
282
|
-
} catch (error) {
|
|
283
|
-
if (error instanceof DxtradeError) throw error;
|
|
284
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
285
|
-
this.throwError("CSRF_ERROR", `CSRF fetch error: ${message}`);
|
|
286
|
-
}
|
|
696
|
+
return fetchCsrf(this._ctx);
|
|
287
697
|
}
|
|
288
698
|
async switchAccount(accountId) {
|
|
289
|
-
this.
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
method: "POST",
|
|
294
|
-
url: endpoints.switchAccount(this.baseUrl, accountId),
|
|
295
|
-
headers: authHeaders(this.csrf, serializeCookies(this.cookies))
|
|
296
|
-
},
|
|
297
|
-
this.retries
|
|
298
|
-
);
|
|
299
|
-
this.callbacks.onAccountSwitch?.(accountId);
|
|
300
|
-
} catch (error) {
|
|
301
|
-
if (error instanceof DxtradeError) throw error;
|
|
302
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
303
|
-
this.throwError(
|
|
304
|
-
"ACCOUNT_SWITCH_ERROR",
|
|
305
|
-
`Error switching account: ${message}`
|
|
306
|
-
);
|
|
307
|
-
}
|
|
699
|
+
return switchAccount(this._ctx, accountId);
|
|
700
|
+
}
|
|
701
|
+
async connect() {
|
|
702
|
+
return connect(this._ctx);
|
|
308
703
|
}
|
|
309
|
-
// ── Market Data ─────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
310
704
|
async getSymbolSuggestions(text) {
|
|
311
|
-
this.
|
|
312
|
-
try {
|
|
313
|
-
const cookieStr = serializeCookies(this.cookies);
|
|
314
|
-
const response = await retryRequest(
|
|
315
|
-
{
|
|
316
|
-
method: "GET",
|
|
317
|
-
url: endpoints.suggest(this.baseUrl, text),
|
|
318
|
-
headers: { ...baseHeaders(), Cookie: cookieStr }
|
|
319
|
-
},
|
|
320
|
-
this.retries
|
|
321
|
-
);
|
|
322
|
-
const suggests = response.data?.suggests;
|
|
323
|
-
if (!suggests?.length) {
|
|
324
|
-
this.throwError("NO_SUGGESTIONS", "No symbol suggestions found");
|
|
325
|
-
}
|
|
326
|
-
return suggests;
|
|
327
|
-
} catch (error) {
|
|
328
|
-
if (error instanceof DxtradeError) throw error;
|
|
329
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
330
|
-
this.throwError(
|
|
331
|
-
"SUGGEST_ERROR",
|
|
332
|
-
`Error getting symbol suggestions: ${message}`
|
|
333
|
-
);
|
|
334
|
-
}
|
|
705
|
+
return getSymbolSuggestions(this._ctx, text);
|
|
335
706
|
}
|
|
336
707
|
async getSymbolInfo(symbol) {
|
|
337
|
-
this.
|
|
338
|
-
try {
|
|
339
|
-
const offsetMinutes = Math.abs((/* @__PURE__ */ new Date()).getTimezoneOffset());
|
|
340
|
-
const cookieStr = serializeCookies(this.cookies);
|
|
341
|
-
const response = await retryRequest(
|
|
342
|
-
{
|
|
343
|
-
method: "GET",
|
|
344
|
-
url: endpoints.instrumentInfo(this.baseUrl, symbol, offsetMinutes),
|
|
345
|
-
headers: { ...baseHeaders(), Cookie: cookieStr }
|
|
346
|
-
},
|
|
347
|
-
this.retries
|
|
348
|
-
);
|
|
349
|
-
if (!response.data) {
|
|
350
|
-
this.throwError("NO_SYMBOL_INFO", "No symbol info returned");
|
|
351
|
-
}
|
|
352
|
-
return response.data;
|
|
353
|
-
} catch (error) {
|
|
354
|
-
if (error instanceof DxtradeError) throw error;
|
|
355
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
356
|
-
this.throwError(
|
|
357
|
-
"SYMBOL_INFO_ERROR",
|
|
358
|
-
`Error getting symbol info: ${message}`
|
|
359
|
-
);
|
|
360
|
-
}
|
|
708
|
+
return getSymbolInfo(this._ctx, symbol);
|
|
361
709
|
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
this.ensureSession();
|
|
365
|
-
const {
|
|
366
|
-
symbol,
|
|
367
|
-
side,
|
|
368
|
-
quantity,
|
|
369
|
-
orderType,
|
|
370
|
-
price,
|
|
371
|
-
instrumentId,
|
|
372
|
-
stopLoss,
|
|
373
|
-
takeProfit,
|
|
374
|
-
positionEffect = "OPENING" /* OPENING */
|
|
375
|
-
} = params;
|
|
376
|
-
const qty = side === "BUY" /* BUY */ ? Math.abs(quantity) : -Math.abs(quantity);
|
|
377
|
-
const priceParam = orderType === "STOP" /* STOP */ ? "stopPrice" : "limitPrice";
|
|
378
|
-
const orderData = {
|
|
379
|
-
directExchange: false,
|
|
380
|
-
legs: [
|
|
381
|
-
{
|
|
382
|
-
...instrumentId != null && { instrumentId },
|
|
383
|
-
positionEffect,
|
|
384
|
-
ratioQuantity: 1,
|
|
385
|
-
symbol
|
|
386
|
-
}
|
|
387
|
-
],
|
|
388
|
-
orderSide: side,
|
|
389
|
-
orderType,
|
|
390
|
-
quantity: qty,
|
|
391
|
-
requestId: `gwt-uid-931-${import_crypto.default.randomUUID()}`,
|
|
392
|
-
timeInForce: "GTC"
|
|
393
|
-
};
|
|
394
|
-
if (price != null && orderType !== "MARKET" /* MARKET */) {
|
|
395
|
-
orderData[priceParam] = price;
|
|
396
|
-
}
|
|
397
|
-
if (stopLoss) {
|
|
398
|
-
orderData.stopLoss = {
|
|
399
|
-
...stopLoss.offset != null && { fixedOffset: stopLoss.offset },
|
|
400
|
-
...stopLoss.price != null && { fixedPrice: stopLoss.price },
|
|
401
|
-
priceFixed: stopLoss.price != null,
|
|
402
|
-
orderChainId: 0,
|
|
403
|
-
orderId: 0,
|
|
404
|
-
orderType: "STOP" /* STOP */,
|
|
405
|
-
quantityForProtection: qty,
|
|
406
|
-
removed: false
|
|
407
|
-
};
|
|
408
|
-
}
|
|
409
|
-
if (takeProfit) {
|
|
410
|
-
orderData.takeProfit = {
|
|
411
|
-
...takeProfit.offset != null && { fixedOffset: takeProfit.offset },
|
|
412
|
-
...takeProfit.price != null && { fixedPrice: takeProfit.price },
|
|
413
|
-
priceFixed: takeProfit.price != null,
|
|
414
|
-
orderChainId: 0,
|
|
415
|
-
orderId: 0,
|
|
416
|
-
orderType: "LIMIT" /* LIMIT */,
|
|
417
|
-
quantityForProtection: qty,
|
|
418
|
-
removed: false
|
|
419
|
-
};
|
|
420
|
-
}
|
|
421
|
-
try {
|
|
422
|
-
const response = await retryRequest(
|
|
423
|
-
{
|
|
424
|
-
method: "POST",
|
|
425
|
-
url: endpoints.submitOrder(this.baseUrl),
|
|
426
|
-
data: orderData,
|
|
427
|
-
headers: authHeaders(this.csrf, serializeCookies(this.cookies))
|
|
428
|
-
},
|
|
429
|
-
this.retries
|
|
430
|
-
);
|
|
431
|
-
if (response.status !== 200) {
|
|
432
|
-
this.throwError(
|
|
433
|
-
"ORDER_SUBMIT_FAILED",
|
|
434
|
-
`Order submission failed with status: ${response.status}`
|
|
435
|
-
);
|
|
436
|
-
}
|
|
437
|
-
this.callbacks.onOrderPlaced?.({
|
|
438
|
-
status: response.status,
|
|
439
|
-
data: response.data
|
|
440
|
-
});
|
|
441
|
-
const wsUrl = endpoints.websocket(this.baseUrl);
|
|
442
|
-
const cookieStr = serializeCookies(this.cookies);
|
|
443
|
-
const orderUpdate = await waitForOrderUpdate(
|
|
444
|
-
wsUrl,
|
|
445
|
-
cookieStr,
|
|
446
|
-
3e4,
|
|
447
|
-
this.debug
|
|
448
|
-
);
|
|
449
|
-
this.callbacks.onOrderUpdate?.(orderUpdate);
|
|
450
|
-
return orderUpdate;
|
|
451
|
-
} catch (error) {
|
|
452
|
-
if (error instanceof DxtradeError) throw error;
|
|
453
|
-
const message = error instanceof Error ? error.response?.data?.message ?? error.message : "Unknown error";
|
|
454
|
-
this.throwError("ORDER_ERROR", `Error submitting order: ${message}`);
|
|
455
|
-
}
|
|
710
|
+
async getSymbolLimits() {
|
|
711
|
+
return getSymbolLimits(this._ctx);
|
|
456
712
|
}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
this.ensureSession();
|
|
460
|
-
try {
|
|
461
|
-
const response = await retryRequest(
|
|
462
|
-
{
|
|
463
|
-
method: "POST",
|
|
464
|
-
url: endpoints.assessments(this.baseUrl),
|
|
465
|
-
data: {
|
|
466
|
-
from: params.from,
|
|
467
|
-
instrument: params.instrument,
|
|
468
|
-
subtype: params.subtype ?? null,
|
|
469
|
-
to: params.to
|
|
470
|
-
},
|
|
471
|
-
headers: authHeaders(this.csrf, serializeCookies(this.cookies))
|
|
472
|
-
},
|
|
473
|
-
this.retries
|
|
474
|
-
);
|
|
475
|
-
return response.data;
|
|
476
|
-
} catch (error) {
|
|
477
|
-
if (error instanceof DxtradeError) throw error;
|
|
478
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
479
|
-
this.throwError(
|
|
480
|
-
"ASSESSMENTS_ERROR",
|
|
481
|
-
`Error fetching assessments: ${message}`
|
|
482
|
-
);
|
|
483
|
-
}
|
|
713
|
+
async submitOrder(params) {
|
|
714
|
+
return submitOrder(this._ctx, params);
|
|
484
715
|
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
await this.login();
|
|
488
|
-
await this.fetchCsrf();
|
|
489
|
-
const wsUrl = endpoints.websocket(this.baseUrl);
|
|
490
|
-
const cookieStr = serializeCookies(this.cookies);
|
|
491
|
-
await waitForHandshake(wsUrl, cookieStr, 3e4, this.debug);
|
|
492
|
-
if (this.config.accountId) {
|
|
493
|
-
await this.switchAccount(this.config.accountId);
|
|
494
|
-
await waitForHandshake(
|
|
495
|
-
endpoints.websocket(this.baseUrl),
|
|
496
|
-
serializeCookies(this.cookies),
|
|
497
|
-
3e4,
|
|
498
|
-
this.debug
|
|
499
|
-
);
|
|
500
|
-
}
|
|
716
|
+
async getAccountMetrics() {
|
|
717
|
+
return getAccountMetrics(this._ctx);
|
|
501
718
|
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
if (!this.csrf) {
|
|
505
|
-
throw new DxtradeError(
|
|
506
|
-
"NO_SESSION",
|
|
507
|
-
"No active session. Call login() and fetchCsrf() or connect() first."
|
|
508
|
-
);
|
|
509
|
-
}
|
|
719
|
+
async getInstruments(params = {}) {
|
|
720
|
+
return getInstruments(this._ctx, params);
|
|
510
721
|
}
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
this.callbacks.onError?.(error);
|
|
514
|
-
throw error;
|
|
722
|
+
async getAssessments(params) {
|
|
723
|
+
return getAssessments(this._ctx, params);
|
|
515
724
|
}
|
|
516
725
|
};
|
|
517
726
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -522,6 +731,8 @@ var DxtradeClient = class {
|
|
|
522
731
|
DxtradeError,
|
|
523
732
|
ORDER_TYPE,
|
|
524
733
|
SIDE,
|
|
734
|
+
TIF,
|
|
735
|
+
WS_MESSAGE,
|
|
525
736
|
endpoints,
|
|
526
737
|
resolveBrokerUrl
|
|
527
738
|
});
|