@mautriz/mt5 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/bench.ts ADDED
@@ -0,0 +1,67 @@
1
+ import { MT5Client } from "./index";
2
+
3
+ const URL = process.argv[2] || "ws://localhost:8080";
4
+ const SYMBOL = process.argv[3] || "BTCUSD";
5
+ const NUM_CLIENTS = parseInt(process.argv[4] || "1");
6
+ const INTERVAL = 1_000; // ms
7
+
8
+ let count = 0;
9
+ let errors = 0;
10
+ let totalMs = 0;
11
+ let minMs = Infinity;
12
+ let maxMs = 0;
13
+
14
+ async function run() {
15
+ const clients = await Promise.all(
16
+ Array.from({ length: NUM_CLIENTS }, async () => {
17
+ const c = new MT5Client(URL, { timeout: 10_000 });
18
+ await c.connect();
19
+ return c;
20
+ }),
21
+ );
22
+
23
+ console.log(
24
+ `${NUM_CLIENTS} clients connected. Polling ${SYMBOL} every ${INTERVAL}ms — Ctrl+C to stop\n`,
25
+ );
26
+
27
+ const interval = setInterval(async () => {
28
+ const start = performance.now();
29
+ try {
30
+ // All clients request simultaneously
31
+ const ticks = await Promise.all(
32
+ clients.map((c) => c.getSymbolTick(SYMBOL)),
33
+ );
34
+ const elapsed = performance.now() - start;
35
+ count += NUM_CLIENTS;
36
+ totalMs += elapsed;
37
+ if (elapsed < minMs) minMs = elapsed;
38
+ if (elapsed > maxMs) maxMs = elapsed;
39
+ const tick = ticks[0];
40
+ process.stdout.write(
41
+ `\r[${count}] bid=${tick.bid} ask=${tick.ask} ${elapsed.toFixed(1)}ms (avg=${(totalMs / Math.ceil(count / NUM_CLIENTS)).toFixed(1)} min=${minMs.toFixed(1)} max=${maxMs.toFixed(1)})`,
42
+ );
43
+ } catch (e: any) {
44
+ errors++;
45
+ process.stdout.write(`\r[${count}] ERROR: ${e.message}`);
46
+ }
47
+ }, INTERVAL);
48
+
49
+ process.on("SIGINT", () => {
50
+ clearInterval(interval);
51
+ const batches = Math.ceil(count / NUM_CLIENTS);
52
+ console.log(`\n\n── Results ──`);
53
+ console.log(` Clients: ${NUM_CLIENTS}`);
54
+ console.log(` Requests: ${count} (${batches} batches of ${NUM_CLIENTS})`);
55
+ console.log(` Errors: ${errors}`);
56
+ console.log(` Avg batch: ${(totalMs / batches).toFixed(1)}ms`);
57
+ console.log(` Min batch: ${minMs.toFixed(1)}ms`);
58
+ console.log(` Max batch: ${maxMs.toFixed(1)}ms`);
59
+ for (const c of clients) c.disconnect();
60
+ process.exit(0);
61
+ });
62
+ }
63
+
64
+ run().catch((err) => {
65
+ console.error("Fatal:", err.message);
66
+ process.exit(1);
67
+ });
@@ -0,0 +1,164 @@
1
+ export interface AccountInfo {
2
+ balance: number;
3
+ equity: number;
4
+ margin: number;
5
+ free_margin: number;
6
+ profit: number;
7
+ leverage: number;
8
+ currency: string;
9
+ name: string;
10
+ number: number;
11
+ server: string;
12
+ company: string;
13
+ }
14
+ export interface Position {
15
+ ticket: number;
16
+ symbol: string;
17
+ type: "buy" | "sell";
18
+ volume: number;
19
+ open_price: number;
20
+ sl: number;
21
+ tp: number;
22
+ profit: number;
23
+ open_time: string;
24
+ }
25
+ export type PendingOrderType = "buy_limit" | "sell_limit" | "buy_stop" | "sell_stop" | "unknown";
26
+ export interface PendingOrder {
27
+ ticket: number;
28
+ symbol: string;
29
+ type: PendingOrderType;
30
+ volume: number;
31
+ price: number;
32
+ sl: number;
33
+ tp: number;
34
+ }
35
+ export type DealType = "buy" | "sell" | "other";
36
+ export type DealEntry = "in" | "out" | "inout" | "unknown";
37
+ export interface Deal {
38
+ ticket: number;
39
+ symbol: string;
40
+ type: DealType;
41
+ entry: DealEntry;
42
+ volume: number;
43
+ price: number;
44
+ profit: number;
45
+ commission: number;
46
+ swap: number;
47
+ time: string;
48
+ }
49
+ export interface SymbolTick {
50
+ symbol: string;
51
+ bid: number;
52
+ ask: number;
53
+ last: number;
54
+ volume: number;
55
+ time: string;
56
+ }
57
+ export type TradeMode = "disabled" | "longonly" | "shortonly" | "closeonly" | "full" | "unknown";
58
+ export interface TimeOffset {
59
+ offset_seconds: number;
60
+ server_time: string;
61
+ gmt_time: string;
62
+ }
63
+ export interface SymbolDetails {
64
+ symbol: string;
65
+ description: string;
66
+ digits: number;
67
+ point: number;
68
+ spread: number;
69
+ trade_mode: TradeMode;
70
+ volume_min: number;
71
+ volume_max: number;
72
+ volume_step: number;
73
+ trade_contract_size: number;
74
+ filling_mode: number;
75
+ bid: number;
76
+ ask: number;
77
+ }
78
+ export type OrderType = "buy" | "sell" | "buy_limit" | "sell_limit" | "buy_stop" | "sell_stop";
79
+ export interface OpenOrderParams {
80
+ type: OrderType;
81
+ symbol: string;
82
+ volume: number;
83
+ price?: number;
84
+ sl?: number;
85
+ tp?: number;
86
+ }
87
+ export interface CloseOrderParams {
88
+ ticket: number;
89
+ volume?: number;
90
+ }
91
+ export interface ModifyOrderParams {
92
+ ticket: number;
93
+ sl?: number;
94
+ tp?: number;
95
+ price?: number;
96
+ }
97
+ export declare class MT5Error extends Error {
98
+ readonly retcode: number | undefined;
99
+ constructor(message: string, retcode?: number);
100
+ }
101
+ export interface HelloMessage {
102
+ type: "hello";
103
+ ea: string;
104
+ symbol: string;
105
+ [key: string]: unknown;
106
+ }
107
+ export interface TickMessage {
108
+ type: "tick";
109
+ symbol: string;
110
+ bid: number;
111
+ ask: number;
112
+ [key: string]: unknown;
113
+ }
114
+ export interface PushMessage {
115
+ type: string;
116
+ [key: string]: unknown;
117
+ }
118
+ type PushEventMap = {
119
+ hello: HelloMessage;
120
+ tick: TickMessage;
121
+ message: PushMessage;
122
+ connected: undefined;
123
+ disconnected: {
124
+ code: number;
125
+ reason: string;
126
+ };
127
+ error: Event;
128
+ };
129
+ type EventCallback<T> = T extends undefined ? () => void : (data: T) => void;
130
+ export declare class MT5Client {
131
+ private readonly url;
132
+ private ws;
133
+ private reqCounter;
134
+ private pending;
135
+ private listeners;
136
+ private timeoutMs;
137
+ constructor(url: string, options?: {
138
+ timeout?: number;
139
+ });
140
+ connect(): Promise<void>;
141
+ disconnect(): void;
142
+ get connected(): boolean;
143
+ on<K extends keyof PushEventMap>(event: K, callback: EventCallback<PushEventMap[K]>): this;
144
+ off<K extends keyof PushEventMap>(event: K, callback: EventCallback<PushEventMap[K]>): this;
145
+ private emit;
146
+ getAccount(): Promise<AccountInfo>;
147
+ openOrder(params: OpenOrderParams): Promise<{
148
+ ticket: number;
149
+ }>;
150
+ closeOrder(params: CloseOrderParams): Promise<{
151
+ ticket: number;
152
+ }>;
153
+ modifyOrder(params: ModifyOrderParams): Promise<void>;
154
+ getPositions(): Promise<Position[]>;
155
+ getOrders(): Promise<PendingOrder[]>;
156
+ getDeals(days?: number): Promise<Deal[]>;
157
+ getSymbolTick(symbol: string): Promise<SymbolTick>;
158
+ getSymbolDetails(symbol: string): Promise<SymbolDetails>;
159
+ getTimeOffset(): Promise<TimeOffset>;
160
+ private nextId;
161
+ private request;
162
+ private setupHandlers;
163
+ }
164
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,257 @@
1
+ // ── Response types ──────────────────────────────────────────────────
2
+ // ── Error type ──────────────────────────────────────────────────────
3
+ export class MT5Error extends Error {
4
+ constructor(message, retcode) {
5
+ super(message);
6
+ this.name = "MT5Error";
7
+ this.retcode = retcode;
8
+ }
9
+ }
10
+ // ── Client ──────────────────────────────────────────────────────────
11
+ export class MT5Client {
12
+ constructor(url, options) {
13
+ this.ws = null;
14
+ this.reqCounter = 0;
15
+ this.pending = new Map();
16
+ this.listeners = new Map();
17
+ this.url = url;
18
+ this.timeoutMs = options?.timeout ?? 30000;
19
+ }
20
+ // ── Connection ──────────────────────────────────────────────────
21
+ connect() {
22
+ return new Promise((resolve, reject) => {
23
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
24
+ resolve();
25
+ return;
26
+ }
27
+ const ws = new WebSocket(this.url);
28
+ this.ws = ws;
29
+ const onOpen = () => {
30
+ cleanup();
31
+ this.setupHandlers(ws);
32
+ this.emit("connected", undefined);
33
+ resolve();
34
+ };
35
+ const onError = (ev) => {
36
+ cleanup();
37
+ reject(new MT5Error("WebSocket connection failed"));
38
+ };
39
+ const onClose = () => {
40
+ cleanup();
41
+ reject(new MT5Error("WebSocket closed before connection was established"));
42
+ };
43
+ const cleanup = () => {
44
+ ws.removeEventListener("open", onOpen);
45
+ ws.removeEventListener("error", onError);
46
+ ws.removeEventListener("close", onClose);
47
+ };
48
+ ws.addEventListener("open", onOpen);
49
+ ws.addEventListener("error", onError);
50
+ ws.addEventListener("close", onClose);
51
+ });
52
+ }
53
+ disconnect() {
54
+ if (this.ws) {
55
+ this.ws.close();
56
+ this.ws = null;
57
+ }
58
+ for (const [, req] of this.pending) {
59
+ clearTimeout(req.timer);
60
+ req.reject(new MT5Error("Disconnected"));
61
+ }
62
+ this.pending.clear();
63
+ }
64
+ get connected() {
65
+ return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
66
+ }
67
+ // ── Event emitter ───────────────────────────────────────────────
68
+ on(event, callback) {
69
+ let set = this.listeners.get(event);
70
+ if (!set) {
71
+ set = new Set();
72
+ this.listeners.set(event, set);
73
+ }
74
+ set.add(callback);
75
+ return this;
76
+ }
77
+ off(event, callback) {
78
+ const set = this.listeners.get(event);
79
+ if (set) {
80
+ set.delete(callback);
81
+ if (set.size === 0)
82
+ this.listeners.delete(event);
83
+ }
84
+ return this;
85
+ }
86
+ emit(event, data) {
87
+ const set = this.listeners.get(event);
88
+ if (set) {
89
+ for (const cb of set) {
90
+ cb(data);
91
+ }
92
+ }
93
+ }
94
+ // ── API methods ─────────────────────────────────────────────────
95
+ async getAccount() {
96
+ const res = await this.request({ action: "get_account" });
97
+ return {
98
+ balance: res.balance,
99
+ equity: res.equity,
100
+ margin: res.margin,
101
+ free_margin: res.free_margin,
102
+ profit: res.profit,
103
+ leverage: res.leverage,
104
+ currency: res.currency,
105
+ name: res.name,
106
+ number: res.number,
107
+ server: res.server,
108
+ company: res.company,
109
+ };
110
+ }
111
+ async openOrder(params) {
112
+ const res = await this.request({
113
+ action: "open_order",
114
+ type: params.type,
115
+ symbol: params.symbol,
116
+ volume: params.volume,
117
+ ...(params.price !== undefined && { price: params.price }),
118
+ ...(params.sl !== undefined && { sl: params.sl }),
119
+ ...(params.tp !== undefined && { tp: params.tp }),
120
+ });
121
+ return { ticket: res.ticket };
122
+ }
123
+ async closeOrder(params) {
124
+ const res = await this.request({
125
+ action: "close_order",
126
+ ticket: params.ticket,
127
+ ...(params.volume !== undefined && { volume: params.volume }),
128
+ });
129
+ return { ticket: res.ticket };
130
+ }
131
+ async modifyOrder(params) {
132
+ await this.request({
133
+ action: "modify_order",
134
+ ticket: params.ticket,
135
+ ...(params.sl !== undefined && { sl: params.sl }),
136
+ ...(params.tp !== undefined && { tp: params.tp }),
137
+ ...(params.price !== undefined && { price: params.price }),
138
+ });
139
+ }
140
+ async getPositions() {
141
+ const res = await this.request({ action: "get_positions" });
142
+ return res.positions;
143
+ }
144
+ async getOrders() {
145
+ const res = await this.request({ action: "get_orders" });
146
+ return res.orders;
147
+ }
148
+ async getDeals(days) {
149
+ const res = await this.request({
150
+ action: "get_deals",
151
+ ...(days !== undefined && { days }),
152
+ });
153
+ return res.deals;
154
+ }
155
+ async getSymbolTick(symbol) {
156
+ const res = await this.request({ action: "get_symbol_tick", symbol });
157
+ return {
158
+ symbol: res.symbol,
159
+ bid: res.bid,
160
+ ask: res.ask,
161
+ last: res.last,
162
+ volume: res.volume,
163
+ time: res.time,
164
+ };
165
+ }
166
+ async getSymbolDetails(symbol) {
167
+ const res = await this.request({ action: "get_symbol_details", symbol });
168
+ return {
169
+ symbol: res.symbol,
170
+ description: res.description,
171
+ digits: res.digits,
172
+ point: res.point,
173
+ spread: res.spread,
174
+ trade_mode: res.trade_mode,
175
+ volume_min: res.volume_min,
176
+ volume_max: res.volume_max,
177
+ volume_step: res.volume_step,
178
+ trade_contract_size: res.trade_contract_size,
179
+ filling_mode: res.filling_mode,
180
+ bid: res.bid,
181
+ ask: res.ask,
182
+ };
183
+ }
184
+ async getTimeOffset() {
185
+ const res = await this.request({ action: "get_time_offset" });
186
+ return {
187
+ offset_seconds: res.offset_seconds,
188
+ server_time: res.server_time,
189
+ gmt_time: res.gmt_time,
190
+ };
191
+ }
192
+ // ── Internals ───────────────────────────────────────────────────
193
+ nextId() {
194
+ return `req-${++this.reqCounter}`;
195
+ }
196
+ request(payload) {
197
+ return new Promise((resolve, reject) => {
198
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
199
+ reject(new MT5Error("Not connected"));
200
+ return;
201
+ }
202
+ const id = this.nextId();
203
+ const timer = setTimeout(() => {
204
+ this.pending.delete(id);
205
+ reject(new MT5Error(`Request ${id} timed out after ${this.timeoutMs}ms`));
206
+ }, this.timeoutMs);
207
+ this.pending.set(id, { resolve, reject, timer });
208
+ this.ws.send(JSON.stringify({ ...payload, id }));
209
+ });
210
+ }
211
+ setupHandlers(ws) {
212
+ ws.addEventListener("message", (ev) => {
213
+ let msg;
214
+ try {
215
+ msg = JSON.parse(typeof ev.data === "string" ? ev.data : String(ev.data));
216
+ }
217
+ catch {
218
+ return;
219
+ }
220
+ // Response to a request (has an "id" field matching a pending request)
221
+ if (msg.id && this.pending.has(msg.id)) {
222
+ const req = this.pending.get(msg.id);
223
+ this.pending.delete(msg.id);
224
+ clearTimeout(req.timer);
225
+ if (msg.status === "ok") {
226
+ req.resolve(msg);
227
+ }
228
+ else {
229
+ req.reject(new MT5Error(msg.error || "Unknown error", msg.retcode));
230
+ }
231
+ return;
232
+ }
233
+ // Push message from EA (no matching request id)
234
+ if (msg.type) {
235
+ this.emit(msg.type, msg);
236
+ // Also emit on the generic "message" event
237
+ this.emit("message", msg);
238
+ }
239
+ });
240
+ ws.addEventListener("close", (ev) => {
241
+ this.emit("disconnected", {
242
+ code: ev.code,
243
+ reason: ev.reason,
244
+ });
245
+ for (const [, req] of this.pending) {
246
+ clearTimeout(req.timer);
247
+ req.reject(new MT5Error("Connection closed"));
248
+ }
249
+ this.pending.clear();
250
+ this.ws = null;
251
+ });
252
+ ws.addEventListener("error", (ev) => {
253
+ this.emit("error", ev);
254
+ });
255
+ }
256
+ }
257
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,155 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from "vitest";
2
+ import { MT5Client } from "./index";
3
+ const SYMBOL = process.env.SYMBOL || "BTCUSD";
4
+ const URL = process.env.WS_URL || "ws://localhost:8080";
5
+ const mt5 = new MT5Client(URL, { timeout: 300000 });
6
+ let buyTicket;
7
+ let sellTicket;
8
+ beforeAll(async () => {
9
+ await mt5.connect();
10
+ });
11
+ afterAll(() => {
12
+ mt5.disconnect();
13
+ });
14
+ describe("Account", () => {
15
+ it("should return account info", async () => {
16
+ const account = await mt5.getAccount();
17
+ expect(account.balance).toBeTypeOf("number");
18
+ expect(account.equity).toBeTypeOf("number");
19
+ expect(account.leverage).toBeGreaterThan(0);
20
+ console.log(` balance=${account.balance} equity=${account.equity} leverage=${account.leverage}`);
21
+ });
22
+ });
23
+ describe("Symbol", () => {
24
+ it("should return symbol details", async () => {
25
+ const details = await mt5.getSymbolDetails(SYMBOL);
26
+ expect(details.symbol).toBe(SYMBOL);
27
+ expect(details.trade_mode).toBe("full");
28
+ expect(details.volume_min).toBeGreaterThan(0);
29
+ console.log(` ${details.symbol}: digits=${details.digits} spread=${details.spread} filling_mode=${details.filling_mode}`);
30
+ console.log(` volume: min=${details.volume_min} max=${details.volume_max} step=${details.volume_step}`);
31
+ });
32
+ it("should return symbol tick with bid/ask", async () => {
33
+ let tick;
34
+ for (let attempt = 1; attempt <= 5; attempt++) {
35
+ tick = await mt5.getSymbolTick(SYMBOL);
36
+ if (tick.bid > 0)
37
+ break;
38
+ console.log(` attempt ${attempt}/5 — no quotes yet, waiting 1s...`);
39
+ await new Promise((r) => setTimeout(r, 1000));
40
+ }
41
+ expect(tick.bid).toBeGreaterThan(0);
42
+ expect(tick.ask).toBeGreaterThan(0);
43
+ console.log(` bid=${tick.bid} ask=${tick.ask} last=${tick.last}`);
44
+ });
45
+ });
46
+ describe("Orders", () => {
47
+ it("should open a buy order", async () => {
48
+ const result = await mt5.openOrder({
49
+ type: "buy",
50
+ symbol: SYMBOL,
51
+ volume: 0.1,
52
+ });
53
+ expect(result.ticket).toBeGreaterThan(0);
54
+ buyTicket = result.ticket;
55
+ console.log(` ticket=${buyTicket}`);
56
+ });
57
+ it("should show the buy in positions", async () => {
58
+ const positions = await mt5.getPositions();
59
+ const our = positions.find((p) => p.ticket === buyTicket);
60
+ expect(our).toBeDefined();
61
+ expect(our.type).toBe("buy");
62
+ expect(our.symbol).toBe(SYMBOL);
63
+ console.log(` found ${positions.length} position(s), ours: ticket=${our.ticket} ${our.type} ${our.volume} ${our.symbol}`);
64
+ });
65
+ it("should open a sell order", async () => {
66
+ const result = await mt5.openOrder({
67
+ type: "sell",
68
+ symbol: SYMBOL,
69
+ volume: 0.01,
70
+ });
71
+ expect(result.ticket).toBeGreaterThan(0);
72
+ sellTicket = result.ticket;
73
+ console.log(` ticket=${sellTicket}`);
74
+ });
75
+ it("should close the buy position", async () => {
76
+ const result = await mt5.closeOrder({ ticket: buyTicket });
77
+ expect(result.ticket).toBeGreaterThan(0);
78
+ });
79
+ it("should close the sell position", async () => {
80
+ const result = await mt5.closeOrder({ ticket: sellTicket });
81
+ expect(result.ticket).toBeGreaterThan(0);
82
+ });
83
+ it("should have no remaining positions for our tickets", async () => {
84
+ const positions = await mt5.getPositions();
85
+ const ours = positions.filter((p) => p.ticket === buyTicket || p.ticket === sellTicket);
86
+ expect(ours).toHaveLength(0);
87
+ console.log(` ${positions.length} position(s) remaining (none are ours)`);
88
+ });
89
+ });
90
+ describe("Concurrent - Four Clients", () => {
91
+ it("should open and close orders simultaneously from 4 clients", async () => {
92
+ const clients = await Promise.all(Array.from({ length: 3 }, () => {
93
+ const c = new MT5Client(URL, { timeout: 300000 });
94
+ return c.connect().then(() => c);
95
+ }));
96
+ const allClients = [mt5, ...clients];
97
+ // Open 4 orders simultaneously from different clients
98
+ const opens = await Promise.all(allClients.map((c, i) => c.openOrder({
99
+ type: i % 2 === 0 ? "buy" : "sell",
100
+ symbol: SYMBOL,
101
+ volume: 0.01,
102
+ })));
103
+ for (const o of opens) {
104
+ expect(o.ticket).toBeGreaterThan(0);
105
+ }
106
+ console.log(` tickets: ${opens.map((o) => o.ticket).join(", ")}`);
107
+ // Close all 4 simultaneously
108
+ const closes = await Promise.all(allClients.map((c, i) => c.closeOrder({ ticket: opens[i].ticket })));
109
+ for (const c of closes) {
110
+ expect(c.ticket).toBeGreaterThan(0);
111
+ }
112
+ console.log(` all 4 closed simultaneously`);
113
+ for (const c of clients)
114
+ c.disconnect();
115
+ });
116
+ });
117
+ describe("Concurrent - Same Client", () => {
118
+ it("should open and close 4 positions simultaneously from one client", async () => {
119
+ // Open 4 orders simultaneously
120
+ const opens = await Promise.all(Array.from({ length: 4 }, (_, i) => mt5.openOrder({
121
+ type: i % 2 === 0 ? "buy" : "sell",
122
+ symbol: SYMBOL,
123
+ volume: 0.01,
124
+ })));
125
+ for (const o of opens) {
126
+ expect(o.ticket).toBeGreaterThan(0);
127
+ }
128
+ console.log(` opened: ${opens.map((o) => o.ticket).join(", ")}`);
129
+ // Close all 4 simultaneously
130
+ const closes = await Promise.all(opens.map((o) => mt5.closeOrder({ ticket: o.ticket })));
131
+ for (const c of closes) {
132
+ expect(c.ticket).toBeGreaterThan(0);
133
+ }
134
+ console.log(` all 4 closed simultaneously`);
135
+ // Verify all gone
136
+ const positions = await mt5.getPositions();
137
+ const ours = positions.filter((p) => opens.some((o) => o.ticket === p.ticket));
138
+ expect(ours).toHaveLength(0);
139
+ console.log(` verified: ${positions.length} position(s) remaining`);
140
+ });
141
+ });
142
+ describe("Deals", () => {
143
+ it("should contain our trades in history", async () => {
144
+ const deals = await mt5.getDeals(1);
145
+ expect(deals.length).toBeGreaterThan(0);
146
+ console.log(` ${deals.length} deal(s) in last 24h`);
147
+ const ourDeals = deals.filter((d) => d.symbol === SYMBOL);
148
+ const entries = ourDeals.filter((d) => d.entry === "in");
149
+ const exits = ourDeals.filter((d) => d.entry === "out");
150
+ console.log(` ${SYMBOL}: ${entries.length} entry deal(s), ${exits.length} exit deal(s)`);
151
+ expect(entries.length).toBeGreaterThanOrEqual(2);
152
+ expect(exits.length).toBeGreaterThanOrEqual(2);
153
+ });
154
+ });
155
+ //# sourceMappingURL=mt5.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mt5.test.js","sourceRoot":"","sources":["../mt5.test.ts"],"names":[],"mappings":"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"}