hedgequantx 2.9.50 → 2.9.51
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/copy-trading.js +109 -0
- package/src/pages/algo/one-account.js +106 -30
- package/src/services/algo-config.js +102 -0
package/package.json
CHANGED
|
@@ -14,6 +14,7 @@ const { getActiveAgentCount, getSupervisionConfig, getActiveAgents } = require('
|
|
|
14
14
|
const { launchCopyTrading } = require('./copy-executor');
|
|
15
15
|
const { runPreflightCheck, formatPreflightResults, getPreflightSummary } = require('../../services/ai-supervision');
|
|
16
16
|
const { getAvailableStrategies } = require('../../lib/m');
|
|
17
|
+
const { getLastCopyTradingConfig, saveCopyTradingConfig } = require('../../services/algo-config');
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Copy Trading Menu
|
|
@@ -54,6 +55,98 @@ const copyTradingMenu = async () => {
|
|
|
54
55
|
|
|
55
56
|
spinner.succeed(`Found ${activeAccounts.length} active accounts`);
|
|
56
57
|
|
|
58
|
+
// Check for saved config
|
|
59
|
+
const lastConfig = getLastCopyTradingConfig();
|
|
60
|
+
|
|
61
|
+
if (lastConfig) {
|
|
62
|
+
// Try to find matching accounts
|
|
63
|
+
const matchingLead = activeAccounts.find(acc =>
|
|
64
|
+
acc.accountId === lastConfig.leadAccountId ||
|
|
65
|
+
acc.accountName === lastConfig.leadAccountName
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
if (matchingLead && lastConfig.followerAccountIds?.length > 0) {
|
|
69
|
+
const matchingFollowers = lastConfig.followerAccountIds
|
|
70
|
+
.map(id => activeAccounts.find(acc => acc.accountId === id || acc.accountName === id))
|
|
71
|
+
.filter(Boolean);
|
|
72
|
+
|
|
73
|
+
if (matchingFollowers.length > 0) {
|
|
74
|
+
console.log();
|
|
75
|
+
console.log(chalk.cyan(' Last configuration found:'));
|
|
76
|
+
console.log(chalk.gray(` Lead: ${lastConfig.leadAccountName} (${lastConfig.leadPropfirm})`));
|
|
77
|
+
console.log(chalk.gray(` Followers: ${matchingFollowers.length} account(s)`));
|
|
78
|
+
console.log(chalk.gray(` Symbol: ${lastConfig.symbol}`));
|
|
79
|
+
console.log(chalk.gray(` Strategy: ${lastConfig.strategyName}`));
|
|
80
|
+
console.log(chalk.gray(` Lead contracts: ${lastConfig.leadContracts} | Follower: ${lastConfig.followerContracts}`));
|
|
81
|
+
console.log(chalk.gray(` Target: $${lastConfig.dailyTarget} | Risk: $${lastConfig.maxRisk}`));
|
|
82
|
+
console.log();
|
|
83
|
+
|
|
84
|
+
const reuseConfig = await prompts.confirmPrompt('Use last configuration?', true);
|
|
85
|
+
|
|
86
|
+
if (reuseConfig) {
|
|
87
|
+
// Load contracts to find symbol
|
|
88
|
+
const leadService = matchingLead.service || connections.getServiceForAccount(matchingLead.accountId);
|
|
89
|
+
const contractsResult = await leadService.getContracts();
|
|
90
|
+
const contract = contractsResult.success
|
|
91
|
+
? contractsResult.contracts.find(c => c.symbol === lastConfig.symbol)
|
|
92
|
+
: null;
|
|
93
|
+
|
|
94
|
+
// Find strategy
|
|
95
|
+
const strategies = getAvailableStrategies();
|
|
96
|
+
const strategy = strategies.find(s => s.id === lastConfig.strategyId);
|
|
97
|
+
|
|
98
|
+
if (contract && strategy) {
|
|
99
|
+
console.log(chalk.green(' ✓ Configuration loaded'));
|
|
100
|
+
|
|
101
|
+
// Check for AI Supervision
|
|
102
|
+
const agentCount = getActiveAgentCount();
|
|
103
|
+
let supervisionConfig = null;
|
|
104
|
+
|
|
105
|
+
if (agentCount > 0) {
|
|
106
|
+
console.log();
|
|
107
|
+
console.log(chalk.cyan(` ${agentCount} AI Agent(s) available for supervision`));
|
|
108
|
+
const enableAI = await prompts.confirmPrompt('Enable AI Supervision?', true);
|
|
109
|
+
|
|
110
|
+
if (enableAI) {
|
|
111
|
+
const agents = getActiveAgents();
|
|
112
|
+
const preflightResults = await runPreflightCheck(agents);
|
|
113
|
+
const lines = formatPreflightResults(preflightResults, 60);
|
|
114
|
+
for (const line of lines) console.log(line);
|
|
115
|
+
const summary = getPreflightSummary(preflightResults);
|
|
116
|
+
console.log();
|
|
117
|
+
console.log(` ${summary.text}`);
|
|
118
|
+
|
|
119
|
+
if (!preflightResults.success) {
|
|
120
|
+
console.log(chalk.red(' Cannot start algo - fix agent connections first.'));
|
|
121
|
+
await prompts.waitForEnter();
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
supervisionConfig = getSupervisionConfig();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const confirm = await prompts.confirmPrompt('Start Copy Trading?', true);
|
|
129
|
+
if (!confirm) return;
|
|
130
|
+
|
|
131
|
+
await launchCopyTrading({
|
|
132
|
+
lead: { account: matchingLead, contracts: lastConfig.leadContracts },
|
|
133
|
+
followers: matchingFollowers.map(f => ({ account: f, contracts: lastConfig.followerContracts })),
|
|
134
|
+
contract,
|
|
135
|
+
strategy,
|
|
136
|
+
dailyTarget: lastConfig.dailyTarget,
|
|
137
|
+
maxRisk: lastConfig.maxRisk,
|
|
138
|
+
showNames: lastConfig.showNames,
|
|
139
|
+
supervisionConfig
|
|
140
|
+
});
|
|
141
|
+
return;
|
|
142
|
+
} else {
|
|
143
|
+
console.log(chalk.yellow(' Symbol or strategy no longer available, please reconfigure'));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
57
150
|
// Step 1: Select LEAD Account
|
|
58
151
|
console.log();
|
|
59
152
|
console.log(chalk.cyan.bold(' STEP 1: SELECT LEAD ACCOUNT'));
|
|
@@ -204,6 +297,22 @@ const copyTradingMenu = async () => {
|
|
|
204
297
|
const confirm = await prompts.confirmPrompt('Start Copy Trading?', true);
|
|
205
298
|
if (!confirm) return;
|
|
206
299
|
|
|
300
|
+
// Save config for next time
|
|
301
|
+
saveCopyTradingConfig({
|
|
302
|
+
leadAccountId: leadAccount.accountId || leadAccount.rithmicAccountId,
|
|
303
|
+
leadAccountName: leadAccount.accountName || leadAccount.rithmicAccountId || leadAccount.accountId,
|
|
304
|
+
leadPropfirm: leadAccount.propfirm || leadAccount.platform || 'Unknown',
|
|
305
|
+
followerAccountIds: followers.map(f => f.accountId || f.rithmicAccountId),
|
|
306
|
+
symbol: contract.symbol,
|
|
307
|
+
strategyId: strategy.id,
|
|
308
|
+
strategyName: strategy.name,
|
|
309
|
+
leadContracts,
|
|
310
|
+
followerContracts,
|
|
311
|
+
dailyTarget,
|
|
312
|
+
maxRisk,
|
|
313
|
+
showNames
|
|
314
|
+
});
|
|
315
|
+
|
|
207
316
|
await launchCopyTrading({
|
|
208
317
|
lead: { account: leadAccount, contracts: leadContracts },
|
|
209
318
|
followers: followers.map(f => ({ account: f, contracts: followerContracts })),
|
|
@@ -13,6 +13,7 @@ const { executeAlgo } = require('./algo-executor');
|
|
|
13
13
|
const { getActiveAgentCount, getSupervisionConfig, getActiveAgents } = require('../ai-agents');
|
|
14
14
|
const { runPreflightCheck, formatPreflightResults, getPreflightSummary } = require('../../services/ai-supervision');
|
|
15
15
|
const { getAvailableStrategies } = require('../../lib/m');
|
|
16
|
+
const { getLastOneAccountConfig, saveOneAccountConfig } = require('../../services/algo-config');
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
|
|
@@ -54,37 +55,112 @@ const oneAccountMenu = async (service) => {
|
|
|
54
55
|
|
|
55
56
|
spinner.succeed(`Found ${activeAccounts.length} active account(s)`);
|
|
56
57
|
|
|
57
|
-
//
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
58
|
+
// Check for saved config
|
|
59
|
+
const lastConfig = getLastOneAccountConfig();
|
|
60
|
+
let selectedAccount = null;
|
|
61
|
+
let contract = null;
|
|
62
|
+
let strategy = null;
|
|
63
|
+
let config = null;
|
|
64
|
+
let accountService = null;
|
|
65
|
+
|
|
66
|
+
if (lastConfig) {
|
|
67
|
+
// Try to find matching account and offer to reuse config
|
|
68
|
+
const matchingAccount = activeAccounts.find(acc =>
|
|
69
|
+
acc.accountId === lastConfig.accountId ||
|
|
70
|
+
acc.rithmicAccountId === lastConfig.accountId ||
|
|
71
|
+
acc.accountName === lastConfig.accountName
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
if (matchingAccount) {
|
|
75
|
+
console.log();
|
|
76
|
+
console.log(chalk.cyan(' Last configuration found:'));
|
|
77
|
+
console.log(chalk.gray(` Account: ${lastConfig.accountName} (${lastConfig.propfirm})`));
|
|
78
|
+
console.log(chalk.gray(` Symbol: ${lastConfig.symbol}`));
|
|
79
|
+
console.log(chalk.gray(` Strategy: ${lastConfig.strategyName}`));
|
|
80
|
+
console.log(chalk.gray(` Contracts: ${lastConfig.contracts} | Target: $${lastConfig.dailyTarget} | Risk: $${lastConfig.maxRisk}`));
|
|
81
|
+
console.log();
|
|
82
|
+
|
|
83
|
+
const reuseConfig = await prompts.confirmPrompt('Use last configuration?', true);
|
|
84
|
+
|
|
85
|
+
if (reuseConfig) {
|
|
86
|
+
selectedAccount = matchingAccount;
|
|
87
|
+
accountService = selectedAccount.service || connections.getServiceForAccount(selectedAccount.accountId) || service;
|
|
88
|
+
|
|
89
|
+
// Load contracts to find the saved symbol
|
|
90
|
+
const contractsResult = await accountService.getContracts();
|
|
91
|
+
if (contractsResult.success) {
|
|
92
|
+
contract = contractsResult.contracts.find(c => c.symbol === lastConfig.symbol);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Find strategy
|
|
96
|
+
const strategies = getAvailableStrategies();
|
|
97
|
+
strategy = strategies.find(s => s.id === lastConfig.strategyId);
|
|
98
|
+
|
|
99
|
+
// Restore config
|
|
100
|
+
if (contract && strategy) {
|
|
101
|
+
config = {
|
|
102
|
+
contracts: lastConfig.contracts,
|
|
103
|
+
dailyTarget: lastConfig.dailyTarget,
|
|
104
|
+
maxRisk: lastConfig.maxRisk,
|
|
105
|
+
showName: lastConfig.showName
|
|
106
|
+
};
|
|
107
|
+
console.log(chalk.green(' ✓ Configuration loaded'));
|
|
108
|
+
} else {
|
|
109
|
+
console.log(chalk.yellow(' Symbol or strategy no longer available, please reconfigure'));
|
|
110
|
+
selectedAccount = null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
84
115
|
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
-
|
|
116
|
+
// If no saved config used, go through normal selection
|
|
117
|
+
if (!selectedAccount) {
|
|
118
|
+
// Select account - display RAW API fields
|
|
119
|
+
const options = activeAccounts.map(acc => {
|
|
120
|
+
// Use what API returns: rithmicAccountId or accountName for Rithmic
|
|
121
|
+
const name = acc.accountName || acc.rithmicAccountId || acc.accountId;
|
|
122
|
+
const balance = acc.balance !== null && acc.balance !== undefined
|
|
123
|
+
? ` - $${acc.balance.toLocaleString()}`
|
|
124
|
+
: '';
|
|
125
|
+
return {
|
|
126
|
+
label: `${name} (${acc.propfirm || acc.platform || 'Unknown'})${balance}`,
|
|
127
|
+
value: acc
|
|
128
|
+
};
|
|
129
|
+
});
|
|
130
|
+
options.push({ label: '< Back', value: 'back' });
|
|
131
|
+
|
|
132
|
+
selectedAccount = await prompts.selectOption('Select Account:', options);
|
|
133
|
+
if (!selectedAccount || selectedAccount === 'back') return;
|
|
134
|
+
|
|
135
|
+
// Use the service attached to the account (from getAllAccounts), fallback to getServiceForAccount
|
|
136
|
+
accountService = selectedAccount.service || connections.getServiceForAccount(selectedAccount.accountId) || service;
|
|
137
|
+
|
|
138
|
+
// Select symbol
|
|
139
|
+
contract = await selectSymbol(accountService, selectedAccount);
|
|
140
|
+
if (!contract) return;
|
|
141
|
+
|
|
142
|
+
// Select strategy
|
|
143
|
+
strategy = await selectStrategy();
|
|
144
|
+
if (!strategy) return;
|
|
145
|
+
|
|
146
|
+
// Configure algo
|
|
147
|
+
config = await configureAlgo(selectedAccount, contract, strategy);
|
|
148
|
+
if (!config) return;
|
|
149
|
+
|
|
150
|
+
// Save config for next time
|
|
151
|
+
saveOneAccountConfig({
|
|
152
|
+
accountId: selectedAccount.accountId || selectedAccount.rithmicAccountId,
|
|
153
|
+
accountName: selectedAccount.accountName || selectedAccount.rithmicAccountId || selectedAccount.accountId,
|
|
154
|
+
propfirm: selectedAccount.propfirm || selectedAccount.platform || 'Unknown',
|
|
155
|
+
symbol: contract.symbol,
|
|
156
|
+
strategyId: strategy.id,
|
|
157
|
+
strategyName: strategy.name,
|
|
158
|
+
contracts: config.contracts,
|
|
159
|
+
dailyTarget: config.dailyTarget,
|
|
160
|
+
maxRisk: config.maxRisk,
|
|
161
|
+
showName: config.showName
|
|
162
|
+
});
|
|
163
|
+
}
|
|
88
164
|
|
|
89
165
|
// Check for AI Supervision BEFORE asking to start
|
|
90
166
|
const agentCount = getActiveAgentCount();
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Algo Config Storage - Persist algo configuration between sessions
|
|
3
|
+
* Saves to ~/.hqx/algo-config.json
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const os = require('os');
|
|
9
|
+
|
|
10
|
+
const CONFIG_DIR = path.join(os.homedir(), '.hqx');
|
|
11
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'algo-config.json');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Ensure config directory exists
|
|
15
|
+
*/
|
|
16
|
+
const ensureConfigDir = () => {
|
|
17
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
18
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Load saved config
|
|
24
|
+
* @returns {Object|null} Saved config or null
|
|
25
|
+
*/
|
|
26
|
+
const loadConfig = () => {
|
|
27
|
+
try {
|
|
28
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
29
|
+
const data = fs.readFileSync(CONFIG_FILE, 'utf8');
|
|
30
|
+
return JSON.parse(data);
|
|
31
|
+
}
|
|
32
|
+
} catch (e) {
|
|
33
|
+
// Ignore errors, return null
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Save config
|
|
40
|
+
* @param {Object} config - Config to save
|
|
41
|
+
*/
|
|
42
|
+
const saveConfig = (config) => {
|
|
43
|
+
try {
|
|
44
|
+
ensureConfigDir();
|
|
45
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
46
|
+
} catch (e) {
|
|
47
|
+
// Ignore save errors
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get last config for one-account mode
|
|
53
|
+
* @returns {Object|null}
|
|
54
|
+
*/
|
|
55
|
+
const getLastOneAccountConfig = () => {
|
|
56
|
+
const config = loadConfig();
|
|
57
|
+
return config?.oneAccount || null;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Save one-account config
|
|
62
|
+
* @param {Object} data - { accountId, accountName, propfirm, symbol, strategyId, contracts, dailyTarget, maxRisk, showName }
|
|
63
|
+
*/
|
|
64
|
+
const saveOneAccountConfig = (data) => {
|
|
65
|
+
const config = loadConfig() || {};
|
|
66
|
+
config.oneAccount = {
|
|
67
|
+
...data,
|
|
68
|
+
savedAt: Date.now()
|
|
69
|
+
};
|
|
70
|
+
saveConfig(config);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Get last config for copy-trading mode
|
|
75
|
+
* @returns {Object|null}
|
|
76
|
+
*/
|
|
77
|
+
const getLastCopyTradingConfig = () => {
|
|
78
|
+
const config = loadConfig();
|
|
79
|
+
return config?.copyTrading || null;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Save copy-trading config
|
|
84
|
+
* @param {Object} data - { masterAccountId, followerAccountIds, symbol, strategyId, contracts, dailyTarget, maxRisk }
|
|
85
|
+
*/
|
|
86
|
+
const saveCopyTradingConfig = (data) => {
|
|
87
|
+
const config = loadConfig() || {};
|
|
88
|
+
config.copyTrading = {
|
|
89
|
+
...data,
|
|
90
|
+
savedAt: Date.now()
|
|
91
|
+
};
|
|
92
|
+
saveConfig(config);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
module.exports = {
|
|
96
|
+
loadConfig,
|
|
97
|
+
saveConfig,
|
|
98
|
+
getLastOneAccountConfig,
|
|
99
|
+
saveOneAccountConfig,
|
|
100
|
+
getLastCopyTradingConfig,
|
|
101
|
+
saveCopyTradingConfig,
|
|
102
|
+
};
|