hedgequantx 2.9.18 → 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 -309
  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,284 +0,0 @@
1
- /**
2
- * Consensus Calculator for Multi-Agent Supervision
3
- *
4
- * Calculates weighted consensus from multiple AI agent responses.
5
- * Each agent has a weight, and the final decision is based on
6
- * the weighted average of all responses.
7
- */
8
-
9
- /**
10
- * Default consensus when no valid responses
11
- */
12
- const DEFAULT_CONSENSUS = {
13
- decision: 'approve',
14
- confidence: 50,
15
- optimizations: null,
16
- reason: 'No consensus - default approve',
17
- alerts: [],
18
- agentCount: 0,
19
- respondedCount: 0,
20
- unanimous: false
21
- };
22
-
23
- /**
24
- * Calculate weighted average of a numeric field
25
- */
26
- const weightedAverage = (values, weights) => {
27
- if (values.length === 0) return 0;
28
-
29
- let sum = 0;
30
- let totalWeight = 0;
31
-
32
- for (let i = 0; i < values.length; i++) {
33
- const val = values[i];
34
- const weight = weights[i] || 1;
35
- if (val !== null && val !== undefined && !isNaN(val)) {
36
- sum += val * weight;
37
- totalWeight += weight;
38
- }
39
- }
40
-
41
- return totalWeight > 0 ? sum / totalWeight : 0;
42
- };
43
-
44
- /**
45
- * Calculate weighted mode (most common value by weight)
46
- */
47
- const weightedMode = (values, weights) => {
48
- if (values.length === 0) return null;
49
-
50
- const weightMap = {};
51
-
52
- for (let i = 0; i < values.length; i++) {
53
- const val = values[i];
54
- const weight = weights[i] || 1;
55
- if (val !== null && val !== undefined) {
56
- weightMap[val] = (weightMap[val] || 0) + weight;
57
- }
58
- }
59
-
60
- let maxWeight = 0;
61
- let mode = null;
62
-
63
- for (const [val, w] of Object.entries(weightMap)) {
64
- if (w > maxWeight) {
65
- maxWeight = w;
66
- mode = val;
67
- }
68
- }
69
-
70
- return mode;
71
- };
72
-
73
- /**
74
- * Merge optimizations from multiple agents
75
- */
76
- const mergeOptimizations = (responses, weights) => {
77
- const validOpts = responses
78
- .map((r, i) => ({ opt: r.optimizations, weight: weights[i] }))
79
- .filter(o => o.opt !== null);
80
-
81
- if (validOpts.length === 0) return null;
82
-
83
- // Collect values for each field
84
- const entries = validOpts.filter(o => o.opt.entry !== null).map(o => ({ val: o.opt.entry, w: o.weight }));
85
- const stops = validOpts.filter(o => o.opt.stopLoss !== null).map(o => ({ val: o.opt.stopLoss, w: o.weight }));
86
- const targets = validOpts.filter(o => o.opt.takeProfit !== null).map(o => ({ val: o.opt.takeProfit, w: o.weight }));
87
- const sizes = validOpts.filter(o => o.opt.size !== null).map(o => ({ val: o.opt.size, w: o.weight }));
88
- const timings = validOpts.map(o => ({ val: o.opt.timing, w: o.weight }));
89
-
90
- return {
91
- entry: entries.length > 0
92
- ? Math.round(weightedAverage(entries.map(e => e.val), entries.map(e => e.w)) * 100) / 100
93
- : null,
94
- stopLoss: stops.length > 0
95
- ? Math.round(weightedAverage(stops.map(s => s.val), stops.map(s => s.w)) * 100) / 100
96
- : null,
97
- takeProfit: targets.length > 0
98
- ? Math.round(weightedAverage(targets.map(t => t.val), targets.map(t => t.w)) * 100) / 100
99
- : null,
100
- size: sizes.length > 0
101
- ? Math.round(weightedAverage(sizes.map(s => s.val), sizes.map(s => s.w)) * 100) / 100
102
- : null,
103
- timing: weightedMode(timings.map(t => t.val), timings.map(t => t.w)) || 'now'
104
- };
105
- };
106
-
107
- /**
108
- * Collect all alerts from responses
109
- */
110
- const collectAlerts = (responses) => {
111
- const alerts = [];
112
- for (const r of responses) {
113
- if (r.alerts && Array.isArray(r.alerts)) {
114
- alerts.push(...r.alerts);
115
- }
116
- }
117
- return [...new Set(alerts)].slice(0, 10);
118
- };
119
-
120
- /**
121
- * Build reason summary from all responses
122
- */
123
- const buildReasonSummary = (responses, decision) => {
124
- const reasons = responses
125
- .filter(r => r.decision === decision && r.reason)
126
- .map(r => r.reason)
127
- .slice(0, 3);
128
-
129
- if (reasons.length === 0) return `Consensus: ${decision}`;
130
- if (reasons.length === 1) return reasons[0];
131
-
132
- return reasons[0] + (reasons.length > 1 ? ` (+${reasons.length - 1} more)` : '');
133
- };
134
-
135
- /**
136
- * Calculate consensus from multiple agent responses
137
- *
138
- * @param {Array} agentResponses - Array of { agentId, response, weight }
139
- * @param {Object} options - Consensus options
140
- * @returns {Object} Consensus result
141
- */
142
- const calculateConsensus = (agentResponses, options = {}) => {
143
- const {
144
- minAgents = 1,
145
- approveThreshold = 0.5,
146
- rejectThreshold = 0.6,
147
- minConfidence = 30
148
- } = options;
149
-
150
- // Filter valid responses
151
- const validResponses = agentResponses.filter(ar =>
152
- ar && ar.response && ar.response.decision
153
- );
154
-
155
- if (validResponses.length === 0) {
156
- return { ...DEFAULT_CONSENSUS, reason: 'No valid agent responses' };
157
- }
158
-
159
- if (validResponses.length < minAgents) {
160
- return {
161
- ...DEFAULT_CONSENSUS,
162
- reason: `Insufficient agents (${validResponses.length}/${minAgents})`,
163
- agentCount: agentResponses.length,
164
- respondedCount: validResponses.length
165
- };
166
- }
167
-
168
- // Extract responses and weights
169
- const responses = validResponses.map(ar => ar.response);
170
- const weights = validResponses.map(ar => ar.weight || 100);
171
- const totalWeight = weights.reduce((a, b) => a + b, 0);
172
-
173
- // Calculate weighted votes for each decision
174
- const votes = { approve: 0, reject: 0, modify: 0 };
175
- for (let i = 0; i < responses.length; i++) {
176
- const decision = responses[i].decision;
177
- votes[decision] = (votes[decision] || 0) + weights[i];
178
- }
179
-
180
- // Normalize votes to percentages
181
- const approveRatio = votes.approve / totalWeight;
182
- const rejectRatio = votes.reject / totalWeight;
183
- const modifyRatio = votes.modify / totalWeight;
184
-
185
- // Determine consensus decision
186
- let decision;
187
- if (rejectRatio >= rejectThreshold) {
188
- decision = 'reject';
189
- } else if (approveRatio >= approveThreshold) {
190
- decision = modifyRatio > 0 ? 'modify' : 'approve';
191
- } else if (modifyRatio > approveRatio) {
192
- decision = 'modify';
193
- } else {
194
- decision = 'approve';
195
- }
196
-
197
- // Calculate weighted confidence
198
- const confidences = responses.map(r => r.confidence);
199
- const avgConfidence = Math.round(weightedAverage(confidences, weights));
200
-
201
- // Apply minimum confidence check
202
- if (avgConfidence < minConfidence && decision !== 'reject') {
203
- decision = 'reject';
204
- }
205
-
206
- // Check unanimity
207
- const decisions = responses.map(r => r.decision);
208
- const unanimous = new Set(decisions).size === 1;
209
-
210
- // Build consensus result
211
- const consensus = {
212
- decision,
213
- confidence: avgConfidence,
214
- optimizations: decision !== 'reject' ? mergeOptimizations(responses, weights) : null,
215
- reason: buildReasonSummary(responses, decision),
216
- alerts: collectAlerts(responses),
217
-
218
- // Metadata
219
- agentCount: agentResponses.length,
220
- respondedCount: validResponses.length,
221
- unanimous,
222
-
223
- // Vote breakdown
224
- votes: {
225
- approve: Math.round(approveRatio * 100),
226
- reject: Math.round(rejectRatio * 100),
227
- modify: Math.round(modifyRatio * 100)
228
- },
229
-
230
- // Individual responses for debugging
231
- agentDetails: validResponses.map(ar => ({
232
- agentId: ar.agentId,
233
- decision: ar.response.decision,
234
- confidence: ar.response.confidence,
235
- weight: ar.weight
236
- }))
237
- };
238
-
239
- return consensus;
240
- };
241
-
242
- /**
243
- * Quick check if consensus approves the signal
244
- */
245
- const isApproved = (consensus) => {
246
- return consensus.decision === 'approve' || consensus.decision === 'modify';
247
- };
248
-
249
- /**
250
- * Apply consensus optimizations to original signal
251
- */
252
- const applyOptimizations = (signal, consensus) => {
253
- if (!isApproved(consensus) || !consensus.optimizations) {
254
- return signal;
255
- }
256
-
257
- const opts = consensus.optimizations;
258
- const optimized = { ...signal };
259
-
260
- if (opts.entry !== null) optimized.entry = opts.entry;
261
- if (opts.stopLoss !== null) optimized.stopLoss = opts.stopLoss;
262
- if (opts.takeProfit !== null) optimized.takeProfit = opts.takeProfit;
263
-
264
- // Apply size adjustment
265
- if (opts.size !== null && signal.size) {
266
- optimized.size = Math.max(1, Math.round(signal.size * (1 + opts.size)));
267
- }
268
-
269
- optimized.aiOptimized = true;
270
- optimized.aiConfidence = consensus.confidence;
271
- optimized.aiTiming = opts.timing;
272
-
273
- return optimized;
274
- };
275
-
276
- module.exports = {
277
- calculateConsensus,
278
- isApproved,
279
- applyOptimizations,
280
- weightedAverage,
281
- weightedMode,
282
- mergeOptimizations,
283
- DEFAULT_CONSENSUS
284
- };
@@ -1,275 +0,0 @@
1
- /**
2
- * Context Builder for AI Supervision
3
- *
4
- * Builds the market context from real Rithmic data
5
- * to send to AI agents for signal analysis.
6
- */
7
-
8
- const { getSymbol, getCurrentSession, isGoodSessionForSymbol } = require('./symbols');
9
-
10
- /**
11
- * Build DOM (Depth of Market) summary from raw data
12
- */
13
- const buildDOMSummary = (domData) => {
14
- if (!domData || !domData.bids || !domData.asks) {
15
- return { available: false };
16
- }
17
-
18
- const bids = domData.bids.slice(0, 10);
19
- const asks = domData.asks.slice(0, 10);
20
-
21
- const totalBidSize = bids.reduce((sum, b) => sum + (b.size || 0), 0);
22
- const totalAskSize = asks.reduce((sum, a) => sum + (a.size || 0), 0);
23
- const imbalance = totalBidSize - totalAskSize;
24
- const imbalanceRatio = totalAskSize > 0 ? totalBidSize / totalAskSize : 1;
25
-
26
- return {
27
- available: true,
28
- topBid: bids[0]?.price || null,
29
- topAsk: asks[0]?.price || null,
30
- spread: asks[0] && bids[0] ? asks[0].price - bids[0].price : null,
31
- totalBidSize,
32
- totalAskSize,
33
- imbalance,
34
- imbalanceRatio: Math.round(imbalanceRatio * 100) / 100,
35
- bidLevels: bids.length,
36
- askLevels: asks.length,
37
- dominantSide: imbalance > 0 ? 'buyers' : imbalance < 0 ? 'sellers' : 'neutral'
38
- };
39
- };
40
-
41
- /**
42
- * Build Order Flow summary from recent ticks
43
- */
44
- const buildOrderFlowSummary = (recentTicks, windowSize = 50) => {
45
- if (!recentTicks || recentTicks.length === 0) {
46
- return { available: false };
47
- }
48
-
49
- const ticks = recentTicks.slice(-windowSize);
50
-
51
- let buyVolume = 0;
52
- let sellVolume = 0;
53
- let totalVolume = 0;
54
- let highPrice = -Infinity;
55
- let lowPrice = Infinity;
56
-
57
- for (const tick of ticks) {
58
- const vol = tick.volume || 1;
59
- totalVolume += vol;
60
-
61
- if (tick.side === 'buy' || tick.lastTradeSide === 'buy') {
62
- buyVolume += vol;
63
- } else if (tick.side === 'sell' || tick.lastTradeSide === 'sell') {
64
- sellVolume += vol;
65
- }
66
-
67
- if (tick.price > highPrice) highPrice = tick.price;
68
- if (tick.price < lowPrice) lowPrice = tick.price;
69
- }
70
-
71
- const delta = buyVolume - sellVolume;
72
- const deltaPercent = totalVolume > 0 ? (delta / totalVolume) * 100 : 0;
73
-
74
- return {
75
- available: true,
76
- tickCount: ticks.length,
77
- totalVolume,
78
- buyVolume,
79
- sellVolume,
80
- delta,
81
- deltaPercent: Math.round(deltaPercent),
82
- highPrice: highPrice === -Infinity ? null : highPrice,
83
- lowPrice: lowPrice === Infinity ? null : lowPrice,
84
- range: highPrice !== -Infinity && lowPrice !== Infinity ? highPrice - lowPrice : null,
85
- lastPrice: ticks[ticks.length - 1]?.price || null,
86
- trend: delta > 0 ? 'bullish' : delta < 0 ? 'bearish' : 'neutral'
87
- };
88
- };
89
-
90
- /**
91
- * Build trade history summary from recent signals/trades
92
- */
93
- const buildTradeHistory = (recentSignals, recentTrades) => {
94
- const signals = recentSignals || [];
95
- const trades = recentTrades || [];
96
-
97
- const wins = trades.filter(t => t.pnl > 0).length;
98
- const losses = trades.filter(t => t.pnl < 0).length;
99
- const totalTrades = wins + losses;
100
- const winRate = totalTrades > 0 ? (wins / totalTrades) * 100 : 0;
101
-
102
- const totalPnL = trades.reduce((sum, t) => sum + (t.pnl || 0), 0);
103
- const avgWin = wins > 0 ? trades.filter(t => t.pnl > 0).reduce((s, t) => s + t.pnl, 0) / wins : 0;
104
- const avgLoss = losses > 0 ? Math.abs(trades.filter(t => t.pnl < 0).reduce((s, t) => s + t.pnl, 0) / losses) : 0;
105
-
106
- return {
107
- recentSignals: signals.length,
108
- totalTrades,
109
- wins,
110
- losses,
111
- winRate: Math.round(winRate),
112
- totalPnL: Math.round(totalPnL * 100) / 100,
113
- avgWin: Math.round(avgWin * 100) / 100,
114
- avgLoss: Math.round(avgLoss * 100) / 100,
115
- profitFactor: avgLoss > 0 ? Math.round((avgWin / avgLoss) * 100) / 100 : 0
116
- };
117
- };
118
-
119
- /**
120
- * Build current position info
121
- */
122
- const buildPositionInfo = (position, currentPrice) => {
123
- if (!position || position.quantity === 0) {
124
- return { hasPosition: false, quantity: 0 };
125
- }
126
-
127
- const qty = position.quantity;
128
- const entryPrice = position.averagePrice || position.entryPrice;
129
- const unrealizedPnL = position.profitAndLoss || 0;
130
- const side = qty > 0 ? 'long' : 'short';
131
-
132
- return {
133
- hasPosition: true,
134
- side,
135
- quantity: Math.abs(qty),
136
- entryPrice,
137
- currentPrice,
138
- unrealizedPnL: Math.round(unrealizedPnL * 100) / 100,
139
- ticksInProfit: entryPrice && currentPrice
140
- ? Math.round((side === 'long' ? currentPrice - entryPrice : entryPrice - currentPrice) * 4)
141
- : 0
142
- };
143
- };
144
-
145
- /**
146
- * Build complete market context for AI analysis
147
- */
148
- const buildMarketContext = ({
149
- symbolId,
150
- signal,
151
- recentTicks = [],
152
- recentSignals = [],
153
- recentTrades = [],
154
- domData = null,
155
- position = null,
156
- stats = {},
157
- config = {}
158
- }) => {
159
- const symbol = getSymbol(symbolId);
160
- const session = getCurrentSession();
161
- const sessionCheck = isGoodSessionForSymbol(symbolId);
162
- const orderFlow = buildOrderFlowSummary(recentTicks);
163
- const dom = buildDOMSummary(domData);
164
- const history = buildTradeHistory(recentSignals, recentTrades);
165
- const positionInfo = buildPositionInfo(position, orderFlow.lastPrice);
166
-
167
- return {
168
- timestamp: new Date().toISOString(),
169
-
170
- // Symbol info
171
- symbol: {
172
- id: symbolId,
173
- name: symbol?.name || symbolId,
174
- tickSize: symbol?.tickSize || 0.25,
175
- tickValue: symbol?.tickValue || 12.50,
176
- characteristics: symbol?.characteristics || {},
177
- correlations: symbol?.correlations || {}
178
- },
179
-
180
- // Current session
181
- session: {
182
- name: session.name,
183
- description: session.description,
184
- isGoodTime: sessionCheck.good,
185
- sessionNote: sessionCheck.reason
186
- },
187
-
188
- // The signal to analyze
189
- signal: {
190
- direction: signal.direction,
191
- entry: signal.entry,
192
- stopLoss: signal.stopLoss,
193
- takeProfit: signal.takeProfit,
194
- confidence: signal.confidence,
195
- pattern: signal.pattern || 'unknown',
196
- timestamp: signal.timestamp || Date.now()
197
- },
198
-
199
- // Market data
200
- orderFlow,
201
- dom,
202
-
203
- // Position and history
204
- position: positionInfo,
205
- history,
206
-
207
- // Session stats
208
- sessionStats: {
209
- pnl: stats.pnl || 0,
210
- trades: stats.trades || 0,
211
- wins: stats.wins || 0,
212
- losses: stats.losses || 0,
213
- target: config.dailyTarget || stats.target || 500,
214
- maxRisk: config.maxRisk || stats.risk || 300,
215
- progressToTarget: stats.pnl && config.dailyTarget
216
- ? Math.round((stats.pnl / config.dailyTarget) * 100)
217
- : 0
218
- }
219
- };
220
- };
221
-
222
- /**
223
- * Format context as a string for AI prompt
224
- */
225
- const formatContextForPrompt = (context) => {
226
- return `
227
- ## MARKET CONTEXT
228
-
229
- **Symbol**: ${context.symbol.name} (${context.symbol.id})
230
- **Tick Size**: ${context.symbol.tickSize} | **Tick Value**: $${context.symbol.tickValue}
231
- **Session**: ${context.session.description} ${context.session.isGoodTime ? '✓' : '⚠'}
232
-
233
- ### SIGNAL TO ANALYZE
234
- - Direction: ${context.signal.direction.toUpperCase()}
235
- - Entry: ${context.signal.entry}
236
- - Stop Loss: ${context.signal.stopLoss}
237
- - Take Profit: ${context.signal.takeProfit}
238
- - Strategy Confidence: ${Math.round(context.signal.confidence * 100)}%
239
-
240
- ### ORDER FLOW (Last ${context.orderFlow.tickCount || 0} ticks)
241
- - Delta: ${context.orderFlow.delta || 0} (${context.orderFlow.deltaPercent || 0}%)
242
- - Buy Volume: ${context.orderFlow.buyVolume || 0}
243
- - Sell Volume: ${context.orderFlow.sellVolume || 0}
244
- - Trend: ${context.orderFlow.trend || 'unknown'}
245
- - Range: ${context.orderFlow.range?.toFixed(2) || 'N/A'}
246
-
247
- ### DOM ANALYSIS
248
- ${context.dom.available
249
- ? `- Spread: ${context.dom.spread?.toFixed(2) || 'N/A'}
250
- - Bid Size: ${context.dom.totalBidSize} | Ask Size: ${context.dom.totalAskSize}
251
- - Imbalance Ratio: ${context.dom.imbalanceRatio}x
252
- - Dominant Side: ${context.dom.dominantSide}`
253
- : '- DOM data not available'}
254
-
255
- ### POSITION
256
- ${context.position.hasPosition
257
- ? `- ${context.position.side.toUpperCase()} ${context.position.quantity}x @ ${context.position.entryPrice}
258
- - Unrealized P&L: $${context.position.unrealizedPnL}`
259
- : '- No open position'}
260
-
261
- ### SESSION PERFORMANCE
262
- - P&L: $${context.sessionStats.pnl} / $${context.sessionStats.target} target
263
- - Trades: ${context.sessionStats.trades} (W: ${context.sessionStats.wins} / L: ${context.sessionStats.losses})
264
- - Progress: ${context.sessionStats.progressToTarget}%
265
- `;
266
- };
267
-
268
- module.exports = {
269
- buildMarketContext,
270
- formatContextForPrompt,
271
- buildDOMSummary,
272
- buildOrderFlowSummary,
273
- buildTradeHistory,
274
- buildPositionInfo
275
- };