hedgequantx 2.7.87 → 2.7.88
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 +1 -1
- package/src/pages/algo/custom-strategy.js +394 -0
- package/src/pages/algo/index.js +1 -23
- package/src/ui/index.js +1 -1
package/package.json
CHANGED
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom Strategy - AI-powered modular strategy builder
|
|
3
|
+
* Each strategy is a folder with modular components
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const chalk = require('chalk');
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const os = require('os');
|
|
10
|
+
const ora = require('ora');
|
|
11
|
+
|
|
12
|
+
const { getLogoWidth, centerText, displayBanner } = require('../../ui');
|
|
13
|
+
const { prompts } = require('../../utils');
|
|
14
|
+
const { getActiveProvider } = require('../ai-agents');
|
|
15
|
+
const cliproxy = require('../../services/cliproxy');
|
|
16
|
+
|
|
17
|
+
// Base strategies directory
|
|
18
|
+
const STRATEGIES_DIR = path.join(os.homedir(), '.hqx', 'strategies');
|
|
19
|
+
|
|
20
|
+
/** Ensure strategies directory exists */
|
|
21
|
+
const ensureStrategiesDir = () => {
|
|
22
|
+
if (!fs.existsSync(STRATEGIES_DIR)) fs.mkdirSync(STRATEGIES_DIR, { recursive: true });
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** Load all saved strategies (folders) */
|
|
26
|
+
const loadStrategies = () => {
|
|
27
|
+
ensureStrategiesDir();
|
|
28
|
+
try {
|
|
29
|
+
const items = fs.readdirSync(STRATEGIES_DIR, { withFileTypes: true });
|
|
30
|
+
return items.filter(i => i.isDirectory()).map(dir => {
|
|
31
|
+
const configPath = path.join(STRATEGIES_DIR, dir.name, 'config.json');
|
|
32
|
+
if (fs.existsSync(configPath)) {
|
|
33
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
34
|
+
return { folder: dir.name, path: path.join(STRATEGIES_DIR, dir.name), ...config };
|
|
35
|
+
}
|
|
36
|
+
return { folder: dir.name, path: path.join(STRATEGIES_DIR, dir.name), name: dir.name };
|
|
37
|
+
});
|
|
38
|
+
} catch (e) { return []; }
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/** Create strategy folder structure */
|
|
42
|
+
const createStrategyFolder = (name) => {
|
|
43
|
+
ensureStrategiesDir();
|
|
44
|
+
const folderName = name.toLowerCase().replace(/[^a-z0-9]/g, '-').replace(/-+/g, '-');
|
|
45
|
+
const strategyPath = path.join(STRATEGIES_DIR, folderName);
|
|
46
|
+
|
|
47
|
+
if (fs.existsSync(strategyPath)) {
|
|
48
|
+
return { success: false, error: 'Strategy folder already exists', path: null };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
fs.mkdirSync(strategyPath, { recursive: true });
|
|
52
|
+
return { success: true, path: strategyPath, folder: folderName };
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/** Save strategy module */
|
|
56
|
+
const saveModule = (strategyPath, moduleName, content) => {
|
|
57
|
+
const filePath = path.join(strategyPath, moduleName);
|
|
58
|
+
fs.writeFileSync(filePath, typeof content === 'string' ? content : JSON.stringify(content, null, 2));
|
|
59
|
+
return filePath;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/** Delete strategy folder */
|
|
63
|
+
const deleteStrategy = (strategyPath) => {
|
|
64
|
+
if (fs.existsSync(strategyPath)) {
|
|
65
|
+
fs.rmSync(strategyPath, { recursive: true, force: true });
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/** Custom Strategy Menu */
|
|
72
|
+
const customStrategyMenu = async (service) => {
|
|
73
|
+
while (true) {
|
|
74
|
+
console.clear();
|
|
75
|
+
displayBanner();
|
|
76
|
+
|
|
77
|
+
const boxWidth = getLogoWidth();
|
|
78
|
+
const W = boxWidth - 2;
|
|
79
|
+
const aiProvider = getActiveProvider();
|
|
80
|
+
const strategies = loadStrategies();
|
|
81
|
+
|
|
82
|
+
console.log(chalk.cyan('╔' + '═'.repeat(W) + '╗'));
|
|
83
|
+
console.log(chalk.cyan('║') + chalk.green.bold(centerText('CUSTOM STRATEGY BUILDER', W)) + chalk.cyan('║'));
|
|
84
|
+
console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
|
|
85
|
+
|
|
86
|
+
// AI Status
|
|
87
|
+
if (aiProvider) {
|
|
88
|
+
const status = `AI: ${aiProvider.name} (${aiProvider.modelName || 'default'})`;
|
|
89
|
+
console.log(chalk.cyan('║') + chalk.green(centerText('● ' + status, W)) + chalk.cyan('║'));
|
|
90
|
+
} else {
|
|
91
|
+
console.log(chalk.cyan('║') + chalk.red(centerText('○ NO AI AGENT CONNECTED', W)) + chalk.cyan('║'));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
|
|
95
|
+
|
|
96
|
+
// Options
|
|
97
|
+
const col1 = '[1] CREATE NEW';
|
|
98
|
+
const col2 = `[2] MY STRATEGIES (${strategies.length})`;
|
|
99
|
+
const colWidth = Math.floor(W / 2);
|
|
100
|
+
const pad1 = Math.floor((colWidth - col1.length) / 2);
|
|
101
|
+
const pad2 = Math.floor((W - colWidth - col2.length) / 2);
|
|
102
|
+
console.log(chalk.cyan('║') +
|
|
103
|
+
' '.repeat(pad1) + chalk.yellow(col1) + ' '.repeat(colWidth - col1.length - pad1) +
|
|
104
|
+
' '.repeat(pad2) + chalk.cyan(col2) + ' '.repeat(W - colWidth - col2.length - pad2) +
|
|
105
|
+
chalk.cyan('║'));
|
|
106
|
+
|
|
107
|
+
console.log(chalk.cyan('╠' + '─'.repeat(W) + '╣'));
|
|
108
|
+
console.log(chalk.cyan('║') + chalk.red(centerText('[B] BACK', W)) + chalk.cyan('║'));
|
|
109
|
+
console.log(chalk.cyan('╚' + '═'.repeat(W) + '╝'));
|
|
110
|
+
|
|
111
|
+
const input = await prompts.textInput(chalk.cyan('SELECT (1/2/B): '));
|
|
112
|
+
const choice = (input || '').toLowerCase().trim();
|
|
113
|
+
|
|
114
|
+
if (choice === 'b' || choice === '') return;
|
|
115
|
+
|
|
116
|
+
if (choice === '1') {
|
|
117
|
+
if (!aiProvider) {
|
|
118
|
+
console.log(chalk.red('\n Connect an AI Agent first (AI Agents menu)'));
|
|
119
|
+
await prompts.waitForEnter();
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
await createStrategyWizard(aiProvider);
|
|
123
|
+
} else if (choice === '2') {
|
|
124
|
+
await myStrategiesMenu(strategies, service);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/** AI Wizard to create modular strategy */
|
|
130
|
+
const createStrategyWizard = async (aiProvider) => {
|
|
131
|
+
console.clear();
|
|
132
|
+
displayBanner();
|
|
133
|
+
|
|
134
|
+
const boxWidth = getLogoWidth();
|
|
135
|
+
const W = boxWidth - 2;
|
|
136
|
+
|
|
137
|
+
console.log(chalk.cyan('╔' + '═'.repeat(W) + '╗'));
|
|
138
|
+
console.log(chalk.cyan('║') + chalk.green.bold(centerText('CREATE STRATEGY WITH AI', W)) + chalk.cyan('║'));
|
|
139
|
+
console.log(chalk.cyan('╚' + '═'.repeat(W) + '╝'));
|
|
140
|
+
console.log();
|
|
141
|
+
|
|
142
|
+
// Step 1: Strategy name
|
|
143
|
+
console.log(chalk.yellow(' STEP 1: Name your strategy'));
|
|
144
|
+
const name = await prompts.textInput(chalk.cyan(' Strategy name: '));
|
|
145
|
+
if (!name || !name.trim()) {
|
|
146
|
+
console.log(chalk.red(' Cancelled'));
|
|
147
|
+
await prompts.waitForEnter();
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Create folder
|
|
152
|
+
const folder = createStrategyFolder(name.trim());
|
|
153
|
+
if (!folder.success) {
|
|
154
|
+
console.log(chalk.red(` Error: ${folder.error}`));
|
|
155
|
+
await prompts.waitForEnter();
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
console.log(chalk.green(` ✓ Created: ${folder.path}`));
|
|
160
|
+
console.log();
|
|
161
|
+
|
|
162
|
+
// Step 2: Chat with AI to build strategy
|
|
163
|
+
console.log(chalk.yellow(' STEP 2: Describe your strategy to the AI'));
|
|
164
|
+
console.log(chalk.gray(' Type your strategy idea in plain English.'));
|
|
165
|
+
console.log(chalk.gray(' The AI will help you build each module.'));
|
|
166
|
+
console.log(chalk.gray(' Type "done" when finished, "cancel" to abort.'));
|
|
167
|
+
console.log();
|
|
168
|
+
|
|
169
|
+
const systemPrompt = `You are an expert algo trading strategy builder for futures (ES, NQ, MES, MNQ, etc).
|
|
170
|
+
Help the user create a modular trading strategy. Build these components:
|
|
171
|
+
|
|
172
|
+
1. ENTRY CONDITIONS - When to open a position (long/short signals)
|
|
173
|
+
2. EXIT CONDITIONS - Take profit, stop loss, trailing stops
|
|
174
|
+
3. RISK MANAGEMENT - Position sizing, max loss, max positions
|
|
175
|
+
4. FILTERS - Market conditions when NOT to trade
|
|
176
|
+
|
|
177
|
+
Ask clarifying questions. Be concise. When ready, output each module.
|
|
178
|
+
|
|
179
|
+
For each module, output JavaScript code in this format:
|
|
180
|
+
\`\`\`javascript:entry.js
|
|
181
|
+
module.exports = {
|
|
182
|
+
checkLongEntry: (data) => { /* return true/false */ },
|
|
183
|
+
checkShortEntry: (data) => { /* return true/false */ }
|
|
184
|
+
};
|
|
185
|
+
\`\`\`
|
|
186
|
+
|
|
187
|
+
The 'data' object contains: { price, bid, ask, volume, atr, ema20, ema50, rsi, macd, vwap, high, low, open, close }`;
|
|
188
|
+
|
|
189
|
+
const messages = [{ role: 'system', content: systemPrompt }];
|
|
190
|
+
const modules = {};
|
|
191
|
+
|
|
192
|
+
console.log(chalk.green(' AI: ') + 'What kind of trading strategy do you want to create?');
|
|
193
|
+
console.log(chalk.gray(' Example: "A mean reversion strategy that buys when RSI < 30"'));
|
|
194
|
+
console.log();
|
|
195
|
+
|
|
196
|
+
while (true) {
|
|
197
|
+
const userInput = await prompts.textInput(chalk.yellow(' You: '));
|
|
198
|
+
|
|
199
|
+
if (!userInput) continue;
|
|
200
|
+
|
|
201
|
+
if (userInput.toLowerCase() === 'cancel') {
|
|
202
|
+
deleteStrategy(folder.path);
|
|
203
|
+
console.log(chalk.gray('\n Strategy cancelled and folder deleted.'));
|
|
204
|
+
await prompts.waitForEnter();
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (userInput.toLowerCase() === 'done') {
|
|
209
|
+
// Save config
|
|
210
|
+
saveModule(folder.path, 'config.json', {
|
|
211
|
+
name: name.trim(),
|
|
212
|
+
description: modules.description || '',
|
|
213
|
+
createdAt: new Date().toISOString(),
|
|
214
|
+
modules: Object.keys(modules).filter(k => k !== 'description')
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
console.log(chalk.green('\n ✓ Strategy saved!'));
|
|
218
|
+
console.log(chalk.cyan(` Location: ${folder.path}`));
|
|
219
|
+
console.log(chalk.gray(' Modules created:'));
|
|
220
|
+
for (const m of Object.keys(modules)) {
|
|
221
|
+
if (m !== 'description') console.log(chalk.gray(` - ${m}`));
|
|
222
|
+
}
|
|
223
|
+
await prompts.waitForEnter();
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
messages.push({ role: 'user', content: userInput });
|
|
228
|
+
|
|
229
|
+
const spinner = ora({ text: 'AI thinking...', color: 'yellow' }).start();
|
|
230
|
+
|
|
231
|
+
try {
|
|
232
|
+
const modelId = aiProvider.modelId || getDefaultModel(aiProvider.id);
|
|
233
|
+
const result = await cliproxy.chatCompletion(modelId, messages);
|
|
234
|
+
|
|
235
|
+
if (!result.success) {
|
|
236
|
+
spinner.fail(`AI Error: ${result.error}`);
|
|
237
|
+
messages.pop();
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const response = result.response?.choices?.[0]?.message?.content || '';
|
|
242
|
+
messages.push({ role: 'assistant', content: response });
|
|
243
|
+
|
|
244
|
+
spinner.stop();
|
|
245
|
+
console.log();
|
|
246
|
+
|
|
247
|
+
// Extract and save code modules
|
|
248
|
+
const codeBlocks = response.matchAll(/```javascript:(\w+\.js)\n([\s\S]*?)```/g);
|
|
249
|
+
for (const match of codeBlocks) {
|
|
250
|
+
const [, filename, code] = match;
|
|
251
|
+
saveModule(folder.path, filename, code.trim());
|
|
252
|
+
modules[filename] = true;
|
|
253
|
+
console.log(chalk.green(` ✓ Saved module: ${filename}`));
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Extract description if present
|
|
257
|
+
const descMatch = response.match(/description[:\s]*["']?([^"'\n]+)/i);
|
|
258
|
+
if (descMatch) modules.description = descMatch[1];
|
|
259
|
+
|
|
260
|
+
// Print AI response (without code blocks for cleaner output)
|
|
261
|
+
const cleanResponse = response.replace(/```[\s\S]*?```/g, '[code saved]');
|
|
262
|
+
console.log(chalk.green(' AI: ') + formatResponse(cleanResponse));
|
|
263
|
+
console.log();
|
|
264
|
+
|
|
265
|
+
} catch (e) {
|
|
266
|
+
spinner.fail(`Error: ${e.message}`);
|
|
267
|
+
messages.pop();
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
/** Get default model */
|
|
273
|
+
const getDefaultModel = (providerId) => {
|
|
274
|
+
const defaults = {
|
|
275
|
+
anthropic: 'claude-sonnet-4-20250514',
|
|
276
|
+
google: 'gemini-2.5-pro',
|
|
277
|
+
openai: 'gpt-4o'
|
|
278
|
+
};
|
|
279
|
+
return defaults[providerId] || 'claude-sonnet-4-20250514';
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
/** Format response for terminal */
|
|
283
|
+
const formatResponse = (text) => {
|
|
284
|
+
const lines = text.split('\n');
|
|
285
|
+
return lines.map((l, i) => i === 0 ? l : ' ' + l).join('\n');
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
/** My Strategies Menu */
|
|
289
|
+
const myStrategiesMenu = async (strategies, service) => {
|
|
290
|
+
while (true) {
|
|
291
|
+
console.clear();
|
|
292
|
+
displayBanner();
|
|
293
|
+
|
|
294
|
+
const boxWidth = getLogoWidth();
|
|
295
|
+
const W = boxWidth - 2;
|
|
296
|
+
|
|
297
|
+
console.log(chalk.cyan('╔' + '═'.repeat(W) + '╗'));
|
|
298
|
+
console.log(chalk.cyan('║') + chalk.yellow.bold(centerText('MY STRATEGIES', W)) + chalk.cyan('║'));
|
|
299
|
+
console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
|
|
300
|
+
|
|
301
|
+
if (strategies.length === 0) {
|
|
302
|
+
console.log(chalk.cyan('║') + chalk.gray(centerText('No strategies yet', W)) + chalk.cyan('║'));
|
|
303
|
+
} else {
|
|
304
|
+
for (let i = 0; i < strategies.length; i++) {
|
|
305
|
+
const s = strategies[i];
|
|
306
|
+
const num = `[${i + 1}]`.padEnd(4);
|
|
307
|
+
const sname = (s.name || s.folder).substring(0, 30).padEnd(32);
|
|
308
|
+
const modules = s.modules ? `${s.modules.length} modules` : '';
|
|
309
|
+
const line = `${num} ${sname} ${chalk.gray(modules)}`;
|
|
310
|
+
console.log(chalk.cyan('║') + ' ' + chalk.white(num) + chalk.cyan(sname) + chalk.gray(modules.padEnd(W - 38)) + chalk.cyan('║'));
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
console.log(chalk.cyan('╠' + '─'.repeat(W) + '╣'));
|
|
315
|
+
console.log(chalk.cyan('║') + chalk.red(centerText('[B] BACK', W)) + chalk.cyan('║'));
|
|
316
|
+
console.log(chalk.cyan('╚' + '═'.repeat(W) + '╝'));
|
|
317
|
+
|
|
318
|
+
if (strategies.length === 0) {
|
|
319
|
+
await prompts.waitForEnter();
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const input = await prompts.textInput(chalk.cyan(`SELECT (1-${strategies.length}/B): `));
|
|
324
|
+
const choice = (input || '').toLowerCase().trim();
|
|
325
|
+
|
|
326
|
+
if (choice === 'b' || choice === '') return;
|
|
327
|
+
|
|
328
|
+
const num = parseInt(choice);
|
|
329
|
+
if (!isNaN(num) && num >= 1 && num <= strategies.length) {
|
|
330
|
+
await strategyDetailMenu(strategies[num - 1], service);
|
|
331
|
+
strategies.length = 0;
|
|
332
|
+
strategies.push(...loadStrategies());
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
/** Strategy Detail Menu */
|
|
338
|
+
const strategyDetailMenu = async (strategy, service) => {
|
|
339
|
+
console.clear();
|
|
340
|
+
displayBanner();
|
|
341
|
+
|
|
342
|
+
const boxWidth = getLogoWidth();
|
|
343
|
+
const W = boxWidth - 2;
|
|
344
|
+
|
|
345
|
+
console.log(chalk.cyan('╔' + '═'.repeat(W) + '╗'));
|
|
346
|
+
console.log(chalk.cyan('║') + chalk.green.bold(centerText((strategy.name || strategy.folder).toUpperCase(), W)) + chalk.cyan('║'));
|
|
347
|
+
console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
|
|
348
|
+
|
|
349
|
+
// Show modules
|
|
350
|
+
const files = fs.readdirSync(strategy.path);
|
|
351
|
+
console.log(chalk.cyan('║') + chalk.gray(centerText(`Path: ${strategy.path}`, W)) + chalk.cyan('║'));
|
|
352
|
+
console.log(chalk.cyan('╠' + '─'.repeat(W) + '╣'));
|
|
353
|
+
console.log(chalk.cyan('║') + chalk.white(centerText('MODULES:', W)) + chalk.cyan('║'));
|
|
354
|
+
|
|
355
|
+
for (const f of files) {
|
|
356
|
+
const icon = f.endsWith('.js') ? '📄' : f.endsWith('.json') ? '⚙️' : '📁';
|
|
357
|
+
console.log(chalk.cyan('║') + centerText(`${icon} ${f}`, W) + chalk.cyan('║'));
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
|
|
361
|
+
|
|
362
|
+
// Options: Run, Edit with AI, Delete
|
|
363
|
+
const opts = ['[1] RUN', '[2] EDIT WITH AI', '[3] DELETE'];
|
|
364
|
+
const optLine = opts.join(' ');
|
|
365
|
+
console.log(chalk.cyan('║') + centerText(
|
|
366
|
+
chalk.green(opts[0]) + ' ' + chalk.yellow(opts[1]) + ' ' + chalk.red(opts[2]), W
|
|
367
|
+
) + chalk.cyan('║'));
|
|
368
|
+
|
|
369
|
+
console.log(chalk.cyan('╠' + '─'.repeat(W) + '╣'));
|
|
370
|
+
console.log(chalk.cyan('║') + chalk.red(centerText('[B] BACK', W)) + chalk.cyan('║'));
|
|
371
|
+
console.log(chalk.cyan('╚' + '═'.repeat(W) + '╝'));
|
|
372
|
+
|
|
373
|
+
const input = await prompts.textInput(chalk.cyan('SELECT (1/2/3/B): '));
|
|
374
|
+
const choice = (input || '').toLowerCase().trim();
|
|
375
|
+
|
|
376
|
+
if (choice === '1') {
|
|
377
|
+
console.log(chalk.yellow('\n Running custom strategy...'));
|
|
378
|
+
console.log(chalk.gray(' This will use your connected accounts and market data.'));
|
|
379
|
+
console.log(chalk.gray(' (Full execution coming soon)'));
|
|
380
|
+
await prompts.waitForEnter();
|
|
381
|
+
} else if (choice === '2') {
|
|
382
|
+
console.log(chalk.yellow('\n Edit with AI coming soon...'));
|
|
383
|
+
await prompts.waitForEnter();
|
|
384
|
+
} else if (choice === '3') {
|
|
385
|
+
const confirm = await prompts.confirmPrompt(`Delete "${strategy.name || strategy.folder}"?`, false);
|
|
386
|
+
if (confirm) {
|
|
387
|
+
deleteStrategy(strategy.path);
|
|
388
|
+
console.log(chalk.green('\n ✓ Strategy deleted'));
|
|
389
|
+
await prompts.waitForEnter();
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
module.exports = { customStrategyMenu, loadStrategies, createStrategyFolder, saveModule };
|
package/src/pages/algo/index.js
CHANGED
|
@@ -10,6 +10,7 @@ const log = logger.scope('AlgoMenu');
|
|
|
10
10
|
|
|
11
11
|
const { oneAccountMenu } = require('./one-account');
|
|
12
12
|
const { copyTradingMenu } = require('./copy-trading');
|
|
13
|
+
const { customStrategyMenu } = require('./custom-strategy');
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Algo Trading Menu
|
|
@@ -87,27 +88,4 @@ const algoTradingMenu = async (service) => {
|
|
|
87
88
|
}
|
|
88
89
|
};
|
|
89
90
|
|
|
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
|
-
|
|
113
91
|
module.exports = { algoTradingMenu };
|
package/src/ui/index.js
CHANGED
|
@@ -70,7 +70,7 @@ const displayBanner = () => {
|
|
|
70
70
|
|
|
71
71
|
console.log(chalk.cyan('╠' + '═'.repeat(innerWidth) + '╣'));
|
|
72
72
|
const tagline = isMobile ? `HQX V${version}` : `PROP FUTURES ALGO TRADING V${version}`;
|
|
73
|
-
console.log(chalk.cyan('║') + chalk.
|
|
73
|
+
console.log(chalk.cyan('║') + chalk.yellow(centerText(tagline, innerWidth)) + chalk.cyan('║'));
|
|
74
74
|
|
|
75
75
|
// ALWAYS close the banner
|
|
76
76
|
console.log(chalk.cyan('╚' + '═'.repeat(innerWidth) + '╝'));
|