hedgequantx 2.7.84 → 2.7.86

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.7.84",
3
+ "version": "2.7.86",
4
4
  "description": "HedgeQuantX - Prop Futures Trading CLI",
5
5
  "main": "src/app.js",
6
6
  "bin": {
@@ -6,6 +6,7 @@
6
6
 
7
7
  const chalk = require('chalk');
8
8
  const { centerText, visibleLength } = require('../ui');
9
+ const cliproxy = require('../services/cliproxy');
9
10
 
10
11
  /**
11
12
  * Draw a 2-column row with perfect alignment
@@ -94,6 +95,9 @@ const drawProvidersTable = (providers, config, boxWidth) => {
94
95
  const W = boxWidth - 2;
95
96
  const colWidth = Math.floor(W / 2);
96
97
 
98
+ // Get connected providers (have auth files)
99
+ const connected = cliproxy.getConnectedProviders();
100
+
97
101
  // New rectangle (banner is always closed)
98
102
  console.log(chalk.cyan('╔' + '═'.repeat(W) + '╗'));
99
103
  console.log(chalk.cyan('║') + chalk.yellow.bold(centerText('AI AGENTS CONFIGURATION', W)) + chalk.cyan('║'));
@@ -104,9 +108,9 @@ const drawProvidersTable = (providers, config, boxWidth) => {
104
108
  // Find max name length across ALL providers for consistent alignment
105
109
  const maxNameLen = Math.max(...providers.map(p => p.name.length));
106
110
 
107
- // Fixed format: "[N] NAME" where N is always same width, NAME padded to maxNameLen
108
- // Total content width = 3 ([N]) + 1 (space) + maxNameLen + 2 (possible " ●")
109
- const contentWidth = 3 + 1 + maxNameLen + 2;
111
+ // Fixed format: "[N] NAME" where is yellow if connected (has auth file)
112
+ // Total content width = 2 (● ) + 3 ([N]) + 1 (space) + maxNameLen
113
+ const contentWidth = 2 + 3 + 1 + maxNameLen;
110
114
  const leftPad = Math.floor((colWidth - contentWidth) / 2);
111
115
  const rightPad = Math.floor(((W - colWidth) - contentWidth) / 2);
112
116
 
@@ -118,12 +122,13 @@ const drawProvidersTable = (providers, config, boxWidth) => {
118
122
  let leftCol = '';
119
123
  if (leftP) {
120
124
  const num = row + 1;
121
- const status = config.providers[leftP.id]?.active ? chalk.green(' ●') : ' ';
122
- const statusRaw = config.providers[leftP.id]?.active ? ' ●' : ' ';
125
+ // Show yellow dot if provider has auth file (connected via OAuth)
126
+ const isConnected = connected[leftP.id] || config.providers[leftP.id]?.active;
127
+ const status = isConnected ? chalk.yellow('● ') : ' ';
123
128
  const name = leftP.provider ? leftP.provider.name : leftP.name;
124
129
  const namePadded = name.toUpperCase().padEnd(maxNameLen);
125
- const content = chalk.cyan(`[${num}]`) + ' ' + chalk[leftP.color](namePadded) + status;
126
- const contentLen = 3 + 1 + maxNameLen + 2;
130
+ const content = status + chalk.cyan(`[${num}]`) + ' ' + chalk[leftP.color](namePadded);
131
+ const contentLen = 2 + 3 + 1 + maxNameLen;
127
132
  const padR = colWidth - leftPad - contentLen;
128
133
  leftCol = ' '.repeat(leftPad) + content + ' '.repeat(Math.max(0, padR));
129
134
  } else {
@@ -135,11 +140,13 @@ const drawProvidersTable = (providers, config, boxWidth) => {
135
140
  const rightColWidth = W - colWidth;
136
141
  if (rightP) {
137
142
  const num = row + rows + 1;
138
- const status = config.providers[rightP.id]?.active ? chalk.green(' ●') : ' ';
143
+ // Show yellow dot if provider has auth file (connected via OAuth)
144
+ const isConnected = connected[rightP.id] || config.providers[rightP.id]?.active;
145
+ const status = isConnected ? chalk.yellow('● ') : ' ';
139
146
  const name = rightP.provider ? rightP.provider.name : rightP.name;
140
147
  const namePadded = name.toUpperCase().padEnd(maxNameLen);
141
- const content = chalk.cyan(`[${num}]`) + ' ' + chalk[rightP.color](namePadded) + status;
142
- const contentLen = 3 + 1 + maxNameLen + 2;
148
+ const content = status + chalk.cyan(`[${num}]`) + ' ' + chalk[rightP.color](namePadded);
149
+ const contentLen = 2 + 3 + 1 + maxNameLen;
143
150
  const padR2 = rightColWidth - rightPad - contentLen;
144
151
  rightCol = ' '.repeat(rightPad) + content + ' '.repeat(Math.max(0, padR2));
145
152
  } else {
@@ -95,11 +95,8 @@ const selectModel = async (provider, apiKey) => {
95
95
  return selectModelFromList(provider, result.models, boxWidth);
96
96
  };
97
97
 
98
- /** Deactivate all providers and activate one */
98
+ /** Activate a provider (multiple providers can be active at the same time) */
99
99
  const activateProvider = (config, providerId, data) => {
100
- Object.keys(config.providers).forEach(id => {
101
- if (config.providers[id]) config.providers[id].active = false;
102
- });
103
100
  if (!config.providers[providerId]) config.providers[providerId] = {};
104
101
  Object.assign(config.providers[providerId], data, { active: true, configuredAt: new Date().toISOString() });
105
102
  };
@@ -30,21 +30,28 @@ const algoTradingMenu = async (service) => {
30
30
  console.log(chalk.cyan('║') + chalk.magenta.bold(centerText('ALGO-TRADING', W)) + chalk.cyan('║'));
31
31
  console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
32
32
 
33
- // Options centered in 2 columns
33
+ // 3 columns layout
34
34
  const col1 = '[1] ONE ACCOUNT';
35
35
  const col2 = '[2] COPY TRADING';
36
- const colWidth = Math.floor(W / 2);
36
+ const col3 = '[3] CUSTOM STRATEGY';
37
+ const colWidth = Math.floor(W / 3);
38
+ const lastColWidth = W - 2 * colWidth;
39
+
37
40
  const pad1 = Math.floor((colWidth - col1.length) / 2);
38
41
  const pad2 = Math.floor((colWidth - col2.length) / 2);
39
- const line = ' '.repeat(pad1) + chalk.cyan(col1) + ' '.repeat(colWidth - col1.length - pad1) +
40
- ' '.repeat(pad2) + chalk.cyan(col2) + ' '.repeat(colWidth - col2.length - pad2);
41
- console.log(chalk.cyan('║') + line + chalk.cyan('║'));
42
+ const pad3 = Math.floor((lastColWidth - col3.length) / 2);
43
+
44
+ const col1Str = ' '.repeat(pad1) + chalk.cyan(col1) + ' '.repeat(colWidth - col1.length - pad1);
45
+ const col2Str = ' '.repeat(pad2) + chalk.yellow(col2) + ' '.repeat(colWidth - col2.length - pad2);
46
+ const col3Str = ' '.repeat(pad3) + chalk.green(col3) + ' '.repeat(lastColWidth - col3.length - pad3);
47
+
48
+ console.log(chalk.cyan('║') + col1Str + col2Str + col3Str + chalk.cyan('║'));
42
49
 
43
50
  console.log(chalk.cyan('╠' + '─'.repeat(W) + '╣'));
44
51
  console.log(chalk.cyan('║') + chalk.red(centerText('[B] BACK', W)) + chalk.cyan('║'));
45
52
  console.log(chalk.cyan('╚' + '═'.repeat(W) + '╝'));
46
53
 
47
- const input = await prompts.textInput(chalk.cyan('SELECT (1/2/B): '));
54
+ const input = await prompts.textInput(chalk.cyan('SELECT (1/2/3/B): '));
48
55
  const choice = (input || '').toLowerCase().trim();
49
56
 
50
57
  log.debug('Algo mode selected', { choice });
@@ -62,6 +69,10 @@ const algoTradingMenu = async (service) => {
62
69
  log.info('Starting Copy Trading mode');
63
70
  await copyTradingMenu();
64
71
  break;
72
+ case '3':
73
+ log.info('Starting Custom Strategy mode');
74
+ await customStrategyMenu(service);
75
+ break;
65
76
  default:
66
77
  console.log(chalk.red(' INVALID OPTION'));
67
78
  await new Promise(r => setTimeout(r, 1000));
@@ -76,4 +87,27 @@ const algoTradingMenu = async (service) => {
76
87
  }
77
88
  };
78
89
 
90
+ /**
91
+ * Custom Strategy Menu - AI-powered strategy creation
92
+ */
93
+ const customStrategyMenu = async (service) => {
94
+ console.clear();
95
+ displayBanner();
96
+
97
+ const boxWidth = getLogoWidth();
98
+ const W = boxWidth - 2;
99
+
100
+ console.log(chalk.cyan('╔' + '═'.repeat(W) + '╗'));
101
+ console.log(chalk.cyan('║') + chalk.green.bold(centerText('CUSTOM STRATEGY', W)) + chalk.cyan('║'));
102
+ console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
103
+ console.log(chalk.cyan('║') + centerText('Create your own trading strategy with AI assistance', W) + chalk.cyan('║'));
104
+ console.log(chalk.cyan('╠' + '─'.repeat(W) + '╣'));
105
+ console.log(chalk.cyan('║') + chalk.gray(centerText('Coming soon...', W)) + chalk.cyan('║'));
106
+ console.log(chalk.cyan('╠' + '─'.repeat(W) + '╣'));
107
+ console.log(chalk.cyan('║') + chalk.red(centerText('[B] BACK', W)) + chalk.cyan('║'));
108
+ console.log(chalk.cyan('╚' + '═'.repeat(W) + '╝'));
109
+
110
+ await prompts.waitForEnter();
111
+ };
112
+
79
113
  module.exports = { algoTradingMenu };
@@ -150,6 +150,29 @@ const fetchProviderModels = async (providerId) => {
150
150
  };
151
151
  };
152
152
 
153
+ /**
154
+ * Check which providers have auth files (are connected)
155
+ * @returns {Object} { anthropic: true/false, google: true/false, openai: true/false, qwen: true/false }
156
+ */
157
+ const getConnectedProviders = () => {
158
+ const fs = require('fs');
159
+ const connected = { anthropic: false, google: false, openai: false, qwen: false };
160
+
161
+ try {
162
+ if (!fs.existsSync(AUTH_DIR)) return connected;
163
+
164
+ const files = fs.readdirSync(AUTH_DIR);
165
+ for (const file of files) {
166
+ if (file.startsWith('claude-') && file.endsWith('.json')) connected.anthropic = true;
167
+ if (file.startsWith('gemini-') && file.endsWith('.json')) connected.google = true;
168
+ if (file.startsWith('codex-') && file.endsWith('.json')) connected.openai = true;
169
+ if (file.startsWith('qwen-') && file.endsWith('.json')) connected.qwen = true;
170
+ }
171
+ } catch (e) { /* ignore */ }
172
+
173
+ return connected;
174
+ };
175
+
153
176
  /**
154
177
  * Chat completion request
155
178
  * @param {string} model - Model ID
@@ -197,5 +220,6 @@ module.exports = {
197
220
  fetchLocal,
198
221
  fetchModels,
199
222
  fetchProviderModels,
223
+ getConnectedProviders,
200
224
  chatCompletion
201
225
  };