hedgequantx 2.9.19 → 2.9.20

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.
Files changed (39) hide show
  1. package/package.json +1 -1
  2. package/src/app.js +42 -64
  3. package/src/lib/m/hqx-2b.js +7 -0
  4. package/src/lib/m/index.js +138 -0
  5. package/src/lib/m/ultra-scalping.js +7 -0
  6. package/src/menus/connect.js +14 -17
  7. package/src/menus/dashboard.js +58 -76
  8. package/src/pages/accounts.js +38 -49
  9. package/src/pages/algo/copy-trading.js +546 -178
  10. package/src/pages/algo/index.js +18 -75
  11. package/src/pages/algo/one-account.js +322 -57
  12. package/src/pages/algo/ui.js +15 -15
  13. package/src/pages/orders.js +19 -22
  14. package/src/pages/positions.js +19 -22
  15. package/src/pages/stats/index.js +15 -16
  16. package/src/pages/user.js +7 -11
  17. package/src/services/ai-supervision/health.js +35 -47
  18. package/src/services/index.js +1 -9
  19. package/src/services/rithmic/accounts.js +8 -6
  20. package/src/ui/box.js +9 -5
  21. package/src/ui/index.js +5 -18
  22. package/src/ui/menu.js +4 -4
  23. package/src/pages/ai-agents-ui.js +0 -388
  24. package/src/pages/ai-agents.js +0 -494
  25. package/src/pages/ai-models.js +0 -389
  26. package/src/pages/algo/algo-executor.js +0 -307
  27. package/src/pages/algo/copy-executor.js +0 -331
  28. package/src/pages/algo/custom-strategy.js +0 -313
  29. package/src/services/ai-supervision/consensus.js +0 -284
  30. package/src/services/ai-supervision/context.js +0 -275
  31. package/src/services/ai-supervision/directive.js +0 -167
  32. package/src/services/ai-supervision/index.js +0 -359
  33. package/src/services/ai-supervision/parser.js +0 -278
  34. package/src/services/ai-supervision/symbols.js +0 -259
  35. package/src/services/cliproxy/index.js +0 -256
  36. package/src/services/cliproxy/installer.js +0 -111
  37. package/src/services/cliproxy/manager.js +0 -387
  38. package/src/services/llmproxy/index.js +0 -166
  39. package/src/services/llmproxy/manager.js +0 -411
