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