hedgequantx 2.6.158 → 2.6.159
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
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Algo Trading Configuration
|
|
3
|
+
* Symbol selection and algo parameters configuration
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const chalk = require('chalk');
|
|
9
|
+
const ora = require('ora');
|
|
10
|
+
const { prompts } = require('../../utils');
|
|
11
|
+
const aiService = require('../../services/ai');
|
|
12
|
+
const { MAX_MULTI_SYMBOLS } = require('./algo-utils');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Symbol selection - sorted with popular indices first
|
|
16
|
+
* Supports selecting multiple symbols (up to MAX_MULTI_SYMBOLS)
|
|
17
|
+
* @param {Object} service - Trading service
|
|
18
|
+
* @param {Object} account - Trading account
|
|
19
|
+
* @param {boolean} allowMultiple - Allow selecting multiple symbols
|
|
20
|
+
* @returns {Object|Array|null} Single contract or array of contracts
|
|
21
|
+
*/
|
|
22
|
+
const selectSymbol = async (service, account, allowMultiple = false) => {
|
|
23
|
+
const spinner = ora({ text: 'LOADING SYMBOLS...', color: 'yellow' }).start();
|
|
24
|
+
|
|
25
|
+
const contractsResult = await service.getContracts();
|
|
26
|
+
if (!contractsResult.success || !contractsResult.contracts?.length) {
|
|
27
|
+
spinner.fail('FAILED TO LOAD CONTRACTS');
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let contracts = contractsResult.contracts;
|
|
32
|
+
|
|
33
|
+
// Sort: Popular indices first (ES, NQ, MES, MNQ, RTY, YM, etc.)
|
|
34
|
+
const popularPrefixes = ['ES', 'NQ', 'MES', 'MNQ', 'M2K', 'RTY', 'YM', 'MYM', 'NKD', 'GC', 'SI', 'CL'];
|
|
35
|
+
|
|
36
|
+
contracts.sort((a, b) => {
|
|
37
|
+
const nameA = a.name || '';
|
|
38
|
+
const nameB = b.name || '';
|
|
39
|
+
|
|
40
|
+
const idxA = popularPrefixes.findIndex(p => nameA.startsWith(p));
|
|
41
|
+
const idxB = popularPrefixes.findIndex(p => nameB.startsWith(p));
|
|
42
|
+
|
|
43
|
+
if (idxA !== -1 && idxB !== -1) return idxA - idxB;
|
|
44
|
+
if (idxA !== -1) return -1;
|
|
45
|
+
if (idxB !== -1) return 1;
|
|
46
|
+
return nameA.localeCompare(nameB);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
spinner.succeed(`FOUND ${contracts.length} CONTRACTS`);
|
|
50
|
+
|
|
51
|
+
const options = contracts.map(c => {
|
|
52
|
+
const name = c.name || c.symbol || c.baseSymbol;
|
|
53
|
+
const desc = c.description || '';
|
|
54
|
+
const label = desc ? `${name} - ${desc}` : name;
|
|
55
|
+
return { label, value: c };
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
options.push({ label: chalk.gray('< BACK'), value: 'back' });
|
|
59
|
+
|
|
60
|
+
// Single symbol mode
|
|
61
|
+
if (!allowMultiple) {
|
|
62
|
+
const contract = await prompts.selectOption(chalk.yellow('SELECT SYMBOL:'), options);
|
|
63
|
+
return contract === 'back' || contract === null ? null : contract;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Multi-symbol selection mode
|
|
67
|
+
const selectedContracts = [];
|
|
68
|
+
|
|
69
|
+
while (selectedContracts.length < MAX_MULTI_SYMBOLS) {
|
|
70
|
+
console.log();
|
|
71
|
+
|
|
72
|
+
if (selectedContracts.length > 0) {
|
|
73
|
+
console.log(chalk.cyan(` SELECTED (${selectedContracts.length}/${MAX_MULTI_SYMBOLS}):`));
|
|
74
|
+
selectedContracts.forEach((c, i) => {
|
|
75
|
+
const name = c.name || c.symbol;
|
|
76
|
+
console.log(chalk.green(` ${i + 1}. ${name} x${c.qty}`));
|
|
77
|
+
});
|
|
78
|
+
console.log();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const availableOptions = options.filter(opt => {
|
|
82
|
+
if (opt.value === 'back') return true;
|
|
83
|
+
const optId = opt.value.id || opt.value.symbol || opt.value.name;
|
|
84
|
+
return !selectedContracts.some(sc => (sc.id || sc.symbol || sc.name) === optId);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
if (selectedContracts.length > 0) {
|
|
88
|
+
availableOptions.unshift({
|
|
89
|
+
label: chalk.green(`✓ DONE - Start trading ${selectedContracts.length} symbol${selectedContracts.length > 1 ? 's' : ''}`),
|
|
90
|
+
value: 'done'
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const promptText = selectedContracts.length === 0
|
|
95
|
+
? chalk.yellow(`SELECT SYMBOL (1/${MAX_MULTI_SYMBOLS}):`)
|
|
96
|
+
: chalk.yellow(`ADD SYMBOL (${selectedContracts.length + 1}/${MAX_MULTI_SYMBOLS}) OR DONE:`);
|
|
97
|
+
|
|
98
|
+
const choice = await prompts.selectOption(promptText, availableOptions);
|
|
99
|
+
|
|
100
|
+
if (choice === null || choice === 'back') {
|
|
101
|
+
if (selectedContracts.length === 0) return null;
|
|
102
|
+
selectedContracts.pop();
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (choice === 'done') break;
|
|
107
|
+
|
|
108
|
+
const symbolName = choice.name || choice.symbol;
|
|
109
|
+
const qty = await prompts.numberInput(`CONTRACTS FOR ${symbolName}:`, 1, 1, 10);
|
|
110
|
+
if (qty === null) continue;
|
|
111
|
+
|
|
112
|
+
choice.qty = qty;
|
|
113
|
+
selectedContracts.push(choice);
|
|
114
|
+
|
|
115
|
+
if (selectedContracts.length >= MAX_MULTI_SYMBOLS) {
|
|
116
|
+
console.log(chalk.yellow(` Maximum ${MAX_MULTI_SYMBOLS} symbols reached`));
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return selectedContracts.length > 0 ? selectedContracts : null;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Configure algo parameters
|
|
126
|
+
* @param {Object} account - Trading account
|
|
127
|
+
* @param {Object|Array} contractOrContracts - Single contract or array of contracts
|
|
128
|
+
*/
|
|
129
|
+
const configureAlgo = async (account, contractOrContracts) => {
|
|
130
|
+
const contractList = Array.isArray(contractOrContracts) ? contractOrContracts : [contractOrContracts];
|
|
131
|
+
const isMultiSymbol = contractList.length > 1;
|
|
132
|
+
|
|
133
|
+
console.log();
|
|
134
|
+
console.log(chalk.cyan(' CONFIGURE ALGO PARAMETERS'));
|
|
135
|
+
|
|
136
|
+
if (isMultiSymbol) {
|
|
137
|
+
console.log(chalk.white(` Trading ${contractList.length} symbols:`));
|
|
138
|
+
contractList.forEach((c, i) => {
|
|
139
|
+
const name = c.name || c.symbol;
|
|
140
|
+
const qty = c.qty || 1;
|
|
141
|
+
console.log(chalk.yellow(` ${i + 1}. ${name} x${qty}`));
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
console.log();
|
|
145
|
+
|
|
146
|
+
let contracts = 1;
|
|
147
|
+
if (!isMultiSymbol) {
|
|
148
|
+
contracts = await prompts.numberInput('NUMBER OF CONTRACTS:', 1, 1, 10);
|
|
149
|
+
if (contracts === null) return null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const dailyTarget = await prompts.numberInput('DAILY TARGET ($):', 1000, 1, 10000);
|
|
153
|
+
if (dailyTarget === null) return null;
|
|
154
|
+
|
|
155
|
+
const maxRisk = await prompts.numberInput('MAX RISK ($):', 500, 1, 5000);
|
|
156
|
+
if (maxRisk === null) return null;
|
|
157
|
+
|
|
158
|
+
const showName = await prompts.confirmPrompt('SHOW ACCOUNT NAME?', false);
|
|
159
|
+
if (showName === null) return null;
|
|
160
|
+
|
|
161
|
+
const aiAgents = aiService.getAgents();
|
|
162
|
+
let enableAI = false;
|
|
163
|
+
|
|
164
|
+
if (aiAgents.length > 0) {
|
|
165
|
+
console.log();
|
|
166
|
+
console.log(chalk.magenta(` ${aiAgents.length} AI AGENT(S) AVAILABLE:`));
|
|
167
|
+
aiAgents.forEach((agent, i) => {
|
|
168
|
+
const modelInfo = agent.model ? chalk.gray(` (${agent.model})`) : '';
|
|
169
|
+
console.log(chalk.white(` ${i + 1}. ${agent.name}${modelInfo}`));
|
|
170
|
+
});
|
|
171
|
+
console.log();
|
|
172
|
+
|
|
173
|
+
enableAI = await prompts.confirmPrompt('ACTIVATE AI MODELS?', true);
|
|
174
|
+
if (enableAI === null) return null;
|
|
175
|
+
|
|
176
|
+
if (enableAI) {
|
|
177
|
+
const mode = aiAgents.length >= 2 ? 'CONSENSUS' : 'INDIVIDUAL';
|
|
178
|
+
console.log(chalk.green(` AI MODE: ${mode} (${aiAgents.length} agent${aiAgents.length > 1 ? 's' : ''})`));
|
|
179
|
+
} else {
|
|
180
|
+
console.log(chalk.gray(' AI AGENTS DISABLED FOR THIS SESSION'));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
console.log();
|
|
185
|
+
const confirm = await prompts.confirmPrompt('START ALGO TRADING?', true);
|
|
186
|
+
if (!confirm) return null;
|
|
187
|
+
|
|
188
|
+
const initSpinner = ora({ text: 'INITIALIZING ALGO TRADING...', color: 'yellow' }).start();
|
|
189
|
+
await new Promise(r => setTimeout(r, 500));
|
|
190
|
+
initSpinner.succeed('LAUNCHING ALGO...');
|
|
191
|
+
|
|
192
|
+
return { contracts, dailyTarget, maxRisk, showName, enableAI };
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
module.exports = { selectSymbol, configureAlgo };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Algo Trading Utilities
|
|
3
|
+
* Shared functions and constants for algo trading
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Format price to avoid floating point errors
|
|
10
|
+
* @param {number} price - Raw price
|
|
11
|
+
* @param {number} tickSize - Tick size (default 0.25)
|
|
12
|
+
* @returns {string} - Formatted price string
|
|
13
|
+
*/
|
|
14
|
+
const formatPrice = (price, tickSize = 0.25) => {
|
|
15
|
+
if (price === null || price === undefined || isNaN(price)) return '--';
|
|
16
|
+
const rounded = Math.round(price / tickSize) * tickSize;
|
|
17
|
+
const decimals = tickSize < 1 ? Math.max(0, -Math.floor(Math.log10(tickSize))) : 0;
|
|
18
|
+
return rounded.toFixed(decimals);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Check if service supports fast path (Rithmic direct)
|
|
23
|
+
* @param {Object} service - Trading service
|
|
24
|
+
* @returns {boolean}
|
|
25
|
+
*/
|
|
26
|
+
const isRithmicFastPath = (service) => {
|
|
27
|
+
return typeof service.fastEntry === 'function' &&
|
|
28
|
+
typeof service.fastExit === 'function' &&
|
|
29
|
+
service.orderConn?.isConnected;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// Maximum symbols for multi-symbol trading
|
|
33
|
+
const MAX_MULTI_SYMBOLS = 5;
|
|
34
|
+
|
|
35
|
+
// Use HFT tick-based strategy for Rithmic
|
|
36
|
+
const USE_HFT_STRATEGY = true;
|
|
37
|
+
|
|
38
|
+
// Timeout for async operations
|
|
39
|
+
const TIMEOUT_MS = 5000;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Wrap promise with timeout
|
|
43
|
+
*/
|
|
44
|
+
const withTimeout = (promise, ms) => {
|
|
45
|
+
return Promise.race([
|
|
46
|
+
promise,
|
|
47
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), ms))
|
|
48
|
+
]);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
module.exports = {
|
|
52
|
+
formatPrice,
|
|
53
|
+
isRithmicFastPath,
|
|
54
|
+
MAX_MULTI_SYMBOLS,
|
|
55
|
+
USE_HFT_STRATEGY,
|
|
56
|
+
TIMEOUT_MS,
|
|
57
|
+
withTimeout
|
|
58
|
+
};
|
|
@@ -25,39 +25,13 @@ const { algoLogger } = require('./logger');
|
|
|
25
25
|
const { recoveryMath } = require('../../services/strategy/recovery-math');
|
|
26
26
|
const { sessionHistory, SessionHistory } = require('../../services/session-history');
|
|
27
27
|
|
|
28
|
-
// Use HFT tick-based strategy for Rithmic (fast path), M1 for ProjectX
|
|
29
|
-
const USE_HFT_STRATEGY = true;
|
|
30
|
-
|
|
31
28
|
// AI Strategy Supervisor - observes, learns, and optimizes the strategy
|
|
32
29
|
const aiService = require('../../services/ai');
|
|
33
30
|
const StrategySupervisor = require('../../services/ai/strategy-supervisor');
|
|
34
31
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
* @param {number} price - Raw price
|
|
39
|
-
* @param {number} tickSize - Tick size (default 0.25)
|
|
40
|
-
* @returns {string} - Formatted price string
|
|
41
|
-
*/
|
|
42
|
-
const formatPrice = (price, tickSize = 0.25) => {
|
|
43
|
-
if (price === null || price === undefined || isNaN(price)) return '--';
|
|
44
|
-
// Round to nearest tick, then format
|
|
45
|
-
const rounded = Math.round(price / tickSize) * tickSize;
|
|
46
|
-
// Determine decimal places from tick size
|
|
47
|
-
const decimals = tickSize < 1 ? Math.max(0, -Math.floor(Math.log10(tickSize))) : 0;
|
|
48
|
-
return rounded.toFixed(decimals);
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Check if service supports fast path (Rithmic direct)
|
|
53
|
-
* @param {Object} service - Trading service
|
|
54
|
-
* @returns {boolean}
|
|
55
|
-
*/
|
|
56
|
-
const isRithmicFastPath = (service) => {
|
|
57
|
-
return typeof service.fastEntry === 'function' &&
|
|
58
|
-
typeof service.fastExit === 'function' &&
|
|
59
|
-
service.orderConn?.isConnected;
|
|
60
|
-
};
|
|
32
|
+
// Shared utilities
|
|
33
|
+
const { formatPrice, isRithmicFastPath, MAX_MULTI_SYMBOLS, USE_HFT_STRATEGY } = require('./algo-utils');
|
|
34
|
+
const { selectSymbol, configureAlgo } = require('./algo-config');
|
|
61
35
|
|
|
62
36
|
/**
|
|
63
37
|
* One Account Menu
|
|
@@ -133,208 +107,6 @@ const oneAccountMenu = async (service) => {
|
|
|
133
107
|
}
|
|
134
108
|
};
|
|
135
109
|
|
|
136
|
-
// Maximum symbols for multi-symbol trading
|
|
137
|
-
const MAX_MULTI_SYMBOLS = 5;
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Symbol selection - sorted with popular indices first
|
|
141
|
-
* Supports selecting multiple symbols (up to MAX_MULTI_SYMBOLS)
|
|
142
|
-
* @param {Object} service - Trading service
|
|
143
|
-
* @param {Object} account - Trading account
|
|
144
|
-
* @param {boolean} allowMultiple - Allow selecting multiple symbols
|
|
145
|
-
* @returns {Object|Array|null} Single contract or array of contracts
|
|
146
|
-
*/
|
|
147
|
-
const selectSymbol = async (service, account, allowMultiple = false) => {
|
|
148
|
-
const spinner = ora({ text: 'LOADING SYMBOLS...', color: 'yellow' }).start();
|
|
149
|
-
|
|
150
|
-
const contractsResult = await service.getContracts();
|
|
151
|
-
if (!contractsResult.success || !contractsResult.contracts?.length) {
|
|
152
|
-
spinner.fail('FAILED TO LOAD CONTRACTS');
|
|
153
|
-
return null;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
let contracts = contractsResult.contracts;
|
|
157
|
-
|
|
158
|
-
// Sort: Popular indices first (ES, NQ, MES, MNQ, RTY, YM, etc.)
|
|
159
|
-
const popularPrefixes = ['ES', 'NQ', 'MES', 'MNQ', 'M2K', 'RTY', 'YM', 'MYM', 'NKD', 'GC', 'SI', 'CL'];
|
|
160
|
-
|
|
161
|
-
contracts.sort((a, b) => {
|
|
162
|
-
const nameA = a.name || '';
|
|
163
|
-
const nameB = b.name || '';
|
|
164
|
-
|
|
165
|
-
// Check if names start with popular prefixes
|
|
166
|
-
const idxA = popularPrefixes.findIndex(p => nameA.startsWith(p));
|
|
167
|
-
const idxB = popularPrefixes.findIndex(p => nameB.startsWith(p));
|
|
168
|
-
|
|
169
|
-
// Both are popular - sort by popularity order
|
|
170
|
-
if (idxA !== -1 && idxB !== -1) return idxA - idxB;
|
|
171
|
-
// Only A is popular - A first
|
|
172
|
-
if (idxA !== -1) return -1;
|
|
173
|
-
// Only B is popular - B first
|
|
174
|
-
if (idxB !== -1) return 1;
|
|
175
|
-
// Neither - alphabetical
|
|
176
|
-
return nameA.localeCompare(nameB);
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
spinner.succeed(`FOUND ${contracts.length} CONTRACTS`);
|
|
180
|
-
|
|
181
|
-
// Display sorted contracts from API (uniform format: NAME - DESCRIPTION)
|
|
182
|
-
const options = contracts.map(c => {
|
|
183
|
-
const name = c.name || c.symbol || c.baseSymbol;
|
|
184
|
-
const desc = c.description || '';
|
|
185
|
-
const label = desc ? `${name} - ${desc}` : name;
|
|
186
|
-
return { label, value: c };
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
options.push({ label: chalk.gray('< BACK'), value: 'back' });
|
|
190
|
-
|
|
191
|
-
// Single symbol mode
|
|
192
|
-
if (!allowMultiple) {
|
|
193
|
-
const contract = await prompts.selectOption(chalk.yellow('SELECT SYMBOL:'), options);
|
|
194
|
-
return contract === 'back' || contract === null ? null : contract;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Multi-symbol selection mode
|
|
198
|
-
// Each contract will have a 'qty' property for number of contracts
|
|
199
|
-
const selectedContracts = [];
|
|
200
|
-
|
|
201
|
-
while (selectedContracts.length < MAX_MULTI_SYMBOLS) {
|
|
202
|
-
console.log();
|
|
203
|
-
|
|
204
|
-
// Show already selected symbols with quantities
|
|
205
|
-
if (selectedContracts.length > 0) {
|
|
206
|
-
console.log(chalk.cyan(` SELECTED (${selectedContracts.length}/${MAX_MULTI_SYMBOLS}):`));
|
|
207
|
-
selectedContracts.forEach((c, i) => {
|
|
208
|
-
const name = c.name || c.symbol;
|
|
209
|
-
console.log(chalk.green(` ${i + 1}. ${name} x${c.qty}`));
|
|
210
|
-
});
|
|
211
|
-
console.log();
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Filter out already selected
|
|
215
|
-
const availableOptions = options.filter(opt => {
|
|
216
|
-
if (opt.value === 'back') return true;
|
|
217
|
-
const optId = opt.value.id || opt.value.symbol || opt.value.name;
|
|
218
|
-
return !selectedContracts.some(sc => (sc.id || sc.symbol || sc.name) === optId);
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
// Add DONE option if at least 1 selected
|
|
222
|
-
if (selectedContracts.length > 0) {
|
|
223
|
-
availableOptions.unshift({
|
|
224
|
-
label: chalk.green(`✓ DONE - Start trading ${selectedContracts.length} symbol${selectedContracts.length > 1 ? 's' : ''}`),
|
|
225
|
-
value: 'done'
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const promptText = selectedContracts.length === 0
|
|
230
|
-
? chalk.yellow(`SELECT SYMBOL (1/${MAX_MULTI_SYMBOLS}):`)
|
|
231
|
-
: chalk.yellow(`ADD SYMBOL (${selectedContracts.length + 1}/${MAX_MULTI_SYMBOLS}) OR DONE:`);
|
|
232
|
-
|
|
233
|
-
const choice = await prompts.selectOption(promptText, availableOptions);
|
|
234
|
-
|
|
235
|
-
if (choice === null || choice === 'back') {
|
|
236
|
-
if (selectedContracts.length === 0) return null;
|
|
237
|
-
selectedContracts.pop(); // Remove last
|
|
238
|
-
continue;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
if (choice === 'done') break;
|
|
242
|
-
|
|
243
|
-
// Ask for number of contracts for this symbol
|
|
244
|
-
const symbolName = choice.name || choice.symbol;
|
|
245
|
-
const qty = await prompts.numberInput(`CONTRACTS FOR ${symbolName}:`, 1, 1, 10);
|
|
246
|
-
if (qty === null) continue; // User cancelled, don't add symbol
|
|
247
|
-
|
|
248
|
-
// Add qty to contract object
|
|
249
|
-
choice.qty = qty;
|
|
250
|
-
selectedContracts.push(choice);
|
|
251
|
-
|
|
252
|
-
if (selectedContracts.length >= MAX_MULTI_SYMBOLS) {
|
|
253
|
-
console.log(chalk.yellow(` Maximum ${MAX_MULTI_SYMBOLS} symbols reached`));
|
|
254
|
-
break;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
return selectedContracts.length > 0 ? selectedContracts : null;
|
|
259
|
-
};
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Configure algo
|
|
263
|
-
* @param {Object} account - Trading account
|
|
264
|
-
* @param {Object|Array} contractOrContracts - Single contract or array of contracts
|
|
265
|
-
*/
|
|
266
|
-
const configureAlgo = async (account, contractOrContracts) => {
|
|
267
|
-
const contractList = Array.isArray(contractOrContracts) ? contractOrContracts : [contractOrContracts];
|
|
268
|
-
const isMultiSymbol = contractList.length > 1;
|
|
269
|
-
|
|
270
|
-
console.log();
|
|
271
|
-
console.log(chalk.cyan(' CONFIGURE ALGO PARAMETERS'));
|
|
272
|
-
|
|
273
|
-
// Show selected symbols with quantities (multi-symbol mode)
|
|
274
|
-
if (isMultiSymbol) {
|
|
275
|
-
console.log(chalk.white(` Trading ${contractList.length} symbols:`));
|
|
276
|
-
contractList.forEach((c, i) => {
|
|
277
|
-
const name = c.name || c.symbol;
|
|
278
|
-
const qty = c.qty || 1;
|
|
279
|
-
console.log(chalk.yellow(` ${i + 1}. ${name} x${qty}`));
|
|
280
|
-
});
|
|
281
|
-
}
|
|
282
|
-
console.log();
|
|
283
|
-
|
|
284
|
-
// Only ask for contracts in single-symbol mode
|
|
285
|
-
// In multi-symbol mode, qty is already set per symbol
|
|
286
|
-
let contracts = 1;
|
|
287
|
-
if (!isMultiSymbol) {
|
|
288
|
-
contracts = await prompts.numberInput('NUMBER OF CONTRACTS:', 1, 1, 10);
|
|
289
|
-
if (contracts === null) return null;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
const dailyTarget = await prompts.numberInput('DAILY TARGET ($):', 1000, 1, 10000);
|
|
293
|
-
if (dailyTarget === null) return null;
|
|
294
|
-
|
|
295
|
-
const maxRisk = await prompts.numberInput('MAX RISK ($):', 500, 1, 5000);
|
|
296
|
-
if (maxRisk === null) return null;
|
|
297
|
-
|
|
298
|
-
const showName = await prompts.confirmPrompt('SHOW ACCOUNT NAME?', false);
|
|
299
|
-
if (showName === null) return null;
|
|
300
|
-
|
|
301
|
-
// Check if AI agents are available
|
|
302
|
-
const aiAgents = aiService.getAgents();
|
|
303
|
-
let enableAI = false;
|
|
304
|
-
|
|
305
|
-
if (aiAgents.length > 0) {
|
|
306
|
-
// Show available agents
|
|
307
|
-
console.log();
|
|
308
|
-
console.log(chalk.magenta(` ${aiAgents.length} AI AGENT(S) AVAILABLE:`));
|
|
309
|
-
aiAgents.forEach((agent, i) => {
|
|
310
|
-
const modelInfo = agent.model ? chalk.gray(` (${agent.model})`) : '';
|
|
311
|
-
console.log(chalk.white(` ${i + 1}. ${agent.name}${modelInfo}`));
|
|
312
|
-
});
|
|
313
|
-
console.log();
|
|
314
|
-
|
|
315
|
-
enableAI = await prompts.confirmPrompt('ACTIVATE AI MODELS?', true);
|
|
316
|
-
if (enableAI === null) return null;
|
|
317
|
-
|
|
318
|
-
if (enableAI) {
|
|
319
|
-
const mode = aiAgents.length >= 2 ? 'CONSENSUS' : 'INDIVIDUAL';
|
|
320
|
-
console.log(chalk.green(` AI MODE: ${mode} (${aiAgents.length} agent${aiAgents.length > 1 ? 's' : ''})`));
|
|
321
|
-
} else {
|
|
322
|
-
console.log(chalk.gray(' AI AGENTS DISABLED FOR THIS SESSION'));
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
console.log();
|
|
327
|
-
const confirm = await prompts.confirmPrompt('START ALGO TRADING?', true);
|
|
328
|
-
if (!confirm) return null;
|
|
329
|
-
|
|
330
|
-
// Show spinner while initializing
|
|
331
|
-
const initSpinner = ora({ text: 'INITIALIZING ALGO TRADING...', color: 'yellow' }).start();
|
|
332
|
-
await new Promise(r => setTimeout(r, 500));
|
|
333
|
-
initSpinner.succeed('LAUNCHING ALGO...');
|
|
334
|
-
|
|
335
|
-
return { contracts, dailyTarget, maxRisk, showName, enableAI };
|
|
336
|
-
};
|
|
337
|
-
|
|
338
110
|
/**
|
|
339
111
|
* Launch algo trading - HQX Ultra Scalping Strategy
|
|
340
112
|
* Real-time market data + Strategy signals + Auto order execution
|