@danielgroen/dxtrade-api 1.0.0
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 +115 -0
- package/dist/index.d.mts +129 -0
- package/dist/index.d.ts +129 -0
- package/dist/index.js +528 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +484 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +40 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
// src/constants/brokers.ts
|
|
2
|
+
var BROKER = {
|
|
3
|
+
LARK: "https://trade.gooeytrade.com",
|
|
4
|
+
EIGHTCAP: "https://trader.dx-eightcap.com"
|
|
5
|
+
};
|
|
6
|
+
function resolveBrokerUrl(broker, customUrls) {
|
|
7
|
+
if (customUrls?.[broker]) {
|
|
8
|
+
return customUrls[broker];
|
|
9
|
+
}
|
|
10
|
+
const key = broker.toUpperCase();
|
|
11
|
+
if (BROKER[key]) {
|
|
12
|
+
return BROKER[key];
|
|
13
|
+
}
|
|
14
|
+
return `https://dxtrade.${broker}.com`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// src/constants/endpoints.ts
|
|
18
|
+
var endpoints = {
|
|
19
|
+
login: (base) => `${base}/api/auth/login`,
|
|
20
|
+
switchAccount: (base, id) => `${base}/api/accounts/switch?accountId=${id}`,
|
|
21
|
+
suggest: (base, text) => `${base}/api/suggest?text=${text}`,
|
|
22
|
+
instrumentInfo: (base, symbol, tzOffset) => `${base}/api/instruments/info?symbol=${symbol}&timezoneOffset=${tzOffset}&withExDividends=true`,
|
|
23
|
+
submitOrder: (base) => `${base}/api/orders/single`,
|
|
24
|
+
assessments: (base) => `${base}/api/assessments`,
|
|
25
|
+
websocket: (base) => `wss://${base.split("//")[1]}/client/connector?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`
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// src/constants/enums.ts
|
|
29
|
+
var ORDER_TYPE = /* @__PURE__ */ ((ORDER_TYPE2) => {
|
|
30
|
+
ORDER_TYPE2["MARKET"] = "MARKET";
|
|
31
|
+
ORDER_TYPE2["LIMIT"] = "LIMIT";
|
|
32
|
+
ORDER_TYPE2["STOP"] = "STOP";
|
|
33
|
+
return ORDER_TYPE2;
|
|
34
|
+
})(ORDER_TYPE || {});
|
|
35
|
+
var SIDE = /* @__PURE__ */ ((SIDE2) => {
|
|
36
|
+
SIDE2["BUY"] = "BUY";
|
|
37
|
+
SIDE2["SELL"] = "SELL";
|
|
38
|
+
return SIDE2;
|
|
39
|
+
})(SIDE || {});
|
|
40
|
+
var ACTION = /* @__PURE__ */ ((ACTION2) => {
|
|
41
|
+
ACTION2["OPENING"] = "OPENING";
|
|
42
|
+
ACTION2["CLOSING"] = "CLOSING";
|
|
43
|
+
return ACTION2;
|
|
44
|
+
})(ACTION || {});
|
|
45
|
+
|
|
46
|
+
// src/constants/errors.ts
|
|
47
|
+
var DxtradeError = class extends Error {
|
|
48
|
+
code;
|
|
49
|
+
constructor(code, message) {
|
|
50
|
+
super(message);
|
|
51
|
+
this.name = "DxtradeError";
|
|
52
|
+
this.code = code;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// src/client.ts
|
|
57
|
+
import crypto from "crypto";
|
|
58
|
+
|
|
59
|
+
// src/utils/cookies.ts
|
|
60
|
+
function parseCookies(setCookieHeaders) {
|
|
61
|
+
const cookies = {};
|
|
62
|
+
for (const cookie of setCookieHeaders) {
|
|
63
|
+
const [nameValue] = cookie.split(";");
|
|
64
|
+
const eqIndex = nameValue.indexOf("=");
|
|
65
|
+
if (eqIndex === -1) continue;
|
|
66
|
+
const name = nameValue.slice(0, eqIndex).trim();
|
|
67
|
+
const value = nameValue.slice(eqIndex + 1).trim();
|
|
68
|
+
cookies[name] = value;
|
|
69
|
+
}
|
|
70
|
+
return cookies;
|
|
71
|
+
}
|
|
72
|
+
function serializeCookies(cookies) {
|
|
73
|
+
return Object.entries(cookies).map(([key, value]) => `${key}=${value}`).join("; ");
|
|
74
|
+
}
|
|
75
|
+
function mergeCookies(existing, incoming) {
|
|
76
|
+
return { ...existing, ...incoming };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/utils/headers.ts
|
|
80
|
+
function baseHeaders() {
|
|
81
|
+
return {
|
|
82
|
+
"Content-Type": "application/json; charset=UTF-8",
|
|
83
|
+
"Accept-Language": "en-US,en;q=0.9"
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function authHeaders(csrf, cookieStr) {
|
|
87
|
+
return {
|
|
88
|
+
...baseHeaders(),
|
|
89
|
+
"X-CSRF-Token": csrf,
|
|
90
|
+
"X-Requested-With": "XMLHttpRequest",
|
|
91
|
+
Accept: "*/*",
|
|
92
|
+
Cookie: cookieStr
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function cookieOnlyHeaders(cookieStr) {
|
|
96
|
+
return { Cookie: cookieStr };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// src/utils/retry.ts
|
|
100
|
+
import axios from "axios";
|
|
101
|
+
async function retryRequest(config, retries = 3) {
|
|
102
|
+
for (let attempt = 1; attempt <= retries; attempt++) {
|
|
103
|
+
try {
|
|
104
|
+
return await axios(config);
|
|
105
|
+
} catch (error) {
|
|
106
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
107
|
+
console.warn(`[dxtrade-api] Attempt ${attempt} failed: ${message}`);
|
|
108
|
+
if (attempt === retries) throw error;
|
|
109
|
+
await new Promise((res) => setTimeout(res, 1e3 * attempt));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
throw new Error("[dxtrade-api] Failed after retries");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// src/utils/websocket.ts
|
|
116
|
+
import WebSocket from "ws";
|
|
117
|
+
function waitForHandshake(wsUrl, cookieStr, timeout = 3e4, debug = false) {
|
|
118
|
+
return new Promise((resolve, reject) => {
|
|
119
|
+
const ws = new WebSocket(wsUrl, { headers: { Cookie: cookieStr } });
|
|
120
|
+
const timer = setTimeout(() => {
|
|
121
|
+
ws.close();
|
|
122
|
+
reject(new Error("[dxtrade-api] Handshake timed out"));
|
|
123
|
+
}, timeout);
|
|
124
|
+
ws.on("message", (data) => {
|
|
125
|
+
const str = data.toString();
|
|
126
|
+
if (debug) console.log("[dxtrade-api:ws]", str);
|
|
127
|
+
if (str.includes(`"POSITIONS"`)) {
|
|
128
|
+
clearTimeout(timer);
|
|
129
|
+
ws.close();
|
|
130
|
+
resolve();
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
ws.on("error", (error) => {
|
|
134
|
+
clearTimeout(timer);
|
|
135
|
+
ws.close();
|
|
136
|
+
reject(new Error(`[dxtrade-api] WebSocket handshake error: ${error.message}`));
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
function waitForOrderUpdate(wsUrl, cookieStr, timeout = 3e4, debug = false) {
|
|
141
|
+
return new Promise((resolve, reject) => {
|
|
142
|
+
const ws = new WebSocket(wsUrl, { headers: { Cookie: cookieStr } });
|
|
143
|
+
const timer = setTimeout(() => {
|
|
144
|
+
ws.close();
|
|
145
|
+
reject(new Error("[dxtrade-api] Order update timed out"));
|
|
146
|
+
}, timeout);
|
|
147
|
+
ws.on("message", (data) => {
|
|
148
|
+
const str = data.toString();
|
|
149
|
+
if (debug) console.log("[dxtrade-api:ws]", str);
|
|
150
|
+
if (!str.includes(`"ORDERS"`)) return;
|
|
151
|
+
if (!str.includes(`orderId`)) return;
|
|
152
|
+
const json = str.replace(/^.*?\{/, "{");
|
|
153
|
+
if (!json.includes("{")) return;
|
|
154
|
+
try {
|
|
155
|
+
const response = JSON.parse(json);
|
|
156
|
+
const body = response.body?.[0];
|
|
157
|
+
if (!body) return;
|
|
158
|
+
clearTimeout(timer);
|
|
159
|
+
ws.close();
|
|
160
|
+
if (body.status === "REJECTED") {
|
|
161
|
+
reject(new Error(`[dxtrade-api] Order rejected: ${body.statusDescription ?? "Unknown reason"}`));
|
|
162
|
+
} else {
|
|
163
|
+
resolve(body);
|
|
164
|
+
}
|
|
165
|
+
} catch {
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
ws.on("error", (error) => {
|
|
169
|
+
clearTimeout(timer);
|
|
170
|
+
ws.close();
|
|
171
|
+
reject(new Error(`[dxtrade-api] WebSocket order listener error: ${error.message}`));
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// src/client.ts
|
|
177
|
+
var DxtradeClient = class {
|
|
178
|
+
config;
|
|
179
|
+
callbacks;
|
|
180
|
+
cookies = {};
|
|
181
|
+
csrf = null;
|
|
182
|
+
baseUrl;
|
|
183
|
+
retries;
|
|
184
|
+
debug;
|
|
185
|
+
constructor(config) {
|
|
186
|
+
this.config = config;
|
|
187
|
+
this.callbacks = config.callbacks ?? {};
|
|
188
|
+
this.baseUrl = resolveBrokerUrl(config.broker, config.brokerUrls);
|
|
189
|
+
this.retries = config.retries ?? 3;
|
|
190
|
+
this.debug = config.debug ?? false;
|
|
191
|
+
}
|
|
192
|
+
// ── Session ─────────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
193
|
+
async login() {
|
|
194
|
+
try {
|
|
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
|
+
}
|
|
221
|
+
}
|
|
222
|
+
async fetchCsrf() {
|
|
223
|
+
try {
|
|
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
|
+
}
|
|
244
|
+
}
|
|
245
|
+
async switchAccount(accountId) {
|
|
246
|
+
this.ensureSession();
|
|
247
|
+
try {
|
|
248
|
+
await retryRequest(
|
|
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
|
+
}
|
|
265
|
+
}
|
|
266
|
+
// ── Market Data ─────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
267
|
+
async getSymbolSuggestions(text) {
|
|
268
|
+
this.ensureSession();
|
|
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
|
+
}
|
|
292
|
+
}
|
|
293
|
+
async getSymbolInfo(symbol) {
|
|
294
|
+
this.ensureSession();
|
|
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
|
+
}
|
|
318
|
+
}
|
|
319
|
+
// ── Trading ─────────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
320
|
+
async submitOrder(params) {
|
|
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
|
+
}
|
|
413
|
+
}
|
|
414
|
+
// ── Analytics ───────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
415
|
+
async getAssessments(params) {
|
|
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
|
+
}
|
|
441
|
+
}
|
|
442
|
+
// ── Convenience ─────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
443
|
+
async connect() {
|
|
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
|
+
}
|
|
458
|
+
}
|
|
459
|
+
// ── Internals ───────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
460
|
+
ensureSession() {
|
|
461
|
+
if (!this.csrf) {
|
|
462
|
+
throw new DxtradeError(
|
|
463
|
+
"NO_SESSION",
|
|
464
|
+
"No active session. Call login() and fetchCsrf() or connect() first."
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
throwError(code, message) {
|
|
469
|
+
const error = new DxtradeError(code, message);
|
|
470
|
+
this.callbacks.onError?.(error);
|
|
471
|
+
throw error;
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
export {
|
|
475
|
+
ACTION,
|
|
476
|
+
BROKER,
|
|
477
|
+
DxtradeClient,
|
|
478
|
+
DxtradeError,
|
|
479
|
+
ORDER_TYPE,
|
|
480
|
+
SIDE,
|
|
481
|
+
endpoints,
|
|
482
|
+
resolveBrokerUrl
|
|
483
|
+
};
|
|
484
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/constants/brokers.ts","../src/constants/endpoints.ts","../src/constants/enums.ts","../src/constants/errors.ts","../src/client.ts","../src/utils/cookies.ts","../src/utils/headers.ts","../src/utils/retry.ts","../src/utils/websocket.ts"],"sourcesContent":["export const BROKER = {\n LARK: \"https://trade.gooeytrade.com\",\n EIGHTCAP: \"https://trader.dx-eightcap.com\",\n} as const;\n\nexport function resolveBrokerUrl(broker: string, customUrls?: Record<string, string>): string {\n if (customUrls?.[broker]) {\n return customUrls[broker];\n }\n\n const key = broker.toUpperCase() as keyof typeof BROKER;\n if (BROKER[key]) {\n return BROKER[key];\n }\n\n return `https://dxtrade.${broker}.com`;\n}\n","export const endpoints = {\n login: (base: string) => `${base}/api/auth/login`,\n\n switchAccount: (base: string, id: string) => `${base}/api/accounts/switch?accountId=${id}`,\n\n suggest: (base: string, text: string) => `${base}/api/suggest?text=${text}`,\n\n instrumentInfo: (base: string, symbol: string, tzOffset: number) =>\n `${base}/api/instruments/info?symbol=${symbol}&timezoneOffset=${tzOffset}&withExDividends=true`,\n\n submitOrder: (base: string) => `${base}/api/orders/single`,\n\n assessments: (base: string) => `${base}/api/assessments`,\n\n websocket: (base: string) =>\n `wss://${base.split(\"//\")[1]}/client/connector`\n + `?X-Atmosphere-tracking-id=0&X-Atmosphere-Framework=2.3.2-javascript`\n + `&X-Atmosphere-Transport=websocket&X-Atmosphere-TrackMessageSize=true`\n + `&Content-Type=text/x-gwt-rpc;%20charset=UTF-8&X-atmo-protocol=true`\n + `&sessionState=dx-new&guest-mode=false`,\n};\n","export enum ORDER_TYPE {\n MARKET = \"MARKET\",\n LIMIT = \"LIMIT\",\n STOP = \"STOP\",\n}\n\nexport enum SIDE {\n BUY = \"BUY\",\n SELL = \"SELL\",\n}\n\nexport enum ACTION {\n OPENING = \"OPENING\",\n CLOSING = \"CLOSING\",\n}\n","export class DxtradeError extends Error {\n public code: string;\n\n constructor(code: string, message: string) {\n super(message);\n this.name = \"DxtradeError\";\n this.code = code;\n }\n}\n","import crypto from \"crypto\";\nimport {\n endpoints,\n ORDER_TYPE,\n SIDE,\n ACTION,\n DxtradeError,\n resolveBrokerUrl,\n} from \"@/constants\";\nimport {\n parseCookies,\n serializeCookies,\n mergeCookies,\n baseHeaders,\n authHeaders,\n cookieOnlyHeaders,\n retryRequest,\n waitForHandshake,\n waitForOrderUpdate,\n} from \"@/utils\";\n\nexport class DxtradeClient {\n private config: DxtradeConfig;\n private callbacks: DxtradeCallbacks;\n private cookies: Record<string, string> = {};\n private csrf: string | null = null;\n private baseUrl: string;\n private retries: number;\n private debug: boolean;\n\n constructor(config: DxtradeConfig) {\n this.config = config;\n this.callbacks = config.callbacks ?? {};\n this.baseUrl = resolveBrokerUrl(config.broker, config.brokerUrls);\n this.retries = config.retries ?? 3;\n this.debug = config.debug ?? false;\n }\n\n // ── Session ─────────────────────────────────────────────────────────────────────────────────────────────────────────\n\n async login(): Promise<void> {\n try {\n const response = await retryRequest(\n {\n method: \"POST\",\n url: endpoints.login(this.baseUrl),\n data: {\n username: this.config.username,\n password: this.config.password,\n domain: this.config.broker,\n },\n headers: { \"Content-Type\": \"application/json\" },\n },\n this.retries,\n );\n\n if (response.status === 200) {\n const setCookies = response.headers[\"set-cookie\"] ?? [];\n const incoming = parseCookies(setCookies);\n this.cookies = mergeCookies(this.cookies, incoming);\n this.callbacks.onLogin?.();\n } else {\n this.throwError(\"LOGIN_FAILED\", `Login failed: ${response.status}`);\n }\n } catch (error: unknown) {\n if (error instanceof DxtradeError) throw error;\n const message = error instanceof Error ? error.message : \"Unknown error\";\n this.throwError(\"LOGIN_ERROR\", `Login error: ${message}`);\n }\n }\n\n async fetchCsrf(): Promise<void> {\n try {\n const cookieStr = serializeCookies(this.cookies);\n const response = await retryRequest(\n {\n method: \"GET\",\n url: this.baseUrl,\n headers: { ...cookieOnlyHeaders(cookieStr), Referer: this.baseUrl },\n },\n this.retries,\n );\n\n const csrfMatch = response.data?.match(/name=\"csrf\" content=\"([^\"]+)\"/);\n if (csrfMatch) {\n this.csrf = csrfMatch[1];\n } else {\n this.throwError(\"CSRF_NOT_FOUND\", \"CSRF token not found\");\n }\n } catch (error: unknown) {\n if (error instanceof DxtradeError) throw error;\n const message = error instanceof Error ? error.message : \"Unknown error\";\n this.throwError(\"CSRF_ERROR\", `CSRF fetch error: ${message}`);\n }\n }\n\n async switchAccount(accountId: string): Promise<void> {\n this.ensureSession();\n\n try {\n await retryRequest(\n {\n method: \"POST\",\n url: endpoints.switchAccount(this.baseUrl, accountId),\n headers: authHeaders(this.csrf!, serializeCookies(this.cookies)),\n },\n this.retries,\n );\n this.callbacks.onAccountSwitch?.(accountId);\n } catch (error: unknown) {\n if (error instanceof DxtradeError) throw error;\n const message = error instanceof Error ? error.message : \"Unknown error\";\n this.throwError(\n \"ACCOUNT_SWITCH_ERROR\",\n `Error switching account: ${message}`,\n );\n }\n }\n\n // ── Market Data ─────────────────────────────────────────────────────────────────────────────────────────────────────\n\n async getSymbolSuggestions(text: string): Promise<SymbolSuggestion[]> {\n this.ensureSession();\n\n try {\n const cookieStr = serializeCookies(this.cookies);\n const response = await retryRequest(\n {\n method: \"GET\",\n url: endpoints.suggest(this.baseUrl, text),\n headers: { ...baseHeaders(), Cookie: cookieStr },\n },\n this.retries,\n );\n\n const suggests = response.data?.suggests;\n if (!suggests?.length) {\n this.throwError(\"NO_SUGGESTIONS\", \"No symbol suggestions found\");\n }\n return suggests as SymbolSuggestion[];\n } catch (error: unknown) {\n if (error instanceof DxtradeError) throw error;\n const message = error instanceof Error ? error.message : \"Unknown error\";\n this.throwError(\n \"SUGGEST_ERROR\",\n `Error getting symbol suggestions: ${message}`,\n );\n }\n }\n\n async getSymbolInfo(symbol: string): Promise<SymbolInfo> {\n this.ensureSession();\n\n try {\n const offsetMinutes = Math.abs(new Date().getTimezoneOffset());\n const cookieStr = serializeCookies(this.cookies);\n const response = await retryRequest(\n {\n method: \"GET\",\n url: endpoints.instrumentInfo(this.baseUrl, symbol, offsetMinutes),\n headers: { ...baseHeaders(), Cookie: cookieStr },\n },\n this.retries,\n );\n\n if (!response.data) {\n this.throwError(\"NO_SYMBOL_INFO\", \"No symbol info returned\");\n }\n return response.data as SymbolInfo;\n } catch (error: unknown) {\n if (error instanceof DxtradeError) throw error;\n const message = error instanceof Error ? error.message : \"Unknown error\";\n this.throwError(\n \"SYMBOL_INFO_ERROR\",\n `Error getting symbol info: ${message}`,\n );\n }\n }\n\n // ── Trading ─────────────────────────────────────────────────────────────────────────────────────────────────────────\n\n async submitOrder(params: SubmitOrderParams): Promise<OrderUpdate> {\n this.ensureSession();\n\n const {\n symbol,\n side,\n quantity,\n orderType,\n price,\n instrumentId,\n stopLoss,\n takeProfit,\n positionEffect = ACTION.OPENING,\n } = params;\n const qty = side === SIDE.BUY ? Math.abs(quantity) : -Math.abs(quantity);\n const priceParam =\n orderType === ORDER_TYPE.STOP ? \"stopPrice\" : \"limitPrice\";\n\n const orderData: Record<string, unknown> = {\n directExchange: false,\n legs: [\n {\n ...(instrumentId != null && { instrumentId }),\n positionEffect,\n ratioQuantity: 1,\n symbol,\n },\n ],\n orderSide: side,\n orderType,\n quantity: qty,\n requestId: `gwt-uid-931-${crypto.randomUUID()}`,\n timeInForce: \"GTC\",\n };\n\n if (price != null && orderType !== ORDER_TYPE.MARKET) {\n orderData[priceParam] = price;\n }\n\n if (stopLoss) {\n orderData.stopLoss = {\n ...(stopLoss.offset != null && { fixedOffset: stopLoss.offset }),\n ...(stopLoss.price != null && { fixedPrice: stopLoss.price }),\n priceFixed: stopLoss.price != null,\n orderChainId: 0,\n orderId: 0,\n orderType: ORDER_TYPE.STOP,\n quantityForProtection: qty,\n removed: false,\n };\n }\n\n if (takeProfit) {\n orderData.takeProfit = {\n ...(takeProfit.offset != null && { fixedOffset: takeProfit.offset }),\n ...(takeProfit.price != null && { fixedPrice: takeProfit.price }),\n priceFixed: takeProfit.price != null,\n orderChainId: 0,\n orderId: 0,\n orderType: ORDER_TYPE.LIMIT,\n quantityForProtection: qty,\n removed: false,\n };\n }\n\n try {\n const response = await retryRequest(\n {\n method: \"POST\",\n url: endpoints.submitOrder(this.baseUrl),\n data: orderData,\n headers: authHeaders(this.csrf!, serializeCookies(this.cookies)),\n },\n this.retries,\n );\n\n if (response.status !== 200) {\n this.throwError(\n \"ORDER_SUBMIT_FAILED\",\n `Order submission failed with status: ${response.status}`,\n );\n }\n\n this.callbacks.onOrderPlaced?.({\n status: response.status,\n data: response.data,\n });\n\n const wsUrl = endpoints.websocket(this.baseUrl);\n const cookieStr = serializeCookies(this.cookies);\n const orderUpdate = await waitForOrderUpdate(\n wsUrl,\n cookieStr,\n 30_000,\n this.debug,\n );\n\n this.callbacks.onOrderUpdate?.(orderUpdate);\n return orderUpdate;\n } catch (error: unknown) {\n if (error instanceof DxtradeError) throw error;\n const message =\n error instanceof Error\n ? ((error as any).response?.data?.message ?? error.message)\n : \"Unknown error\";\n this.throwError(\"ORDER_ERROR\", `Error submitting order: ${message}`);\n }\n }\n\n // ── Analytics ───────────────────────────────────────────────────────────────────────────────────────────────────────\n\n async getAssessments(\n params: AssessmentsParams,\n ): Promise<AssessmentsResponse> {\n this.ensureSession();\n\n try {\n const response = await retryRequest(\n {\n method: \"POST\",\n url: endpoints.assessments(this.baseUrl),\n data: {\n from: params.from,\n instrument: params.instrument,\n subtype: params.subtype ?? null,\n to: params.to,\n },\n headers: authHeaders(this.csrf!, serializeCookies(this.cookies)),\n },\n this.retries,\n );\n\n return response.data as AssessmentsResponse;\n } catch (error: unknown) {\n if (error instanceof DxtradeError) throw error;\n const message = error instanceof Error ? error.message : \"Unknown error\";\n this.throwError(\n \"ASSESSMENTS_ERROR\",\n `Error fetching assessments: ${message}`,\n );\n }\n }\n\n // ── Convenience ─────────────────────────────────────────────────────────────────────────────────────────────────────\n\n async connect(): Promise<void> {\n await this.login();\n await this.fetchCsrf();\n\n const wsUrl = endpoints.websocket(this.baseUrl);\n const cookieStr = serializeCookies(this.cookies);\n await waitForHandshake(wsUrl, cookieStr, 30_000, this.debug);\n\n if (this.config.accountId) {\n await this.switchAccount(this.config.accountId);\n await waitForHandshake(\n endpoints.websocket(this.baseUrl),\n serializeCookies(this.cookies),\n 30_000,\n this.debug,\n );\n }\n }\n\n // ── Internals ───────────────────────────────────────────────────────────────────────────────────────────────────────\n\n private ensureSession(): void {\n if (!this.csrf) {\n throw new DxtradeError(\n \"NO_SESSION\",\n \"No active session. Call login() and fetchCsrf() or connect() first.\",\n );\n }\n }\n\n private throwError(code: string, message: string): never {\n const error = new DxtradeError(code, message);\n this.callbacks.onError?.(error);\n throw error;\n }\n}\n","export function parseCookies(setCookieHeaders: string[]): Record<string, string> {\n const cookies: Record<string, string> = {};\n\n for (const cookie of setCookieHeaders) {\n const [nameValue] = cookie.split(\";\");\n const eqIndex = nameValue.indexOf(\"=\");\n if (eqIndex === -1) continue;\n const name = nameValue.slice(0, eqIndex).trim();\n const value = nameValue.slice(eqIndex + 1).trim();\n cookies[name] = value;\n }\n\n return cookies;\n}\n\nexport function serializeCookies(cookies: Record<string, string>): string {\n return Object.entries(cookies)\n .map(([key, value]) => `${key}=${value}`)\n .join(\"; \");\n}\n\nexport function mergeCookies(existing: Record<string, string>, incoming: Record<string, string>): Record<string, string> {\n return { ...existing, ...incoming };\n}\n","export function baseHeaders(): Record<string, string> {\n return {\n \"Content-Type\": \"application/json; charset=UTF-8\",\n \"Accept-Language\": \"en-US,en;q=0.9\",\n };\n}\n\nexport function authHeaders(csrf: string, cookieStr: string): Record<string, string> {\n return {\n ...baseHeaders(),\n \"X-CSRF-Token\": csrf,\n \"X-Requested-With\": \"XMLHttpRequest\",\n Accept: \"*/*\",\n Cookie: cookieStr,\n };\n}\n\nexport function cookieOnlyHeaders(cookieStr: string): Record<string, string> {\n return { Cookie: cookieStr };\n}\n","import axios, { type AxiosRequestConfig, type AxiosResponse } from \"axios\";\n\nexport async function retryRequest(config: AxiosRequestConfig, retries = 3): Promise<AxiosResponse> {\n for (let attempt = 1; attempt <= retries; attempt++) {\n try {\n return await axios(config);\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : \"Unknown error\";\n console.warn(`[dxtrade-api] Attempt ${attempt} failed: ${message}`);\n if (attempt === retries) throw error;\n await new Promise((res) => setTimeout(res, 1000 * attempt));\n }\n }\n throw new Error(\"[dxtrade-api] Failed after retries\");\n}\n","import WebSocket from \"ws\";\n\nexport function waitForHandshake(wsUrl: string, cookieStr: string, timeout = 30_000, debug = false): Promise<void> {\n return new Promise((resolve, reject) => {\n const ws = new WebSocket(wsUrl, { headers: { Cookie: cookieStr } });\n\n const timer = setTimeout(() => {\n ws.close();\n reject(new Error(\"[dxtrade-api] Handshake timed out\"));\n }, timeout);\n\n ws.on(\"message\", (data) => {\n const str = data.toString();\n if (debug) console.log(\"[dxtrade-api:ws]\", str);\n\n if (str.includes(`\"POSITIONS\"`)) {\n clearTimeout(timer);\n ws.close();\n resolve();\n }\n });\n\n ws.on(\"error\", (error) => {\n clearTimeout(timer);\n ws.close();\n reject(new Error(`[dxtrade-api] WebSocket handshake error: ${error.message}`));\n });\n });\n}\n\nexport function waitForOrderUpdate(wsUrl: string, cookieStr: string, timeout = 30_000, debug = false): Promise<OrderUpdate> {\n return new Promise((resolve, reject) => {\n const ws = new WebSocket(wsUrl, { headers: { Cookie: cookieStr } });\n\n const timer = setTimeout(() => {\n ws.close();\n reject(new Error(\"[dxtrade-api] Order update timed out\"));\n }, timeout);\n\n ws.on(\"message\", (data) => {\n const str = data.toString();\n if (debug) console.log(\"[dxtrade-api:ws]\", str);\n\n if (!str.includes(`\"ORDERS\"`)) return;\n if (!str.includes(`orderId`)) return;\n\n const json = str.replace(/^.*?\\{/, \"{\");\n if (!json.includes(\"{\")) return;\n\n try {\n const response = JSON.parse(json);\n const body = response.body?.[0] as OrderUpdate | undefined;\n if (!body) return;\n\n clearTimeout(timer);\n ws.close();\n\n if (body.status === \"REJECTED\") {\n reject(new Error(`[dxtrade-api] Order rejected: ${body.statusDescription ?? \"Unknown reason\"}`));\n } else {\n resolve(body);\n }\n } catch {\n // ignore parse errors, wait for next message\n }\n });\n\n ws.on(\"error\", (error) => {\n clearTimeout(timer);\n ws.close();\n reject(new Error(`[dxtrade-api] WebSocket order listener error: ${error.message}`));\n });\n });\n}\n"],"mappings":"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","names":["ORDER_TYPE","SIDE","ACTION"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@danielgroen/dxtrade-api",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "DXTrade trading API client library",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"gen:globals": "tsx scripts/generate-globals.ts",
|
|
20
|
+
"build": "npm run gen:globals && tsup",
|
|
21
|
+
"dev": "npm run gen:globals && tsup --watch",
|
|
22
|
+
"example:connect": "tsx examples/connect.ts",
|
|
23
|
+
"example:order": "tsx examples/submit-order.ts",
|
|
24
|
+
"example:assessments": "tsx examples/get-assessments.ts",
|
|
25
|
+
"example:symbol": "tsx examples/symbol-info.ts",
|
|
26
|
+
"example:symbol:btc": "tsx examples/symbol-info.ts BTCUSD"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"axios": "^1.7.9",
|
|
30
|
+
"ws": "^8.18.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/ws": "^8.5.14",
|
|
34
|
+
"dotenv": "^16.4.7",
|
|
35
|
+
"tsup": "^8.3.6",
|
|
36
|
+
"tsx": "^4.19.2",
|
|
37
|
+
"typescript": "^5.7.3"
|
|
38
|
+
},
|
|
39
|
+
"license": "MIT"
|
|
40
|
+
}
|