hedgequantx 1.3.0 → 1.3.2
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 -71
- package/package.json +1 -1
- package/src/services/index.js +1 -1
- package/src/services/projectx/index.js +345 -0
- package/src/services/projectx/market.js +145 -0
- package/src/services/projectx/stats.js +110 -0
- package/src/services/rithmic/accounts.js +183 -0
- package/src/services/rithmic/handlers.js +191 -0
- package/src/services/rithmic/index.js +69 -673
- package/src/services/rithmic/orders.js +192 -0
- package/src/services/tradovate/index.js +10 -121
- package/src/services/tradovate/market.js +47 -0
- package/src/services/tradovate/websocket.js +97 -0
- package/src/services/projectx.js +0 -771
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
|
[](https://www.npmjs.com/package/hedgequantx)
|
|
17
17
|
[](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
|
|
24
|
-
- 37 supported prop firms
|
|
25
|
-
- Multi-account connections
|
|
26
|
-
- Real-time stats
|
|
27
|
-
-
|
|
28
|
-
-
|
|
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
|
-
##
|
|
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** -
|
|
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
|
-
║
|
|
119
|
-
|
|
120
|
-
║ Connections:
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
|
163
|
+
│ └── cli.js # Entry point
|
|
145
164
|
├── src/
|
|
146
|
-
│ ├── app.js
|
|
165
|
+
│ ├── app.js # Main router (380 lines)
|
|
147
166
|
│ ├── config/
|
|
148
|
-
│ │ ├──
|
|
149
|
-
│ │
|
|
150
|
-
│
|
|
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
|
-
│ │
|
|
154
|
-
│ │ ├── positions.js
|
|
155
|
-
│ │ ├── stats.js
|
|
156
|
-
│ │ └── user.js
|
|
178
|
+
│ │ └── stats.js
|
|
157
179
|
│ ├── security/
|
|
158
|
-
│ │ ├── encryption.js
|
|
159
|
-
│ │ ├── validation.js
|
|
160
|
-
│ │ └── rateLimit.js
|
|
180
|
+
│ │ ├── encryption.js # AES-256-GCM
|
|
181
|
+
│ │ ├── validation.js # Input sanitization
|
|
182
|
+
│ │ └── rateLimit.js # API rate limiting
|
|
161
183
|
│ ├── services/
|
|
162
|
-
│ │ ├── projectx
|
|
163
|
-
│ │ ├──
|
|
164
|
-
│ │
|
|
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
|
|
191
|
-
3.
|
|
192
|
-
4.
|
|
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
|
-
|
|
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]
|
|
206
|
-
- [x]
|
|
207
|
-
- [x]
|
|
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]
|
|
212
|
-
- [
|
|
213
|
-
- [
|
|
214
|
-
- [ ]
|
|
215
|
-
- [ ] Real-time market data
|
|
216
|
-
- [ ]
|
|
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
package/src/services/index.js
CHANGED
|
@@ -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 };
|