@@ -1,278 +0,0 @@
1
- /**
2
- * AI Response Parser
3
- *
4
- * Parses responses from AI agents (JSON or text)
5
- * and normalizes them to a standard format.
6
- */
7
-
8
- /**
9
- * Default response when parsing fails
10
- */
11
- const DEFAULT_RESPONSE = {
12
- decision: 'approve',
13
- confidence: 50,
14
- optimizations: null,
15
- reason: 'Parse failed - defaulting to approve',
16
- alerts: null,
17
- parseSuccess: false
18
- };
19
-
20
- /**
21
- * Extract JSON from a string that may contain markdown or extra text
22
- */
23
- const extractJSON = (text) => {
24
- if (!text || typeof text !== 'string') return null;
25
-
26
- // Try direct parse first
27
- try {
28
- return JSON.parse(text.trim());
29
- } catch (e) { /* continue */ }
30
-
31
- // Try to find JSON in markdown code blocks
32
- const codeBlockMatch = text.match(/```(?:json)?\s*([\s\S]*?)```/);
33
- if (codeBlockMatch) {
34
- try {
35
- return JSON.parse(codeBlockMatch[1].trim());
36
- } catch (e) { /* continue */ }
37
- }
38
-
39
- // Try to find JSON object pattern
40
- const jsonMatch = text.match(/\{[\s\S]*"decision"[\s\S]*\}/);
41
- if (jsonMatch) {
42
- try {
43
- return JSON.parse(jsonMatch[0]);
44
- } catch (e) { /* continue */ }
45
- }
46
-
47
- return null;
48
- };
49
-
50
- /**
51
- * Validate and normalize the decision field
52
- */
53
- const normalizeDecision = (decision) => {
54
- if (!decision) return 'approve';
55
-
56
- const d = String(decision).toLowerCase().trim();
57
-
58
- if (d === 'approve' || d === 'yes' || d === 'accept' || d === 'go') return 'approve';
59
- if (d === 'reject' || d === 'no' || d === 'deny' || d === 'stop') return 'reject';
60
- if (d === 'modify' || d === 'adjust' || d === 'optimize') return 'modify';
61
-
62
- return 'approve';
63
- };
64
-
65
- /**
66
- * Validate and normalize confidence score
67
- */
68
- const normalizeConfidence = (confidence) => {
69
- if (confidence === undefined || confidence === null) return 50;
70
-
71
- const c = Number(confidence);
72
- if (isNaN(c)) return 50;
73
-
74
- // Handle percentage strings like "85%"
75
- if (typeof confidence === 'string' && confidence.includes('%')) {
76
- const parsed = parseFloat(confidence);
77
- if (!isNaN(parsed)) return Math.min(100, Math.max(0, parsed));
78
- }
79
-
80
- // Normalize to 0-100 range
81
- if (c >= 0 && c <= 1) return Math.round(c * 100);
82
- return Math.min(100, Math.max(0, Math.round(c)));
83
- };
84
-
85
- /**
86
- * Validate and normalize optimizations
87
- */
88
- const normalizeOptimizations = (opts, signal) => {
89
- if (!opts) return null;
90
-
91
- const normalized = {
92
- entry: null,
93
- stopLoss: null,
94
- takeProfit: null,
95
- size: null,
96
- timing: 'now'
97
- };
98
-
99
- // Entry price
100
- if (opts.entry !== undefined && opts.entry !== null) {
101
- const entry = Number(opts.entry);
102
- if (!isNaN(entry) && entry > 0) normalized.entry = entry;
103
- }
104
-
105
- // Stop loss
106
- if (opts.stopLoss !== undefined && opts.stopLoss !== null) {
107
- const sl = Number(opts.stopLoss);
108
- if (!isNaN(sl) && sl > 0) normalized.stopLoss = sl;
109
- }
110
-
111
- // Take profit
112
- if (opts.takeProfit !== undefined && opts.takeProfit !== null) {
113
- const tp = Number(opts.takeProfit);
114
- if (!isNaN(tp) && tp > 0) normalized.takeProfit = tp;
115
- }
116
-
117
- // Size adjustment (-0.5 to +0.5)
118
- if (opts.size !== undefined && opts.size !== null) {
119
- const size = Number(opts.size);
120
- if (!isNaN(size)) {
121
- normalized.size = Math.min(0.5, Math.max(-0.5, size));
122
- }
123
- }
124
-
125
- // Timing
126
- if (opts.timing) {
127
- const t = String(opts.timing).toLowerCase().trim();
128
- if (t === 'now' || t === 'immediate') normalized.timing = 'now';
129
- else if (t === 'wait' || t === 'delay') normalized.timing = 'wait';
130
- else if (t === 'cancel' || t === 'abort') normalized.timing = 'cancel';
131
- else normalized.timing = 'now';
132
- }
133
-
134
- return normalized;
135
- };
136
-
137
- /**
138
- * Normalize reason string
139
- */
140
- const normalizeReason = (reason) => {
141
- if (!reason) return 'No reason provided';
142
-
143
- const r = String(reason).trim();
144
- if (r.length > 100) return r.substring(0, 97) + '...';
145
- return r;
146
- };
147
-
148
- /**
149
- * Normalize alerts array
150
- */
151
- const normalizeAlerts = (alerts) => {
152
- if (!alerts) return null;
153
- if (!Array.isArray(alerts)) {
154
- if (typeof alerts === 'string') return [alerts];
155
- return null;
156
- }
157
- return alerts.filter(a => a && typeof a === 'string').slice(0, 5);
158
- };
159
-
160
- /**
161
- * Parse text response when JSON parsing fails
162
- * Attempts to extract decision from natural language
163
- */
164
- const parseTextResponse = (text, signal) => {
165
- if (!text) return DEFAULT_RESPONSE;
166
-
167
- const lower = text.toLowerCase();
168
-
169
- // Determine decision from keywords
170
- let decision = 'approve';
171
- if (lower.includes('reject') || lower.includes('do not') || lower.includes("don't") ||
172
- lower.includes('avoid') || lower.includes('skip') || lower.includes('no trade')) {
173
- decision = 'reject';
174
- } else if (lower.includes('modify') || lower.includes('adjust') || lower.includes('optimize') ||
175
- lower.includes('tighten') || lower.includes('widen')) {
176
- decision = 'modify';
177
- }
178
-
179
- // Try to extract confidence
180
- let confidence = 60;
181
- const confMatch = lower.match(/confidence[:\s]*(\d+)/i) ||
182
- lower.match(/(\d+)%?\s*confiden/i) ||
183
- lower.match(/score[:\s]*(\d+)/i);
184
- if (confMatch) {
185
- confidence = normalizeConfidence(confMatch[1]);
186
- }
187
-
188
- // Extract reason (first sentence or up to 100 chars)
189
- let reason = text.split(/[.!?\n]/)[0]?.trim() || 'Parsed from text response';
190
- reason = normalizeReason(reason);
191
-
192
- return {
193
- decision,
194
- confidence,
195
- optimizations: decision === 'modify' ? {
196
- entry: signal?.entry || null,
197
- stopLoss: signal?.stopLoss || null,
198
- takeProfit: signal?.takeProfit || null,
199
- size: null,
200
- timing: 'now'
201
- } : null,
202
- reason,
203
- alerts: null,
204
- parseSuccess: false,
205
- parsedFromText: true
206
- };
207
- };
208
-
209
- /**
210
- * Main parser function - parse AI response to standard format
211
- */
212
- const parseAgentResponse = (response, signal = null) => {
213
- // Handle empty response
214
- if (!response) {
215
- return { ...DEFAULT_RESPONSE, reason: 'Empty response from agent' };
216
- }
217
-
218
- // Handle response object with content field (common API format)
219
- let text = response;
220
- if (typeof response === 'object') {
221
- if (response.content) text = response.content;
222
- else if (response.text) text = response.text;
223
- else if (response.message) text = response.message;
224
- else text = JSON.stringify(response);
225
- }
226
-
227
- // Try to extract and parse JSON
228
- const json = extractJSON(text);
229
-
230
- if (json && json.decision) {
231
- // Successfully parsed JSON
232
- return {
233
- decision: normalizeDecision(json.decision),
234
- confidence: normalizeConfidence(json.confidence),
235
- optimizations: normalizeOptimizations(json.optimizations, signal),
236
- reason: normalizeReason(json.reason),
237
- alerts: normalizeAlerts(json.alerts),
238
- parseSuccess: true
239
- };
240
- }
241
-
242
- // Fallback to text parsing
243
- return parseTextResponse(text, signal);
244
- };
245
-
246
- /**
247
- * Validate a parsed response
248
- */
249
- const validateResponse = (parsed) => {
250
- const errors = [];
251
-
252
- if (!['approve', 'reject', 'modify'].includes(parsed.decision)) {
253
- errors.push(`Invalid decision: ${parsed.decision}`);
254
- }
255
-
256
- if (parsed.confidence < 0 || parsed.confidence > 100) {
257
- errors.push(`Invalid confidence: ${parsed.confidence}`);
258
- }
259
-
260
- if (parsed.decision === 'modify' && !parsed.optimizations) {
261
- errors.push('Modify decision requires optimizations');
262
- }
263
-
264
- return {
265
- valid: errors.length === 0,
266
- errors
267
- };
268
- };
269
-
270
- module.exports = {
271
- parseAgentResponse,
272
- validateResponse,
273
- extractJSON,
274
- normalizeDecision,
275
- normalizeConfidence,
276
- normalizeOptimizations,
277
- DEFAULT_RESPONSE
278
- };
@@ -1,259 +0,0 @@
1
- /**
2
- * Symbol Data for AI Supervision
3
- *
4
- * Contains detailed information about tradeable symbols
5
- * including tick sizes, sessions, correlations, and characteristics.
6
- */
7
-
8
- const SYMBOLS = {
9
- NQ: {
10
- id: 'NQ',
11
- name: 'E-mini Nasdaq 100',
12
- exchange: 'CME',
13
- tickSize: 0.25,
14
- tickValue: 5.00,
15
- pointValue: 20.00,
16
- margin: 15000,
17
- characteristics: {
18
- volatility: 'high',
19
- sector: 'technology',
20
- behavior: 'momentum-driven, gap-prone',
21
- avgRange: '150-300 points'
22
- },
23
- correlations: {
24
- positive: ['ES', 'YM'],
25
- negative: ['VIX'],
26
- related: ['QQQ', 'AAPL', 'MSFT', 'NVDA']
27
- },
28
- sessions: {
29
- most_active: ['us_open', 'us_close'],
30
- avoid: ['asia_lunch', 'low_volume']
31
- }
32
- },
33
-
34
- ES: {
35
- id: 'ES',
36
- name: 'E-mini S&P 500',
37
- exchange: 'CME',
38
- tickSize: 0.25,
39
- tickValue: 12.50,
40
- pointValue: 50.00,
41
- margin: 12000,
42
- characteristics: {
43
- volatility: 'medium',
44
- sector: 'broad_market',
45
- behavior: 'reference index, institutional flow',
46
- avgRange: '30-60 points'
47
- },
48
- correlations: {
49
- positive: ['NQ', 'YM', 'RTY'],
50
- negative: ['VIX', 'ZB'],
51
- related: ['SPY', 'SPX']
52
- },
53
- sessions: {
54
- most_active: ['us_open', 'us_close', 'london_us_overlap'],
55
- avoid: ['asia_session']
56
- }
57
- },
58
-
59
- YM: {
60
- id: 'YM',
61
- name: 'E-mini Dow',
62
- exchange: 'CME',
63
- tickSize: 1.00,
64
- tickValue: 5.00,
65
- pointValue: 5.00,
66
- margin: 10000,
67
- characteristics: {
68
- volatility: 'medium-low',
69
- sector: 'value_stocks',
70
- behavior: 'slower than ES/NQ, less spiky',
71
- avgRange: '200-400 points'
72
- },
73
- correlations: {
74
- positive: ['ES', 'NQ'],
75
- negative: ['VIX'],
76
- related: ['DIA', 'DJIA']
77
- },
78
- sessions: {
79
- most_active: ['us_open', 'us_close'],
80
- avoid: ['overnight']
81
- }
82
- },
83
-
84
- RTY: {
85
- id: 'RTY',
86
- name: 'E-mini Russell 2000',
87
- exchange: 'CME',
88
- tickSize: 0.10,
89
- tickValue: 5.00,
90
- pointValue: 50.00,
91
- margin: 8000,
92
- characteristics: {
93
- volatility: 'high',
94
- sector: 'small_caps',
95
- behavior: 'more volatile than ES, liquidity gaps',
96
- avgRange: '20-40 points'
97
- },
98
- correlations: {
99
- positive: ['ES', 'NQ'],
100
- negative: ['VIX'],
101
- related: ['IWM', 'RUT']
102
- },
103
- sessions: {
104
- most_active: ['us_open'],
105
- avoid: ['overnight', 'low_volume']
106
- }
107
- },
108
-
109
- GC: {
110
- id: 'GC',
111
- name: 'Gold Futures',
112
- exchange: 'COMEX',
113
- tickSize: 0.10,
114
- tickValue: 10.00,
115
- pointValue: 100.00,
116
- margin: 11000,
117
- characteristics: {
118
- volatility: 'medium',
119
- sector: 'precious_metals',
120
- behavior: 'safe haven, inverse USD, central bank sensitive',
121
- avgRange: '15-30 points'
122
- },
123
- correlations: {
124
- positive: ['SI', 'EURUSD'],
125
- negative: ['DXY', 'US10Y'],
126
- related: ['GLD', 'XAUUSD']
127
- },
128
- sessions: {
129
- most_active: ['london', 'us_open', 'asia_open'],
130
- avoid: ['us_afternoon']
131
- }
132
- },
133
-
134
- SI: {
135
- id: 'SI',
136
- name: 'Silver Futures',
137
- exchange: 'COMEX',
138
- tickSize: 0.005,
139
- tickValue: 25.00,
140
- pointValue: 5000.00,
141
- margin: 9000,
142
- characteristics: {
143
- volatility: 'high',
144
- sector: 'precious_metals',
145
- behavior: 'follows gold with more volatility, industrial demand',
146
- avgRange: '0.30-0.60 points'
147
- },
148
- correlations: {
149
- positive: ['GC'],
150
- negative: ['DXY'],
151
- related: ['SLV', 'XAGUSD']
152
- },
153
- sessions: {
154
- most_active: ['london', 'us_open'],
155
- avoid: ['asia_lunch']
156
- }
157
- },
158
-
159
- CL: {
160
- id: 'CL',
161
- name: 'Crude Oil Futures',
162
- exchange: 'NYMEX',
163
- tickSize: 0.01,
164
- tickValue: 10.00,
165
- pointValue: 1000.00,
166
- margin: 7000,
167
- characteristics: {
168
- volatility: 'high',
169
- sector: 'energy',
170
- behavior: 'news-driven, inventories, geopolitical, OPEC',
171
- avgRange: '1.50-3.00 points'
172
- },
173
- correlations: {
174
- positive: ['BZ', 'XLE'],
175
- negative: [],
176
- related: ['USO', 'WTI']
177
- },
178
- sessions: {
179
- most_active: ['us_open', 'inventory_report'],
180
- avoid: ['overnight_thin']
181
- }
182
- }
183
- };
184
-
185
- /**
186
- * Trading sessions with times (Eastern Time)
187
- */
188
- const SESSIONS = {
189
- asia_open: { start: '18:00', end: '20:00', description: 'Asia market open' },
190
- asia_session: { start: '20:00', end: '03:00', description: 'Asia main session' },
191
- asia_lunch: { start: '00:00', end: '01:00', description: 'Asia lunch (low volume)' },
192
- london: { start: '03:00', end: '08:00', description: 'London session' },
193
- london_us_overlap: { start: '08:00', end: '11:30', description: 'London/US overlap' },
194
- us_open: { start: '09:30', end: '11:30', description: 'US market open (high volume)' },
195
- us_midday: { start: '11:30', end: '14:00', description: 'US midday (lower volume)' },
196
- us_afternoon: { start: '14:00', end: '15:00', description: 'US afternoon' },
197
- us_close: { start: '15:00', end: '16:00', description: 'US close (rebalancing)' },
198
- overnight: { start: '16:00', end: '18:00', description: 'Overnight transition' }
199
- };
200
-
201
- /**
202
- * Get symbol data by ID
203
- */
204
- const getSymbol = (symbolId) => {
205
- const key = symbolId?.toUpperCase?.()?.replace(/[0-9]/g, '') || '';
206
- return SYMBOLS[key] || null;
207
- };
208
-
209
- /**
210
- * Get current session based on time
211
- */
212
- const getCurrentSession = (date = new Date()) => {
213
- const et = new Date(date.toLocaleString('en-US', { timeZone: 'America/New_York' }));
214
- const hours = et.getHours();
215
- const minutes = et.getMinutes();
216
- const time = hours * 60 + minutes;
217
-
218
- for (const [name, session] of Object.entries(SESSIONS)) {
219
- const [startH, startM] = session.start.split(':').map(Number);
220
- const [endH, endM] = session.end.split(':').map(Number);
221
- const start = startH * 60 + startM;
222
- const end = endH * 60 + endM;
223
-
224
- if (start <= end) {
225
- if (time >= start && time < end) return { name, ...session };
226
- } else {
227
- if (time >= start || time < end) return { name, ...session };
228
- }
229
- }
230
- return { name: 'unknown', description: 'Unknown session' };
231
- };
232
-
233
- /**
234
- * Check if current time is good for trading a symbol
235
- */
236
- const isGoodSessionForSymbol = (symbolId, date = new Date()) => {
237
- const symbol = getSymbol(symbolId);
238
- if (!symbol) return { good: true, reason: 'Unknown symbol' };
239
-
240
- const session = getCurrentSession(date);
241
-
242
- if (symbol.sessions.avoid?.includes(session.name)) {
243
- return { good: false, reason: `${session.description} - typically low volume for ${symbolId}` };
244
- }
245
-
246
- if (symbol.sessions.most_active?.includes(session.name)) {
247
- return { good: true, reason: `${session.description} - optimal for ${symbolId}` };
248
- }
249
-
250
- return { good: true, reason: session.description };
251
- };
252
-
253
- module.exports = {
254
- SYMBOLS,
255
- SESSIONS,
256
- getSymbol,
257
- getCurrentSession,
258
- isGoodSessionForSymbol
259
- };