hedgequantx 2.5.41 → 2.5.43

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedgequantx",
3
- "version": "2.5.41",
3
+ "version": "2.5.43",
4
4
  "description": "HedgeQuantX - Prop Futures Trading CLI",
5
5
  "main": "src/app.js",
6
6
  "bin": {
@@ -481,7 +481,7 @@ const showStats = async (service) => {
481
481
 
482
482
  drawBoxFooter(boxWidth);
483
483
 
484
- // ========== AI BEHAVIOR DIAGRAM ==========
484
+ // ========== AI BEHAVIOR DIAGRAM (HORIZONTAL BARS) ==========
485
485
  const behaviorData = StrategySupervisor.getBehaviorHistory(100);
486
486
  const learningStats = StrategySupervisor.getLearningStats();
487
487
 
@@ -511,28 +511,10 @@ const showStats = async (service) => {
511
511
  PAUSE: Math.round((behaviorCounts.PAUSE / total) * 100)
512
512
  };
513
513
 
514
- // Bar chart configuration
515
- const labelWidth = 12;
516
- const percentWidth = 6;
517
- const barMaxWidth = behaviorInnerWidth - labelWidth - percentWidth - 6;
518
-
519
514
  // Get current behavior
520
515
  const currentValue = behaviorData.values.length > 0 ? behaviorData.values[behaviorData.values.length - 1] : 2;
521
516
  const currentAction = valueToAction[Math.round(currentValue)] || 'NORMAL';
522
517
 
523
- // Draw vertical bar chart (bars going up)
524
- const chartHeight = 8;
525
- const barWidth = Math.floor((barMaxWidth - 12) / 4); // 4 bars with spacing
526
-
527
- // Calculate bar heights (max height = chartHeight)
528
- const maxPercent = Math.max(...Object.values(percentages), 1);
529
- const barHeights = {
530
- AGGRESSIVE: Math.round((percentages.AGGRESSIVE / 100) * chartHeight),
531
- NORMAL: Math.round((percentages.NORMAL / 100) * chartHeight),
532
- CAUTIOUS: Math.round((percentages.CAUTIOUS / 100) * chartHeight),
533
- PAUSE: Math.round((percentages.PAUSE / 100) * chartHeight)
534
- };
535
-
536
518
  // Colors for each behavior
537
519
  const barColors = {
538
520
  AGGRESSIVE: chalk.green,
@@ -541,70 +523,50 @@ const showStats = async (service) => {
541
523
  PAUSE: chalk.red
542
524
  };
543
525
 
544
- // Draw bars from top to bottom
526
+ // Horizontal bar chart configuration
545
527
  const barLabels = ['AGGRESSIVE', 'NORMAL', 'CAUTIOUS', 'PAUSE'];
546
528
  const shortLabels = ['AGR', 'NOR', 'CAU', 'PAU'];
529
+ const labelWidth = 6; // "AGR " etc.
530
+ const pctWidth = 6; // "100% "
531
+ const spacing = 4; // spaces between bars
532
+
533
+ // Calculate bar width for each category (total 4 bars with spacing)
534
+ const availableWidth = behaviorInnerWidth - (labelWidth * 4) - (pctWidth * 4) - (spacing * 3) - 4;
535
+ const maxBarWidth = Math.floor(availableWidth / 4);
536
+
537
+ // Calculate bar widths based on percentage (max 100% = maxBarWidth)
538
+ const barWidths = {
539
+ AGGRESSIVE: Math.round((percentages.AGGRESSIVE / 100) * maxBarWidth),
540
+ NORMAL: Math.round((percentages.NORMAL / 100) * maxBarWidth),
541
+ CAUTIOUS: Math.round((percentages.CAUTIOUS / 100) * maxBarWidth),
542
+ PAUSE: Math.round((percentages.PAUSE / 100) * maxBarWidth)
543
+ };
547
544
 
548
- // Calculate left padding to center the chart
549
- const totalBarWidth = (barWidth * 4) + 9; // 4 bars + 3 spaces of 3 chars
550
- const leftPad = Math.floor((behaviorInnerWidth - totalBarWidth - 4) / 2);
551
-
552
- for (let row = chartHeight; row >= 1; row--) {
553
- let line = ' '.repeat(leftPad);
545
+ // Draw horizontal bars (each bar is a row)
546
+ for (let i = 0; i < 4; i++) {
547
+ const label = barLabels[i];
548
+ const shortLabel = shortLabels[i];
549
+ const pct = percentages[label];
550
+ const barWidth = barWidths[label];
551
+ const color = barColors[label];
552
+ const isCurrent = label === currentAction;
554
553
 
555
- for (let i = 0; i < 4; i++) {
556
- const label = barLabels[i];
557
- const height = barHeights[label];
558
- const color = barColors[label];
559
- const isCurrent = label === currentAction;
560
-
561
- if (row <= height) {
562
- // Draw filled bar
563
- const block = isCurrent ? '█' : '▓';
564
- line += color(block.repeat(barWidth));
565
- } else {
566
- // Empty space
567
- line += ' '.repeat(barWidth);
568
- }
569
-
570
- if (i < 3) line += ' '; // Space between bars
571
- }
554
+ // Build the bar
555
+ const block = isCurrent ? '█' : '▓';
556
+ const bar = barWidth > 0 ? color(block.repeat(barWidth)) : '';
557
+ const emptySpace = ' '.repeat(Math.max(0, maxBarWidth - barWidth));
572
558
 
559
+ // Format: " AGR ████████████████ 100% "
560
+ const labelPart = ' ' + color(shortLabel.padEnd(labelWidth));
561
+ const barPart = bar + emptySpace;
562
+ const pctPart = chalk.white((pct + '%').padStart(pctWidth));
563
+
564
+ let line = labelPart + barPart + pctPart;
573
565
  const lineLen = line.replace(/\x1b\[[0-9;]*m/g, '').length;
574
566
  line += ' '.repeat(Math.max(0, behaviorInnerWidth - lineLen));
575
567
  console.log(chalk.cyan('\u2551') + line + chalk.cyan('\u2551'));
576
568
  }
577
569
 
578
- // Draw baseline
579
- let baseLine = ' '.repeat(leftPad) + '─'.repeat(totalBarWidth);
580
- const baseLen = baseLine.length;
581
- baseLine += ' '.repeat(Math.max(0, behaviorInnerWidth - baseLen));
582
- console.log(chalk.cyan('\u2551') + chalk.white(baseLine) + chalk.cyan('\u2551'));
583
-
584
- // Draw labels
585
- let labelLine = ' '.repeat(leftPad);
586
- for (let i = 0; i < 4; i++) {
587
- const lbl = shortLabels[i];
588
- const pad = Math.floor((barWidth - lbl.length) / 2);
589
- labelLine += ' '.repeat(pad) + barColors[barLabels[i]](lbl) + ' '.repeat(barWidth - pad - lbl.length);
590
- if (i < 3) labelLine += ' ';
591
- }
592
- const lblLen = labelLine.replace(/\x1b\[[0-9;]*m/g, '').length;
593
- labelLine += ' '.repeat(Math.max(0, behaviorInnerWidth - lblLen));
594
- console.log(chalk.cyan('\u2551') + labelLine + chalk.cyan('\u2551'));
595
-
596
- // Draw percentages
597
- let pctLine = ' '.repeat(leftPad);
598
- for (let i = 0; i < 4; i++) {
599
- const pct = percentages[barLabels[i]] + '%';
600
- const pad = Math.floor((barWidth - pct.length) / 2);
601
- pctLine += ' '.repeat(pad) + chalk.white(pct) + ' '.repeat(barWidth - pad - pct.length);
602
- if (i < 3) pctLine += ' ';
603
- }
604
- const pctLen = pctLine.replace(/\x1b\[[0-9;]*m/g, '').length;
605
- pctLine += ' '.repeat(Math.max(0, behaviorInnerWidth - pctLen));
606
- console.log(chalk.cyan('\u2551') + pctLine + chalk.cyan('\u2551'));
607
-
608
570
  // Empty line
609
571
  console.log(chalk.cyan('\u2551') + ' '.repeat(behaviorInnerWidth) + chalk.cyan('\u2551'));
610
572
 
@@ -6,6 +6,7 @@
6
6
  const { getProviders, getProvider } = require('./providers');
7
7
  const { storage } = require('../session');
8
8
  const AISupervisor = require('./supervisor');
9
+ const StrategySupervisor = require('./strategy-supervisor');
9
10
 
10
11
  // In-memory cache of connections
11
12
  let connectionsCache = null;
@@ -228,10 +229,17 @@ const addAgent = async (providerId, optionId, credentials, model = null, customN
228
229
 
229
230
  saveAISettings(aiSettings);
230
231
 
231
- // Agent is ready - supervision will start when algo trading begins
232
- // Supervision requires a real service connection and account
232
+ // Get the full agent object
233
233
  const agent = getAgent(agentId);
234
234
 
235
+ // Notify StrategySupervisor if algo is running
236
+ // This ensures new agents are immediately connected to live trading
237
+ try {
238
+ StrategySupervisor.addAgent(agent);
239
+ } catch (e) {
240
+ // Supervisor might not be active - that's OK
241
+ }
242
+
235
243
  return agent;
236
244
  };
237
245
 
@@ -253,6 +261,13 @@ const removeAgent = (agentId) => {
253
261
  // Stop AI supervision for this agent
254
262
  AISupervisor.stop(agentId);
255
263
 
264
+ // Notify StrategySupervisor to remove agent from live trading
265
+ try {
266
+ StrategySupervisor.removeAgent(agentId);
267
+ } catch (e) {
268
+ // Supervisor might not be active - that's OK
269
+ }
270
+
256
271
  // If removed agent was active, set new active
257
272
  if (aiSettings.activeAgentId === agentId) {
258
273
  aiSettings.activeAgentId = agents.length > 0 ? agents[0].id : null;
@@ -289,6 +304,13 @@ const disconnectAll = () => {
289
304
  // Stop all AI supervision sessions
290
305
  AISupervisor.stopAll();
291
306
 
307
+ // Refresh StrategySupervisor to clear agents
308
+ try {
309
+ StrategySupervisor.refreshAgents();
310
+ } catch (e) {
311
+ // Supervisor might not be active
312
+ }
313
+
292
314
  saveAISettings({ agents: [] });
293
315
  };
294
316
 
@@ -609,6 +609,11 @@ const initialize = (strategy, agents, service, accountId) => {
609
609
  }
610
610
  }, 10000);
611
611
 
612
+ // Start agent sync interval - ensures new agents are picked up dynamically
613
+ if (!supervisorState.agentSyncInterval) {
614
+ supervisorState.agentSyncInterval = setInterval(syncAgents, 5000);
615
+ }
616
+
612
617
  return {
613
618
  success: true,
614
619
  agents: agents.length,
@@ -616,6 +621,108 @@ const initialize = (strategy, agents, service, accountId) => {
616
621
  };
617
622
  };
618
623
 
624
+ /**
625
+ * Sync agents with AI service
626
+ * Called periodically to pick up newly added/removed agents
627
+ * Ensures all agents are always connected to the supervisor
628
+ */
629
+ const syncAgents = () => {
630
+ if (!supervisorState.active) return;
631
+
632
+ try {
633
+ // Dynamic import to avoid circular dependency
634
+ const aiService = require('./index');
635
+ const currentAgents = aiService.getAgents();
636
+
637
+ if (!currentAgents) return;
638
+
639
+ const currentIds = new Set(currentAgents.map(a => a.id));
640
+ const supervisorIds = new Set(supervisorState.agents.map(a => a.id));
641
+
642
+ // Check for new agents
643
+ const newAgents = currentAgents.filter(a => !supervisorIds.has(a.id));
644
+
645
+ // Check for removed agents
646
+ const removedIds = [...supervisorIds].filter(id => !currentIds.has(id));
647
+
648
+ // Add new agents
649
+ if (newAgents.length > 0) {
650
+ supervisorState.agents = [...supervisorState.agents, ...newAgents];
651
+ // Log would go here if we had UI access
652
+ }
653
+
654
+ // Remove agents that were disconnected
655
+ if (removedIds.length > 0) {
656
+ supervisorState.agents = supervisorState.agents.filter(a => !removedIds.includes(a.id));
657
+ }
658
+
659
+ // Update mode based on current agent count
660
+ supervisorState.mode = supervisorState.agents.length >= 2 ? 'CONSENSUS' : 'INDIVIDUAL';
661
+
662
+ } catch (e) {
663
+ // Silent fail - aiService might not be ready
664
+ }
665
+ };
666
+
667
+ /**
668
+ * Force refresh agents from AI service
669
+ * Call this when you know agents have changed
670
+ */
671
+ const refreshAgents = () => {
672
+ syncAgents();
673
+ return {
674
+ agents: supervisorState.agents.length,
675
+ mode: supervisorState.agents.length >= 2 ? 'CONSENSUS' : 'INDIVIDUAL'
676
+ };
677
+ };
678
+
679
+ /**
680
+ * Add a single agent to the supervisor (called when agent is added)
681
+ */
682
+ const addAgent = (agent) => {
683
+ if (!supervisorState.active) return false;
684
+
685
+ // Check if already exists
686
+ if (supervisorState.agents.some(a => a.id === agent.id)) {
687
+ return false;
688
+ }
689
+
690
+ supervisorState.agents.push(agent);
691
+ supervisorState.mode = supervisorState.agents.length >= 2 ? 'CONSENSUS' : 'INDIVIDUAL';
692
+
693
+ return true;
694
+ };
695
+
696
+ /**
697
+ * Remove an agent from the supervisor (called when agent is removed)
698
+ */
699
+ const removeAgent = (agentId) => {
700
+ if (!supervisorState.active) return false;
701
+
702
+ const index = supervisorState.agents.findIndex(a => a.id === agentId);
703
+ if (index === -1) return false;
704
+
705
+ supervisorState.agents.splice(index, 1);
706
+ supervisorState.mode = supervisorState.agents.length >= 2 ? 'CONSENSUS' : 'INDIVIDUAL';
707
+
708
+ return true;
709
+ };
710
+
711
+ /**
712
+ * Get current agent count and mode
713
+ */
714
+ const getAgentInfo = () => {
715
+ return {
716
+ count: supervisorState.agents.length,
717
+ mode: supervisorState.agents.length >= 2 ? 'CONSENSUS' : 'INDIVIDUAL',
718
+ agents: supervisorState.agents.map(a => ({
719
+ id: a.id,
720
+ name: a.name,
721
+ provider: a.providerId
722
+ }))
723
+ };
724
+ };
725
+
619
726
  /**
620
727
  * Stop supervisor and save learned data
621
728
  */
@@ -625,6 +732,12 @@ const stop = () => {
625
732
  analysisInterval = null;
626
733
  }
627
734
 
735
+ // Stop agent sync
736
+ if (supervisorState.agentSyncInterval) {
737
+ clearInterval(supervisorState.agentSyncInterval);
738
+ supervisorState.agentSyncInterval = null;
739
+ }
740
+
628
741
  // Save all learned data before stopping
629
742
  const saved = saveLearningData();
630
743
 
@@ -1921,18 +2034,34 @@ const clearLearningData = () => {
1921
2034
  };
1922
2035
 
1923
2036
  module.exports = {
2037
+ // Core lifecycle
1924
2038
  initialize,
1925
2039
  stop,
2040
+
2041
+ // Data feeds (from algo)
1926
2042
  feedTick,
1927
2043
  feedSignal,
1928
2044
  feedTradeResult,
2045
+
2046
+ // Trading decisions
1929
2047
  getCurrentAdvice,
1930
2048
  shouldTrade,
2049
+
2050
+ // Agent management (dynamic sync)
2051
+ syncAgents,
2052
+ refreshAgents,
2053
+ addAgent,
2054
+ removeAgent,
2055
+ getAgentInfo,
2056
+
2057
+ // Status & stats
1931
2058
  getStatus,
1932
2059
  analyzeAndOptimize,
1933
2060
  getBehaviorHistory,
1934
2061
  getLearningStats,
1935
2062
  getLifetimeStats,
2063
+
2064
+ // Data management
1936
2065
  clearLearningData,
1937
2066
  loadLearningData
1938
2067
  };