hedgequantx 1.3.0 → 1.3.1

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 CHANGED
@@ -11,7 +11,7 @@
11
11
 
12
12
  **Prop Futures Algo Trading CLI**
13
13
 
14
- A powerful command-line interface for connecting to prop trading firms and managing your futures trading accounts.
14
+ A powerful command-line interface for connecting to prop trading firms and managing your futures trading accounts with automated algo trading capabilities.
15
15
 
16
16
  [![npm version](https://img.shields.io/npm/v/hedgequantx.svg)](https://www.npmjs.com/package/hedgequantx)
17
17
  [![npm downloads](https://img.shields.io/npm/dm/hedgequantx.svg)](https://www.npmjs.com/package/hedgequantx)
@@ -20,12 +20,14 @@ A powerful command-line interface for connecting to prop trading firms and manag
20
20
 
21
21
  ## Features
22
22
 
23
- - Multi-platform support (ProjectX, Tradovate, Rithmic)
24
- - 37 supported prop firms
25
- - Multi-account connections
26
- - Real-time stats (balance, P&L, positions)
27
- - Secure encrypted session storage
28
- - Auto-update with restart
23
+ - **Multi-platform support** - ProjectX, Rithmic, Tradovate
24
+ - **37+ supported prop firms**
25
+ - **Multi-account connections** - Connect multiple accounts simultaneously
26
+ - **Real-time stats** - Balance, P&L, positions, orders
27
+ - **Algo Trading** - One Account & Copy Trading modes
28
+ - **HQX Server** - Cloud-based execution engine
29
+ - **Secure sessions** - AES-256-GCM encrypted storage
30
+ - **Auto-update** - Built-in update with restart
29
31
 
30
32
  ---
31
33
 
@@ -63,11 +65,33 @@ hedgequantx version
63
65
 
64
66
  ---
65
67
 
66
- ## Supported Prop Firms (37)
68
+ ## Algo Trading Modes
69
+
70
+ ### One Account Mode
71
+ Trade on a single account with automated signals and risk management.
72
+
73
+ - Symbol selection (ES, NQ, MNQ, etc.)
74
+ - Configurable contracts quantity
75
+ - Daily target and max risk limits
76
+ - Real-time P&L tracking
77
+ - Activity log with trade history
78
+
79
+ ### Copy Trading Mode
80
+ Mirror trades from a Lead account to Follower accounts.
81
+
82
+ - Lead -> Follower trade copying
83
+ - Different symbols per account
84
+ - Configurable contract ratios
85
+ - Privacy mode (hide account names)
86
+ - Low-latency execution via HQX Server
87
+
88
+ ---
89
+
90
+ ## Supported Prop Firms (37+)
67
91
 
68
92
  | ProjectX (19) | Rithmic (16) | Tradovate (2) |
69
93
  |---------------|--------------|---------------|
70
- | TopStep | Apex | Apex |
94
+ | TopStep | Apex Trader Funding | Apex |
71
95
  | Alpha Futures | TopstepTrader | TakeProfitTrader |
72
96
  | TickTickTrader | MES Capital | |
73
97
  | Bulenox | Bulenox | |
@@ -87,51 +111,46 @@ hedgequantx version
87
111
  | Lucid Trading | | |
88
112
  | Tradeify | | |
89
113
 
90
- > **Note:** ProjectX firms are fully supported. Rithmic and Tradovate coming soon.
91
-
92
114
  ---
93
115
 
94
116
  ## Dashboard Features
95
117
 
96
118
  - **View Accounts** - List all trading accounts with balance and status
97
- - **View Positions** - Open positions with P&L
98
- - **View Orders** - Pending and filled orders
99
119
  - **View Stats** - Trading metrics, equity curve, P&L calendar
100
- - **User Info** - Account details
101
120
  - **Add Prop-Account** - Connect multiple prop firms
102
- - **Algo-Trading** - Automated trading (coming soon)
103
- - **Update HQX** - Auto-update with restart
121
+ - **Algo-Trading** - One Account & Copy Trading modes
122
+ - **Update HQX** - Auto-update with confirmation and restart
104
123
 
105
124
  ---
106
125
 
107
126
  ## Screenshots
108
127
 
128
+ ### Main Dashboard
109
129
  ```
110
- ╔══════════════════════════════════════════════════════════════════════════════════════════════╗
111
- ║██╗ ██╗███████╗██████╗ ██████╗ ███████╗ ██████╗ ██╗ ██╗ █████╗ ███╗ ██╗████████╗██╗ ██╗║
112
- ║██║ ██║██╔════╝██╔══██╗██╔════╝ ██╔════╝██╔═══██╗██║ ██║██╔══██╗████╗ ██║╚══██╔══╝╚██╗██╔╝║
113
- ║███████║█████╗ ██║ ██║██║ ███╗█████╗ ██║ ██║██║ ██║███████║██╔██╗ ██║ ██║ ╚███╔╝
114
- ║██╔══██║██╔══╝ ██║ ██║██║ ██║██╔══╝ ██║▄▄ ██║██║ ██║██╔══██║██║╚██╗██║ ██║ ██╔██╗
115
- ║██║ ██║███████╗██████╔╝╚██████╔╝███████╗╚██████╔╝╚██████╔╝██║ ██║██║ ╚████║ ██║ ██╔╝ ██╗║
116
- ║╚═╝ ╚═╝╚══════╝╚═════╝ ╚═════╝ ╚══════╝ ╚══▀▀═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝║
117
- ╠══════════════════════════════════════════════════════════════════════════════════════════════╣
118
- Prop Futures Algo Trading v1.2.0 ║
119
- ╠══════════════════════════════════════════════════════════════════════════════════════════════╣
120
- ║ Connections: 1 Accounts: 2 Balance: $299,776 P&L: $-223 (-0.1%) ║
121
- ╚══════════════════════════════════════════════════════════════════════════════════════════════╝
122
-
123
- ? Dashboard:
124
- > View Accounts
125
- View Positions
126
- View Orders
127
- View Stats
128
- User Info
129
- Add Prop-Account
130
- ──────────────
131
- Algo-Trading
132
- ──────────────
133
- Update HQX
134
- Disconnect
130
+ ╔════════════════════════════════════════════════════════════════════════════════════════════════╗
131
+ ║ ██╗ ██╗███████╗██████╗ ██████╗ ███████╗ ██████╗ ██╗ ██╗ █████╗ ███╗ ██╗████████╗██╗ ██╗ ║
132
+ ║ ██║ ██║██╔════╝██╔══██╗██╔════╝ ██╔════╝██╔═══██╗██║ ██║██╔══██╗████╗ ██║╚══██╔══╝╚██╗██╔╝ ║
133
+ ║ ███████║█████╗ ██║ ██║██║ ███╗█████╗ ██║ ██║██║ ██║███████║██╔██╗ ██║ ██║ ╚███╔╝
134
+ ║ ██╔══██║██╔══╝ ██║ ██║██║ ██║██╔══╝ ██║▄▄ ██║██║ ██║██╔══██║██║╚██╗██║ ██║ ██╔██╗
135
+ ║ ██║ ██║███████╗██████╔╝╚██████╔╝███████╗╚██████╔╝╚██████╔╝██║ ██║██║ ╚████║ ██║ ██╔╝ ██╗ ║
136
+ ║ ╚═╝ ╚═╝╚══════╝╚═════╝ ╚═════╝ ╚══════╝ ╚══▀▀═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝ ║
137
+ ╠════════════════════════════════════════════════════════════════════════════════════════════════╣
138
+ Prop Futures Algo Trading v1.3.0 ║
139
+ ╠════════════════════════════════════════════════════════════════════════════════════════════════╣
140
+ ║ Connections: 2 Accounts: 3 Balance: $601,526 P&L: +$1,526 (+0.3%) ║
141
+ ╚════════════════════════════════════════════════════════════════════════════════════════════════╝
142
+
143
+ ╔════════════════════════════════════════════════════════════════════════════════════════════════╗
144
+ ║ Welcome, HQX Trader! ║
145
+ ╠════════════════════════════════════════════════════════════════════════════════════════════════╣
146
+ ┌────────────────────────────────────────────┐ ┌────────────────────────────────────────────┐ ║
147
+ │ TopStep │ │ Apex Trader Funding │ ║
148
+ └────────────────────────────────────────────┘ └────────────────────────────────────────────┘ ║
149
+ ╠════════════════════════════════════════════════════════════════════════════════════════════════╣
150
+ ║ [1] View Accounts [2] View Stats ║
151
+ ║ [+] Add Prop-Account [A] Algo-Trading
152
+ ║ [U] Update HQX [X] Disconnect ║
153
+ ╚════════════════════════════════════════════════════════════════════════════════════════════════╝
135
154
  ```
136
155
 
137
156
  ---
@@ -141,27 +160,32 @@ hedgequantx version
141
160
  ```
142
161
  HQX-CLI/
143
162
  ├── bin/
144
- │ └── cli.js # Entry point
163
+ │ └── cli.js # Entry point
145
164
  ├── src/
146
- │ ├── app.js # Main router
165
+ │ ├── app.js # Main router (380 lines)
147
166
  │ ├── config/
148
- │ │ ├── index.js
149
- │ │ ├── constants.js
150
- │ └── propfirms.js # 37 PropFirms config
167
+ │ │ ├── constants.js # Futures symbols
168
+ │ │ └── propfirms.js # 37+ PropFirms config
169
+ ├── menus/
170
+ │ │ ├── connect.js # Connection menus
171
+ │ │ └── dashboard.js # Dashboard & update
151
172
  │ ├── pages/
173
+ │ │ ├── algo/
174
+ │ │ │ ├── ui.js # Algo trading UI
175
+ │ │ │ ├── one-account.js # One Account mode
176
+ │ │ │ └── copy-trading.js # Copy Trading mode
152
177
  │ │ ├── accounts.js
153
- │ │ ├── orders.js
154
- │ │ ├── positions.js
155
- │ │ ├── stats.js
156
- │ │ └── user.js
178
+ │ │ └── stats.js
157
179
  │ ├── security/
158
- │ │ ├── encryption.js # AES-256-GCM
159
- │ │ ├── validation.js # Input sanitization
160
- │ │ └── rateLimit.js # API rate limiting
180
+ │ │ ├── encryption.js # AES-256-GCM
181
+ │ │ ├── validation.js # Input sanitization
182
+ │ │ └── rateLimit.js # API rate limiting
161
183
  │ ├── services/
162
- │ │ ├── projectx.js # ProjectX API
163
- │ │ ├── session.js # Encrypted sessions
164
- │ │ └── hqx-server.js # HQX Server API
184
+ │ │ ├── projectx/ # ProjectX API
185
+ │ │ ├── rithmic/ # Rithmic API
186
+ │ │ ├── tradovate/ # Tradovate API
187
+ │ │ ├── hqx-server.js # HQX Server API
188
+ │ │ └── session.js # Encrypted sessions
165
189
  │ └── ui/
166
190
  │ ├── box.js
167
191
  │ ├── table.js
@@ -179,6 +203,7 @@ HQX-CLI/
179
203
  - Input validation and sanitization
180
204
  - API rate limiting
181
205
  - Secure file permissions (0600)
206
+ - No credentials stored in plain text
182
207
 
183
208
  ---
184
209
 
@@ -186,34 +211,53 @@ HQX-CLI/
186
211
 
187
212
  The CLI has a built-in update feature:
188
213
 
189
- 1. Select **Update HQX** from the dashboard
190
- 2. CLI pulls latest changes from GitHub
191
- 3. Installs dependencies
192
- 4. Auto-restarts with new version
214
+ 1. Select **[U] Update HQX** from the dashboard
215
+ 2. CLI checks npm registry for latest version
216
+ 3. Prompts for confirmation before updating
217
+ 4. Installs new version globally
218
+ 5. Auto-restarts with new version
193
219
 
194
220
  Or manually:
195
221
 
196
222
  ```bash
197
- cd ~/HQX-CLI && git pull origin main
223
+ npm install -g hedgequantx@latest
198
224
  ```
199
225
 
200
226
  ---
201
227
 
228
+ ## Changelog
229
+
230
+ ### v1.3.0
231
+ - Major refactoring for maintainability
232
+ - Robust update function with confirmation
233
+ - Fixed stdin leak in menus
234
+ - Split services into modules
235
+ - Algo UI: logs now show newest at bottom
236
+
237
+ ### v1.2.x
238
+ - Algo Trading: One Account & Copy Trading modes
239
+ - HQX Server integration
240
+ - Rithmic full support
241
+ - Multi-account dashboard
242
+ - Privacy mode for account names
243
+
244
+ ---
245
+
202
246
  ## Roadmap
203
247
 
204
248
  - [x] ProjectX integration
205
- - [x] Multi-propfirm support (37 firms)
206
- - [x] Account viewing
207
- - [x] Position viewing
208
- - [x] Order viewing
249
+ - [x] Rithmic integration
250
+ - [x] Multi-propfirm support (37+ firms)
251
+ - [x] Multi-account connections
209
252
  - [x] Stats with equity curve
210
253
  - [x] Encrypted sessions
211
- - [x] Multi-account connections
212
- - [ ] Rithmic integration
213
- - [ ] Tradovate integration
214
- - [ ] Algo trading engine
215
- - [ ] Real-time market data
216
- - [ ] Order placement
254
+ - [x] Algo Trading - One Account mode
255
+ - [x] Algo Trading - Copy Trading mode
256
+ - [x] HQX Server integration
257
+ - [ ] Tradovate full integration
258
+ - [ ] Real-time market data streaming
259
+ - [ ] Advanced order types
260
+ - [ ] Mobile companion app
217
261
 
218
262
  ---
219
263
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedgequantx",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "Prop Futures Algo Trading CLI - Connect to Topstep, Alpha Futures, and other prop firms",
5
5
  "main": "src/app.js",
6
6
  "bin": {
@@ -3,7 +3,7 @@
3
3
  * @module services
4
4
  */
5
5
 
6
- const { ProjectXService } = require('./projectx');
6
+ const { ProjectXService } = require('./projectx/index');
7
7
  const { storage, connections } = require('./session');
8
8
 
9
9
  module.exports = {
@@ -0,0 +1,345 @@
1
+ /**
2
+ * ProjectX API Service
3
+ * Main service for ProjectX prop firm connections
4
+ */
5
+
6
+ const https = require('https');
7
+ const { PROPFIRMS } = require('../../config');
8
+ const {
9
+ validateUsername,
10
+ validatePassword,
11
+ validateApiKey,
12
+ validateAccountId,
13
+ sanitizeString,
14
+ maskSensitive
15
+ } = require('../../security');
16
+ const { getLimiter } = require('../../security/rateLimit');
17
+ const { getMarketHolidays, checkHoliday, checkMarketHours } = require('./market');
18
+ const { calculateLifetimeStats, calculateDailyPnL, formatTrades } = require('./stats');
19
+
20
+ class ProjectXService {
21
+ constructor(propfirmKey = 'topstep') {
22
+ this.propfirm = PROPFIRMS[propfirmKey] || PROPFIRMS.topstep;
23
+ this.propfirmKey = propfirmKey;
24
+ this.token = null;
25
+ this.user = null;
26
+ this.rateLimiter = getLimiter('api');
27
+ this.loginLimiter = getLimiter('login');
28
+ this.orderLimiter = getLimiter('orders');
29
+ }
30
+
31
+ getToken() { return this.token; }
32
+ getPropfirm() { return this.propfirmKey; }
33
+
34
+ // ==================== HTTP ====================
35
+
36
+ async _request(host, path, method = 'GET', data = null, limiterType = 'api') {
37
+ const limiter = limiterType === 'login' ? this.loginLimiter :
38
+ limiterType === 'orders' ? this.orderLimiter : this.rateLimiter;
39
+ return limiter.execute(() => this._doRequest(host, path, method, data));
40
+ }
41
+
42
+ async _doRequest(host, path, method, data) {
43
+ return new Promise((resolve, reject) => {
44
+ const options = {
45
+ hostname: host,
46
+ port: 443,
47
+ path: path,
48
+ method: method,
49
+ headers: {
50
+ 'Content-Type': 'application/json',
51
+ 'Accept': 'application/json',
52
+ 'User-Agent': 'HedgeQuantX-CLI/1.3.0'
53
+ },
54
+ timeout: 15000
55
+ };
56
+
57
+ if (this.token) {
58
+ options.headers['Authorization'] = `Bearer ${this.token}`;
59
+ }
60
+
61
+ const req = https.request(options, (res) => {
62
+ let body = '';
63
+ res.on('data', chunk => body += chunk);
64
+ res.on('end', () => {
65
+ try {
66
+ resolve({ statusCode: res.statusCode, data: JSON.parse(body) });
67
+ } catch (e) {
68
+ resolve({ statusCode: res.statusCode, data: body });
69
+ }
70
+ });
71
+ });
72
+
73
+ req.on('error', reject);
74
+ req.on('timeout', () => { req.destroy(); reject(new Error('Request timeout')); });
75
+ if (data) req.write(JSON.stringify(data));
76
+ req.end();
77
+ });
78
+ }
79
+
80
+ // ==================== AUTH ====================
81
+
82
+ async login(userName, password) {
83
+ try {
84
+ validateUsername(userName);
85
+ validatePassword(password);
86
+
87
+ const response = await this._request(
88
+ this.propfirm.userApi, '/Login', 'POST',
89
+ { userName: sanitizeString(userName), password },
90
+ 'login'
91
+ );
92
+
93
+ if (response.statusCode === 200 && response.data.token) {
94
+ this.token = response.data.token;
95
+ return { success: true, token: maskSensitive(this.token) };
96
+ }
97
+
98
+ return { success: false, error: response.data.errorMessage || 'Invalid credentials' };
99
+ } catch (error) {
100
+ return { success: false, error: error.message };
101
+ }
102
+ }
103
+
104
+ async loginWithApiKey(userName, apiKey) {
105
+ try {
106
+ validateUsername(userName);
107
+ validateApiKey(apiKey);
108
+
109
+ const response = await this._request(
110
+ this.propfirm.userApi, '/Login/key', 'POST',
111
+ { userName: sanitizeString(userName), apiKey },
112
+ 'login'
113
+ );
114
+
115
+ if (response.statusCode === 200 && response.data.token) {
116
+ this.token = response.data.token;
117
+ return { success: true, token: maskSensitive(this.token) };
118
+ }
119
+
120
+ return { success: false, error: response.data.errorMessage || 'Invalid API key' };
121
+ } catch (error) {
122
+ return { success: false, error: error.message };
123
+ }
124
+ }
125
+
126
+ logout() {
127
+ this.token = null;
128
+ this.user = null;
129
+ }
130
+
131
+ // ==================== USER ====================
132
+
133
+ async getUser() {
134
+ try {
135
+ const response = await this._request(this.propfirm.userApi, '/User', 'GET');
136
+ if (response.statusCode === 200) {
137
+ this.user = response.data;
138
+ return { success: true, user: response.data };
139
+ }
140
+ return { success: false, error: 'Failed to get user info' };
141
+ } catch (error) {
142
+ return { success: false, error: error.message };
143
+ }
144
+ }
145
+
146
+ // ==================== ACCOUNTS ====================
147
+
148
+ async getTradingAccounts() {
149
+ try {
150
+ const response = await this._request(this.propfirm.userApi, '/TradingAccount', 'GET');
151
+ if (response.statusCode === 200) {
152
+ return { success: true, accounts: Array.isArray(response.data) ? response.data : [] };
153
+ }
154
+ return { success: false, accounts: [], error: 'Failed to get accounts' };
155
+ } catch (error) {
156
+ return { success: false, accounts: [], error: error.message };
157
+ }
158
+ }
159
+
160
+ // ==================== TRADING ====================
161
+
162
+ async getPositions(accountId) {
163
+ try {
164
+ const id = validateAccountId(accountId);
165
+ const response = await this._request(
166
+ this.propfirm.gatewayApi, '/api/Position/searchOpen', 'POST', { accountId: id }
167
+ );
168
+ if (response.statusCode === 200) {
169
+ const positions = response.data.positions || response.data || [];
170
+ return { success: true, positions: Array.isArray(positions) ? positions : [] };
171
+ }
172
+ return { success: true, positions: [] };
173
+ } catch (error) {
174
+ return { success: true, positions: [], error: error.message };
175
+ }
176
+ }
177
+
178
+ async getOrders(accountId) {
179
+ try {
180
+ const id = validateAccountId(accountId);
181
+ const response = await this._request(
182
+ this.propfirm.gatewayApi, '/api/Order/searchOpen', 'POST', { accountId: id }
183
+ );
184
+ if (response.statusCode === 200) {
185
+ const orders = response.data.orders || response.data || [];
186
+ return { success: true, orders: Array.isArray(orders) ? orders : [] };
187
+ }
188
+ return { success: true, orders: [] };
189
+ } catch (error) {
190
+ return { success: true, orders: [], error: error.message };
191
+ }
192
+ }
193
+
194
+ async placeOrder(orderData) {
195
+ try {
196
+ const response = await this._request(
197
+ this.propfirm.gatewayApi, '/api/Order/place', 'POST', orderData, 'orders'
198
+ );
199
+ if (response.statusCode === 200 && response.data.success) {
200
+ return { success: true, order: response.data };
201
+ }
202
+ return { success: false, error: response.data.errorMessage || 'Order failed' };
203
+ } catch (error) {
204
+ return { success: false, error: error.message };
205
+ }
206
+ }
207
+
208
+ async cancelOrder(orderId) {
209
+ try {
210
+ const response = await this._request(
211
+ this.propfirm.gatewayApi, '/api/Order/cancel', 'POST',
212
+ { orderId: parseInt(orderId, 10) }, 'orders'
213
+ );
214
+ return { success: response.statusCode === 200 && response.data.success };
215
+ } catch (error) {
216
+ return { success: false, error: error.message };
217
+ }
218
+ }
219
+
220
+ async cancelAllOrders(accountId) {
221
+ try {
222
+ const id = validateAccountId(accountId);
223
+ const ordersResult = await this.getOrders(id);
224
+ if (!ordersResult.success || !ordersResult.orders) {
225
+ return { success: true, cancelled: 0 };
226
+ }
227
+
228
+ const pendingOrders = ordersResult.orders.filter(o =>
229
+ o.status === 'Working' || o.status === 'Pending' || o.status === 0 || o.status === 1
230
+ );
231
+
232
+ let cancelled = 0;
233
+ for (const order of pendingOrders) {
234
+ const result = await this.cancelOrder(order.orderId || order.id);
235
+ if (result.success) cancelled++;
236
+ }
237
+
238
+ return { success: true, cancelled };
239
+ } catch (error) {
240
+ return { success: false, error: error.message };
241
+ }
242
+ }
243
+
244
+ async closePosition(accountId, contractId) {
245
+ try {
246
+ const id = validateAccountId(accountId);
247
+ const response = await this._request(
248
+ this.propfirm.gatewayApi, '/api/Position/closeContract', 'POST',
249
+ { accountId: id, contractId }, 'orders'
250
+ );
251
+ return { success: response.statusCode === 200 && response.data.success };
252
+ } catch (error) {
253
+ return { success: false, error: error.message };
254
+ }
255
+ }
256
+
257
+ // ==================== TRADES & STATS ====================
258
+
259
+ async getTradeHistory(accountId, days = 30) {
260
+ try {
261
+ const id = validateAccountId(accountId);
262
+ const endDate = new Date();
263
+ const startDate = new Date();
264
+ startDate.setDate(startDate.getDate() - days);
265
+
266
+ const response = await this._request(
267
+ this.propfirm.gatewayApi, '/api/Trade/search', 'POST',
268
+ { accountId: id, startTimestamp: startDate.toISOString(), endTimestamp: endDate.toISOString() }
269
+ );
270
+
271
+ if (response.statusCode === 200 && response.data) {
272
+ let trades = Array.isArray(response.data) ? response.data : (response.data.trades || []);
273
+ return { success: true, trades: formatTrades(trades) };
274
+ }
275
+
276
+ return { success: true, trades: [] };
277
+ } catch (error) {
278
+ return { success: true, trades: [], error: error.message };
279
+ }
280
+ }
281
+
282
+ async getDailyStats(accountId) {
283
+ try {
284
+ const id = validateAccountId(accountId);
285
+ const now = new Date();
286
+ const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
287
+
288
+ const response = await this._request(
289
+ this.propfirm.gatewayApi, '/api/Trade/search', 'POST',
290
+ { accountId: id, startTimestamp: startOfMonth.toISOString(), endTimestamp: now.toISOString() }
291
+ );
292
+
293
+ if (response.statusCode === 200 && response.data) {
294
+ let trades = Array.isArray(response.data) ? response.data : (response.data.trades || []);
295
+ return { success: true, stats: calculateDailyPnL(trades) };
296
+ }
297
+
298
+ return { success: false, stats: [] };
299
+ } catch (error) {
300
+ return { success: false, stats: [], error: error.message };
301
+ }
302
+ }
303
+
304
+ async getLifetimeStats(accountId) {
305
+ try {
306
+ const tradesResult = await this.getTradeHistory(accountId, 90);
307
+ if (!tradesResult.success || tradesResult.trades.length === 0) {
308
+ return { success: true, stats: null };
309
+ }
310
+ return { success: true, stats: calculateLifetimeStats(tradesResult.trades) };
311
+ } catch (error) {
312
+ return { success: false, stats: null, error: error.message };
313
+ }
314
+ }
315
+
316
+ // ==================== CONTRACTS ====================
317
+
318
+ async searchContracts(searchText) {
319
+ try {
320
+ const response = await this._request(
321
+ this.propfirm.gatewayApi, '/api/Contract/search', 'POST',
322
+ { searchText: sanitizeString(searchText), live: false }
323
+ );
324
+ if (response.statusCode === 200) {
325
+ return { success: true, contracts: response.data.contracts || response.data || [] };
326
+ }
327
+ return { success: false, contracts: [] };
328
+ } catch (error) {
329
+ return { success: false, contracts: [], error: error.message };
330
+ }
331
+ }
332
+
333
+ // ==================== MARKET STATUS ====================
334
+
335
+ getMarketHolidays() { return getMarketHolidays(); }
336
+ checkHoliday() { return checkHoliday(); }
337
+ checkMarketHours() { return checkMarketHours(); }
338
+
339
+ async getMarketStatus(accountId) {
340
+ const hours = checkMarketHours();
341
+ return { success: true, isOpen: hours.isOpen, message: hours.message };
342
+ }
343
+ }
344
+
345
+ module.exports = { ProjectXService };