@danielgroen/dxtrade-api 1.0.8 → 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/dist/index.d.mts +84 -11
- package/dist/index.d.ts +84 -11
- package/dist/index.js +211 -85
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +211 -85
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -1
package/dist/index.d.mts
CHANGED
|
@@ -33,8 +33,11 @@ declare enum TIF {
|
|
|
33
33
|
GTD = "GTD"
|
|
34
34
|
}
|
|
35
35
|
declare enum WS_MESSAGE {
|
|
36
|
+
ACCOUNT_METRICS = "ACCOUNT_METRICS",
|
|
36
37
|
ACCOUNTS = "ACCOUNTS",
|
|
37
38
|
AVAILABLE_WATCHLISTS = "AVAILABLE_WATCHLISTS",
|
|
39
|
+
INSTRUMENTS = "INSTRUMENTS",
|
|
40
|
+
LIMITS = "LIMITS",
|
|
38
41
|
MESSAGE = "MESSAGE",
|
|
39
42
|
ORDERS = "ORDERS",
|
|
40
43
|
POSITIONS = "POSITIONS",
|
|
@@ -162,17 +165,18 @@ interface WsPayload {
|
|
|
162
165
|
type: string;
|
|
163
166
|
}
|
|
164
167
|
|
|
165
|
-
declare namespace
|
|
166
|
-
interface
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
168
|
+
declare namespace Account {
|
|
169
|
+
interface Metrics {
|
|
170
|
+
availableFunds: number;
|
|
171
|
+
marginCallLevel: number | string;
|
|
172
|
+
riskLevel: number;
|
|
173
|
+
openPl: number;
|
|
174
|
+
cashBalance: number;
|
|
175
|
+
equity: number;
|
|
176
|
+
conversionRate: number;
|
|
177
|
+
initialMargin: number;
|
|
178
|
+
availableBalance: number;
|
|
179
|
+
reverseRiskLevel: number | string;
|
|
176
180
|
[key: string]: unknown;
|
|
177
181
|
}
|
|
178
182
|
}
|
|
@@ -190,6 +194,72 @@ declare namespace Assessments {
|
|
|
190
194
|
}
|
|
191
195
|
}
|
|
192
196
|
|
|
197
|
+
declare namespace Instrument {
|
|
198
|
+
interface Info {
|
|
199
|
+
id: number;
|
|
200
|
+
symbol: string;
|
|
201
|
+
description: string;
|
|
202
|
+
type: string;
|
|
203
|
+
subtype: string;
|
|
204
|
+
currency: string;
|
|
205
|
+
currencyPrecision: number;
|
|
206
|
+
precision: number;
|
|
207
|
+
pipsSize: number;
|
|
208
|
+
quantityIncrement: number;
|
|
209
|
+
quantityPrecision: number;
|
|
210
|
+
priceIncrement: number;
|
|
211
|
+
version: number;
|
|
212
|
+
priceIncrementsTO: {
|
|
213
|
+
priceIncrements: number[];
|
|
214
|
+
pricePrecisions: number[];
|
|
215
|
+
bondFraction: boolean;
|
|
216
|
+
};
|
|
217
|
+
lotSize: number;
|
|
218
|
+
baseCurrency: string | null;
|
|
219
|
+
lotName: string | null;
|
|
220
|
+
multiplier: number;
|
|
221
|
+
open: boolean;
|
|
222
|
+
expiration: string | null;
|
|
223
|
+
firstNoticeDate: string | null;
|
|
224
|
+
lastTradeDate: string | null;
|
|
225
|
+
underlying: string | null;
|
|
226
|
+
mmy: string | null;
|
|
227
|
+
optionParametersTO: unknown;
|
|
228
|
+
unitName: string | null;
|
|
229
|
+
additionalFields: unknown;
|
|
230
|
+
additionalObject: unknown;
|
|
231
|
+
currencyParametersTO: unknown;
|
|
232
|
+
tradingHours: string;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
declare namespace Symbol {
|
|
237
|
+
interface Suggestion {
|
|
238
|
+
id: number;
|
|
239
|
+
name: string;
|
|
240
|
+
[key: string]: unknown;
|
|
241
|
+
}
|
|
242
|
+
interface Info {
|
|
243
|
+
maxVolume: number;
|
|
244
|
+
minVolume: number;
|
|
245
|
+
volumeStep: number;
|
|
246
|
+
lotSize: number;
|
|
247
|
+
[key: string]: unknown;
|
|
248
|
+
}
|
|
249
|
+
interface Limits {
|
|
250
|
+
symbol: string;
|
|
251
|
+
instrumentId: number;
|
|
252
|
+
limitStopDistanceType: string;
|
|
253
|
+
limitStopDistance: number;
|
|
254
|
+
limitStopDistanceInPercentOfSpread: number;
|
|
255
|
+
minOrderSize: number;
|
|
256
|
+
minOrderSizeBypass: boolean;
|
|
257
|
+
maxOrderSize: number;
|
|
258
|
+
minOrderIncrement: number;
|
|
259
|
+
limitType: string;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
193
263
|
declare class DxtradeClient {
|
|
194
264
|
private _ctx;
|
|
195
265
|
constructor(config: DxtradeConfig);
|
|
@@ -199,7 +269,10 @@ declare class DxtradeClient {
|
|
|
199
269
|
connect(): Promise<void>;
|
|
200
270
|
getSymbolSuggestions(text: string): Promise<Symbol.Suggestion[]>;
|
|
201
271
|
getSymbolInfo(symbol: string): Promise<Symbol.Info>;
|
|
272
|
+
getSymbolLimits(): Promise<Symbol.Limits[]>;
|
|
202
273
|
submitOrder(params: Order.SubmitParams): Promise<Order.Update>;
|
|
274
|
+
getAccountMetrics(): Promise<Account.Metrics>;
|
|
275
|
+
getInstruments(params?: Partial<Instrument.Info>): Promise<Instrument.Info[]>;
|
|
203
276
|
getAssessments(params: Assessments.Params): Promise<Assessments.Response>;
|
|
204
277
|
}
|
|
205
278
|
|
package/dist/index.d.ts
CHANGED
|
@@ -33,8 +33,11 @@ declare enum TIF {
|
|
|
33
33
|
GTD = "GTD"
|
|
34
34
|
}
|
|
35
35
|
declare enum WS_MESSAGE {
|
|
36
|
+
ACCOUNT_METRICS = "ACCOUNT_METRICS",
|
|
36
37
|
ACCOUNTS = "ACCOUNTS",
|
|
37
38
|
AVAILABLE_WATCHLISTS = "AVAILABLE_WATCHLISTS",
|
|
39
|
+
INSTRUMENTS = "INSTRUMENTS",
|
|
40
|
+
LIMITS = "LIMITS",
|
|
38
41
|
MESSAGE = "MESSAGE",
|
|
39
42
|
ORDERS = "ORDERS",
|
|
40
43
|
POSITIONS = "POSITIONS",
|
|
@@ -162,17 +165,18 @@ interface WsPayload {
|
|
|
162
165
|
type: string;
|
|
163
166
|
}
|
|
164
167
|
|
|
165
|
-
declare namespace
|
|
166
|
-
interface
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
168
|
+
declare namespace Account {
|
|
169
|
+
interface Metrics {
|
|
170
|
+
availableFunds: number;
|
|
171
|
+
marginCallLevel: number | string;
|
|
172
|
+
riskLevel: number;
|
|
173
|
+
openPl: number;
|
|
174
|
+
cashBalance: number;
|
|
175
|
+
equity: number;
|
|
176
|
+
conversionRate: number;
|
|
177
|
+
initialMargin: number;
|
|
178
|
+
availableBalance: number;
|
|
179
|
+
reverseRiskLevel: number | string;
|
|
176
180
|
[key: string]: unknown;
|
|
177
181
|
}
|
|
178
182
|
}
|
|
@@ -190,6 +194,72 @@ declare namespace Assessments {
|
|
|
190
194
|
}
|
|
191
195
|
}
|
|
192
196
|
|
|
197
|
+
declare namespace Instrument {
|
|
198
|
+
interface Info {
|
|
199
|
+
id: number;
|
|
200
|
+
symbol: string;
|
|
201
|
+
description: string;
|
|
202
|
+
type: string;
|
|
203
|
+
subtype: string;
|
|
204
|
+
currency: string;
|
|
205
|
+
currencyPrecision: number;
|
|
206
|
+
precision: number;
|
|
207
|
+
pipsSize: number;
|
|
208
|
+
quantityIncrement: number;
|
|
209
|
+
quantityPrecision: number;
|
|
210
|
+
priceIncrement: number;
|
|
211
|
+
version: number;
|
|
212
|
+
priceIncrementsTO: {
|
|
213
|
+
priceIncrements: number[];
|
|
214
|
+
pricePrecisions: number[];
|
|
215
|
+
bondFraction: boolean;
|
|
216
|
+
};
|
|
217
|
+
lotSize: number;
|
|
218
|
+
baseCurrency: string | null;
|
|
219
|
+
lotName: string | null;
|
|
220
|
+
multiplier: number;
|
|
221
|
+
open: boolean;
|
|
222
|
+
expiration: string | null;
|
|
223
|
+
firstNoticeDate: string | null;
|
|
224
|
+
lastTradeDate: string | null;
|
|
225
|
+
underlying: string | null;
|
|
226
|
+
mmy: string | null;
|
|
227
|
+
optionParametersTO: unknown;
|
|
228
|
+
unitName: string | null;
|
|
229
|
+
additionalFields: unknown;
|
|
230
|
+
additionalObject: unknown;
|
|
231
|
+
currencyParametersTO: unknown;
|
|
232
|
+
tradingHours: string;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
declare namespace Symbol {
|
|
237
|
+
interface Suggestion {
|
|
238
|
+
id: number;
|
|
239
|
+
name: string;
|
|
240
|
+
[key: string]: unknown;
|
|
241
|
+
}
|
|
242
|
+
interface Info {
|
|
243
|
+
maxVolume: number;
|
|
244
|
+
minVolume: number;
|
|
245
|
+
volumeStep: number;
|
|
246
|
+
lotSize: number;
|
|
247
|
+
[key: string]: unknown;
|
|
248
|
+
}
|
|
249
|
+
interface Limits {
|
|
250
|
+
symbol: string;
|
|
251
|
+
instrumentId: number;
|
|
252
|
+
limitStopDistanceType: string;
|
|
253
|
+
limitStopDistance: number;
|
|
254
|
+
limitStopDistanceInPercentOfSpread: number;
|
|
255
|
+
minOrderSize: number;
|
|
256
|
+
minOrderSizeBypass: boolean;
|
|
257
|
+
maxOrderSize: number;
|
|
258
|
+
minOrderIncrement: number;
|
|
259
|
+
limitType: string;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
193
263
|
declare class DxtradeClient {
|
|
194
264
|
private _ctx;
|
|
195
265
|
constructor(config: DxtradeConfig);
|
|
@@ -199,7 +269,10 @@ declare class DxtradeClient {
|
|
|
199
269
|
connect(): Promise<void>;
|
|
200
270
|
getSymbolSuggestions(text: string): Promise<Symbol.Suggestion[]>;
|
|
201
271
|
getSymbolInfo(symbol: string): Promise<Symbol.Info>;
|
|
272
|
+
getSymbolLimits(): Promise<Symbol.Limits[]>;
|
|
202
273
|
submitOrder(params: Order.SubmitParams): Promise<Order.Update>;
|
|
274
|
+
getAccountMetrics(): Promise<Account.Metrics>;
|
|
275
|
+
getInstruments(params?: Partial<Instrument.Info>): Promise<Instrument.Info[]>;
|
|
203
276
|
getAssessments(params: Assessments.Params): Promise<Assessments.Response>;
|
|
204
277
|
}
|
|
205
278
|
|
package/dist/index.js
CHANGED
|
@@ -93,8 +93,11 @@ var TIF = /* @__PURE__ */ ((TIF2) => {
|
|
|
93
93
|
return TIF2;
|
|
94
94
|
})(TIF || {});
|
|
95
95
|
var WS_MESSAGE = /* @__PURE__ */ ((WS_MESSAGE2) => {
|
|
96
|
+
WS_MESSAGE2["ACCOUNT_METRICS"] = "ACCOUNT_METRICS";
|
|
96
97
|
WS_MESSAGE2["ACCOUNTS"] = "ACCOUNTS";
|
|
97
98
|
WS_MESSAGE2["AVAILABLE_WATCHLISTS"] = "AVAILABLE_WATCHLISTS";
|
|
99
|
+
WS_MESSAGE2["INSTRUMENTS"] = "INSTRUMENTS";
|
|
100
|
+
WS_MESSAGE2["LIMITS"] = "LIMITS";
|
|
98
101
|
WS_MESSAGE2["MESSAGE"] = "MESSAGE";
|
|
99
102
|
WS_MESSAGE2["ORDERS"] = "ORDERS";
|
|
100
103
|
WS_MESSAGE2["POSITIONS"] = "POSITIONS";
|
|
@@ -115,7 +118,7 @@ var DxtradeError = class extends Error {
|
|
|
115
118
|
}
|
|
116
119
|
};
|
|
117
120
|
|
|
118
|
-
// src/domains/
|
|
121
|
+
// src/domains/account/account.ts
|
|
119
122
|
var import_ws = __toESM(require("ws"));
|
|
120
123
|
|
|
121
124
|
// src/utils/cookies.ts
|
|
@@ -203,115 +206,113 @@ function parseWsData(data) {
|
|
|
203
206
|
}
|
|
204
207
|
}
|
|
205
208
|
|
|
206
|
-
// src/domains/
|
|
207
|
-
function
|
|
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);
|
|
208
214
|
return new Promise((resolve, reject) => {
|
|
209
215
|
const ws = new import_ws.default(wsUrl, { headers: { Cookie: cookieStr } });
|
|
210
216
|
const timer = setTimeout(() => {
|
|
211
217
|
ws.close();
|
|
212
|
-
reject(new
|
|
218
|
+
reject(new DxtradeError("ACCOUNT_METRICS_TIMEOUT", "Account metrics timed out"));
|
|
213
219
|
}, timeout);
|
|
214
220
|
ws.on("message", (data) => {
|
|
215
221
|
const msg = parseWsData(data);
|
|
216
|
-
if (shouldLog(msg, debug)) debugLog(msg);
|
|
222
|
+
if (shouldLog(msg, ctx.debug)) debugLog(msg);
|
|
217
223
|
if (typeof msg === "string") return;
|
|
218
|
-
if (msg.type === "
|
|
224
|
+
if (msg.type === "ACCOUNT_METRICS" /* ACCOUNT_METRICS */) {
|
|
219
225
|
clearTimeout(timer);
|
|
220
226
|
ws.close();
|
|
221
|
-
|
|
227
|
+
const body = msg.body;
|
|
228
|
+
resolve(body.allMetrics);
|
|
222
229
|
}
|
|
223
230
|
});
|
|
224
231
|
ws.on("error", (error) => {
|
|
225
232
|
clearTimeout(timer);
|
|
226
233
|
ws.close();
|
|
227
|
-
reject(new
|
|
234
|
+
reject(new DxtradeError("ACCOUNT_METRICS_ERROR", `Account metrics error: ${error.message}`));
|
|
228
235
|
});
|
|
229
236
|
});
|
|
230
237
|
}
|
|
231
|
-
|
|
238
|
+
|
|
239
|
+
// src/domains/assessments/assessments.ts
|
|
240
|
+
async function getAssessments(ctx, params) {
|
|
241
|
+
ctx.ensureSession();
|
|
232
242
|
try {
|
|
233
243
|
const response = await retryRequest(
|
|
234
244
|
{
|
|
235
245
|
method: "POST",
|
|
236
|
-
url: endpoints.
|
|
246
|
+
url: endpoints.assessments(ctx.baseUrl),
|
|
237
247
|
data: {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
248
|
+
from: params.from,
|
|
249
|
+
instrument: params.instrument,
|
|
250
|
+
subtype: params.subtype ?? null,
|
|
251
|
+
to: params.to
|
|
241
252
|
},
|
|
242
|
-
headers: { "Content-Type": "application/json" }
|
|
243
|
-
},
|
|
244
|
-
ctx.retries
|
|
245
|
-
);
|
|
246
|
-
if (response.status === 200) {
|
|
247
|
-
const setCookies = response.headers["set-cookie"] ?? [];
|
|
248
|
-
const incoming = Cookies.parse(setCookies);
|
|
249
|
-
ctx.cookies = Cookies.merge(ctx.cookies, incoming);
|
|
250
|
-
ctx.callbacks.onLogin?.();
|
|
251
|
-
} else {
|
|
252
|
-
ctx.throwError("LOGIN_FAILED", `Login failed: ${response.status}`);
|
|
253
|
-
}
|
|
254
|
-
} catch (error) {
|
|
255
|
-
if (error instanceof DxtradeError) throw error;
|
|
256
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
257
|
-
ctx.throwError("LOGIN_ERROR", `Login error: ${message}`);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
async function fetchCsrf(ctx) {
|
|
261
|
-
try {
|
|
262
|
-
const cookieStr = Cookies.serialize(ctx.cookies);
|
|
263
|
-
const response = await retryRequest(
|
|
264
|
-
{
|
|
265
|
-
method: "GET",
|
|
266
|
-
url: ctx.baseUrl,
|
|
267
|
-
headers: { ...cookieOnlyHeaders(cookieStr), Referer: ctx.baseUrl }
|
|
268
|
-
},
|
|
269
|
-
ctx.retries
|
|
270
|
-
);
|
|
271
|
-
const csrfMatch = response.data?.match(/name="csrf" content="([^"]+)"/);
|
|
272
|
-
if (csrfMatch) {
|
|
273
|
-
ctx.csrf = csrfMatch[1];
|
|
274
|
-
} else {
|
|
275
|
-
ctx.throwError("CSRF_NOT_FOUND", "CSRF token not found");
|
|
276
|
-
}
|
|
277
|
-
} catch (error) {
|
|
278
|
-
if (error instanceof DxtradeError) throw error;
|
|
279
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
280
|
-
ctx.throwError("CSRF_ERROR", `CSRF fetch error: ${message}`);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
async function switchAccount(ctx, accountId) {
|
|
284
|
-
ctx.ensureSession();
|
|
285
|
-
try {
|
|
286
|
-
await retryRequest(
|
|
287
|
-
{
|
|
288
|
-
method: "POST",
|
|
289
|
-
url: endpoints.switchAccount(ctx.baseUrl, accountId),
|
|
290
253
|
headers: authHeaders(ctx.csrf, Cookies.serialize(ctx.cookies))
|
|
291
254
|
},
|
|
292
255
|
ctx.retries
|
|
293
256
|
);
|
|
294
|
-
|
|
257
|
+
return response.data;
|
|
295
258
|
} catch (error) {
|
|
296
259
|
if (error instanceof DxtradeError) throw error;
|
|
297
260
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
298
|
-
ctx.throwError("
|
|
261
|
+
ctx.throwError("ASSESSMENTS_ERROR", `Error fetching assessments: ${message}`);
|
|
299
262
|
}
|
|
300
263
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
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();
|
|
305
269
|
const wsUrl = endpoints.websocket(ctx.baseUrl);
|
|
306
270
|
const cookieStr = Cookies.serialize(ctx.cookies);
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
271
|
+
return new Promise((resolve, reject) => {
|
|
272
|
+
const ws = new import_ws2.default(wsUrl, { headers: { Cookie: cookieStr } });
|
|
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
|
+
});
|
|
312
308
|
}
|
|
313
309
|
|
|
310
|
+
// src/domains/order/order.ts
|
|
311
|
+
var import_crypto = __toESM(require("crypto"));
|
|
312
|
+
var import_ws4 = __toESM(require("ws"));
|
|
313
|
+
|
|
314
314
|
// src/domains/symbol/symbol.ts
|
|
315
|
+
var import_ws3 = __toESM(require("ws"));
|
|
315
316
|
async function getSymbolSuggestions(ctx, text) {
|
|
316
317
|
ctx.ensureSession();
|
|
317
318
|
try {
|
|
@@ -358,12 +359,45 @@ async function getSymbolInfo(ctx, symbol) {
|
|
|
358
359
|
ctx.throwError("SYMBOL_INFO_ERROR", `Error getting symbol info: ${message}`);
|
|
359
360
|
}
|
|
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
|
+
}
|
|
361
397
|
|
|
362
398
|
// src/domains/order/order.ts
|
|
363
|
-
var import_crypto = __toESM(require("crypto"));
|
|
364
|
-
var import_ws2 = __toESM(require("ws"));
|
|
365
399
|
function createOrderListener(wsUrl, cookieStr, timeout = 3e4, debug = false) {
|
|
366
|
-
const ws = new
|
|
400
|
+
const ws = new import_ws4.default(wsUrl, { headers: { Cookie: cookieStr } });
|
|
367
401
|
let settled = false;
|
|
368
402
|
const ready = new Promise((resolve) => {
|
|
369
403
|
ws.on("open", resolve);
|
|
@@ -521,29 +555,112 @@ async function submitOrder(ctx, params) {
|
|
|
521
555
|
}
|
|
522
556
|
}
|
|
523
557
|
|
|
524
|
-
// src/domains/
|
|
525
|
-
|
|
526
|
-
|
|
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) {
|
|
527
585
|
try {
|
|
528
586
|
const response = await retryRequest(
|
|
529
587
|
{
|
|
530
588
|
method: "POST",
|
|
531
|
-
url: endpoints.
|
|
589
|
+
url: endpoints.login(ctx.baseUrl),
|
|
532
590
|
data: {
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
to: params.to
|
|
591
|
+
username: ctx.config.username,
|
|
592
|
+
password: ctx.config.password,
|
|
593
|
+
domain: ctx.config.broker
|
|
537
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),
|
|
538
643
|
headers: authHeaders(ctx.csrf, Cookies.serialize(ctx.cookies))
|
|
539
644
|
},
|
|
540
645
|
ctx.retries
|
|
541
646
|
);
|
|
542
|
-
|
|
647
|
+
ctx.callbacks.onAccountSwitch?.(accountId);
|
|
543
648
|
} catch (error) {
|
|
544
649
|
if (error instanceof DxtradeError) throw error;
|
|
545
650
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
546
|
-
ctx.throwError("
|
|
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);
|
|
547
664
|
}
|
|
548
665
|
}
|
|
549
666
|
|
|
@@ -590,9 +707,18 @@ var DxtradeClient = class {
|
|
|
590
707
|
async getSymbolInfo(symbol) {
|
|
591
708
|
return getSymbolInfo(this._ctx, symbol);
|
|
592
709
|
}
|
|
710
|
+
async getSymbolLimits() {
|
|
711
|
+
return getSymbolLimits(this._ctx);
|
|
712
|
+
}
|
|
593
713
|
async submitOrder(params) {
|
|
594
714
|
return submitOrder(this._ctx, params);
|
|
595
715
|
}
|
|
716
|
+
async getAccountMetrics() {
|
|
717
|
+
return getAccountMetrics(this._ctx);
|
|
718
|
+
}
|
|
719
|
+
async getInstruments(params = {}) {
|
|
720
|
+
return getInstruments(this._ctx, params);
|
|
721
|
+
}
|
|
596
722
|
async getAssessments(params) {
|
|
597
723
|
return getAssessments(this._ctx, params);
|
|
598
724
|
}
|