@trading-boy/cli 1.12.0 → 2.0.1
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/LICENSE +22 -0
- package/README.md +64 -29
- package/dist/api-client.d.ts +4 -7
- package/dist/api-client.js +8 -13
- package/dist/cli.bundle.js +2314 -33711
- package/dist/credentials.js +1 -1
- package/dist/index.d.ts +0 -28
- package/dist/index.js +0 -24
- package/dist/logger.d.ts +8 -0
- package/dist/logger.js +12 -0
- package/dist/utils.js +3 -3
- package/package.json +30 -16
- package/dist/cli.d.ts +0 -5
- package/dist/cli.js +0 -157
- package/dist/commands/agent-cmd.d.ts +0 -9
- package/dist/commands/agent-cmd.js +0 -567
- package/dist/commands/audit.d.ts +0 -18
- package/dist/commands/audit.js +0 -73
- package/dist/commands/behavioral.d.ts +0 -73
- package/dist/commands/behavioral.js +0 -349
- package/dist/commands/benchmark-cmd.d.ts +0 -3
- package/dist/commands/benchmark-cmd.js +0 -191
- package/dist/commands/billing.d.ts +0 -12
- package/dist/commands/billing.js +0 -142
- package/dist/commands/catalysts.d.ts +0 -17
- package/dist/commands/catalysts.js +0 -151
- package/dist/commands/coaching-cmd.d.ts +0 -16
- package/dist/commands/coaching-cmd.js +0 -222
- package/dist/commands/config-cmd.d.ts +0 -30
- package/dist/commands/config-cmd.js +0 -515
- package/dist/commands/connect-chatgpt.d.ts +0 -5
- package/dist/commands/connect-chatgpt.js +0 -293
- package/dist/commands/connect-claude.d.ts +0 -5
- package/dist/commands/connect-claude.js +0 -280
- package/dist/commands/context.d.ts +0 -41
- package/dist/commands/context.js +0 -405
- package/dist/commands/cron-cmd.d.ts +0 -3
- package/dist/commands/cron-cmd.js +0 -305
- package/dist/commands/decisions.d.ts +0 -57
- package/dist/commands/decisions.js +0 -364
- package/dist/commands/edge-cmd.d.ts +0 -78
- package/dist/commands/edge-cmd.js +0 -183
- package/dist/commands/edge-guard-cmd.d.ts +0 -36
- package/dist/commands/edge-guard-cmd.js +0 -169
- package/dist/commands/events.d.ts +0 -3
- package/dist/commands/events.js +0 -117
- package/dist/commands/infra.d.ts +0 -24
- package/dist/commands/infra.js +0 -137
- package/dist/commands/journal.d.ts +0 -3
- package/dist/commands/journal.js +0 -302
- package/dist/commands/login.d.ts +0 -18
- package/dist/commands/login.js +0 -127
- package/dist/commands/logout.d.ts +0 -8
- package/dist/commands/logout.js +0 -108
- package/dist/commands/narratives.d.ts +0 -3
- package/dist/commands/narratives.js +0 -259
- package/dist/commands/onboarding.d.ts +0 -7
- package/dist/commands/onboarding.js +0 -281
- package/dist/commands/query.d.ts +0 -32
- package/dist/commands/query.js +0 -135
- package/dist/commands/replay-cmd.d.ts +0 -43
- package/dist/commands/replay-cmd.js +0 -184
- package/dist/commands/review.d.ts +0 -3
- package/dist/commands/review.js +0 -443
- package/dist/commands/risk.d.ts +0 -47
- package/dist/commands/risk.js +0 -158
- package/dist/commands/social.d.ts +0 -43
- package/dist/commands/social.js +0 -318
- package/dist/commands/soul-wizard.d.ts +0 -29
- package/dist/commands/soul-wizard.js +0 -155
- package/dist/commands/strategy-cmd.d.ts +0 -44
- package/dist/commands/strategy-cmd.js +0 -335
- package/dist/commands/subscribe.d.ts +0 -78
- package/dist/commands/subscribe.js +0 -552
- package/dist/commands/suggestions-cmd.d.ts +0 -24
- package/dist/commands/suggestions-cmd.js +0 -148
- package/dist/commands/thesis-cmd.d.ts +0 -3
- package/dist/commands/thesis-cmd.js +0 -129
- package/dist/commands/trader.d.ts +0 -30
- package/dist/commands/trader.js +0 -971
- package/dist/commands/watch.d.ts +0 -16
- package/dist/commands/watch.js +0 -104
- package/dist/commands/whoami.d.ts +0 -14
- package/dist/commands/whoami.js +0 -105
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
interface TokenSentiment {
|
|
3
|
-
sentiment24h: number;
|
|
4
|
-
sentimentTrend: string;
|
|
5
|
-
signalCount24h: number;
|
|
6
|
-
topTierCount: number;
|
|
7
|
-
breakdown: {
|
|
8
|
-
tier1Avg: number | null;
|
|
9
|
-
tier2Avg: number | null;
|
|
10
|
-
tier3Avg: number | null;
|
|
11
|
-
};
|
|
12
|
-
}
|
|
13
|
-
interface MindshareRanking {
|
|
14
|
-
rank: number;
|
|
15
|
-
compositeScore: number;
|
|
16
|
-
txCount24h: number;
|
|
17
|
-
uniqueWallets24h: number;
|
|
18
|
-
whaleVolume24h: number;
|
|
19
|
-
}
|
|
20
|
-
interface TokenNarrativeRecord {
|
|
21
|
-
narrativeName: string;
|
|
22
|
-
narrativeStatus: string;
|
|
23
|
-
confidence: number;
|
|
24
|
-
}
|
|
25
|
-
export interface SocialSignalRecord {
|
|
26
|
-
id?: string;
|
|
27
|
-
platform: string;
|
|
28
|
-
sentiment: number;
|
|
29
|
-
authorTier: string;
|
|
30
|
-
content?: string | null;
|
|
31
|
-
authorId?: string | null;
|
|
32
|
-
authorHandle?: string | null;
|
|
33
|
-
engagementScore?: number | null;
|
|
34
|
-
extractedEntities?: string[];
|
|
35
|
-
narrativeTags?: string[];
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Format the full social query output for a token.
|
|
39
|
-
*/
|
|
40
|
-
export declare function formatTokenSocial(tokenSymbol: string, sentiment: TokenSentiment | null, mindshare: MindshareRanking | null, narratives: TokenNarrativeRecord[], signals: SocialSignalRecord[]): string;
|
|
41
|
-
export declare function registerSocialCommand(program: Command): void;
|
|
42
|
-
export {};
|
|
43
|
-
//# sourceMappingURL=social.d.ts.map
|
package/dist/commands/social.js
DELETED
|
@@ -1,318 +0,0 @@
|
|
|
1
|
-
import { Option } from 'commander';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import { createLogger, Platform, AuthorTier, } from '@trading-boy/core';
|
|
4
|
-
import { formatConnectionError, padRight, truncate, colorSentiment } from '../utils.js';
|
|
5
|
-
import { apiRequest, ApiError } from '../api-client.js';
|
|
6
|
-
// ─── Logger ───
|
|
7
|
-
const logger = createLogger('cli-social');
|
|
8
|
-
// ─── Formatters ───
|
|
9
|
-
/**
|
|
10
|
-
* Color-code a trend direction.
|
|
11
|
-
*/
|
|
12
|
-
function colorTrend(trend) {
|
|
13
|
-
switch (trend) {
|
|
14
|
-
case 'IMPROVING':
|
|
15
|
-
return chalk.green(trend);
|
|
16
|
-
case 'DECLINING':
|
|
17
|
-
return chalk.red(trend);
|
|
18
|
-
case 'STABLE':
|
|
19
|
-
default:
|
|
20
|
-
return chalk.yellow(trend);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Format the full social query output for a token.
|
|
25
|
-
*/
|
|
26
|
-
export function formatTokenSocial(tokenSymbol, sentiment, mindshare, narratives, signals) {
|
|
27
|
-
const lines = [];
|
|
28
|
-
lines.push('');
|
|
29
|
-
lines.push(chalk.bold.cyan(` Social Overview: ${tokenSymbol}`));
|
|
30
|
-
lines.push(chalk.gray(' ' + '\u2500'.repeat(60)));
|
|
31
|
-
lines.push('');
|
|
32
|
-
// ─── Sentiment section ───
|
|
33
|
-
lines.push(chalk.bold(' Sentiment'));
|
|
34
|
-
if (sentiment) {
|
|
35
|
-
lines.push(` ${chalk.gray('24h Sentiment:')} ${colorSentiment(sentiment.sentiment24h)}`);
|
|
36
|
-
lines.push(` ${chalk.gray('Trend:')} ${colorTrend(sentiment.sentimentTrend)}`);
|
|
37
|
-
lines.push(` ${chalk.gray('Signals (24h):')} ${sentiment.signalCount24h}`);
|
|
38
|
-
lines.push(` ${chalk.gray('Top Tier Count:')} ${sentiment.topTierCount}`);
|
|
39
|
-
if (sentiment.breakdown.tier1Avg !== null) {
|
|
40
|
-
lines.push(` ${chalk.gray('Tier 1 (Mover):')} ${colorSentiment(sentiment.breakdown.tier1Avg)}`);
|
|
41
|
-
}
|
|
42
|
-
if (sentiment.breakdown.tier2Avg !== null) {
|
|
43
|
-
lines.push(` ${chalk.gray('Tier 2 (Alpha):')} ${colorSentiment(sentiment.breakdown.tier2Avg)}`);
|
|
44
|
-
}
|
|
45
|
-
if (sentiment.breakdown.tier3Avg !== null) {
|
|
46
|
-
lines.push(` ${chalk.gray('Tier 3 (Comm.):')} ${colorSentiment(sentiment.breakdown.tier3Avg)}`);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
else {
|
|
50
|
-
lines.push(` ${chalk.dim('No sentiment data available.')}`);
|
|
51
|
-
}
|
|
52
|
-
lines.push('');
|
|
53
|
-
// ─── Mindshare section ───
|
|
54
|
-
lines.push(chalk.bold(' Mindshare'));
|
|
55
|
-
if (mindshare) {
|
|
56
|
-
lines.push(` ${chalk.gray('Rank:')} #${mindshare.rank}`);
|
|
57
|
-
lines.push(` ${chalk.gray('Composite Score:')} ${mindshare.compositeScore.toFixed(3)}`);
|
|
58
|
-
lines.push(` ${chalk.gray('Tx Count (24h):')} ${mindshare.txCount24h.toLocaleString()}`);
|
|
59
|
-
lines.push(` ${chalk.gray('Unique Wallets:')} ${mindshare.uniqueWallets24h.toLocaleString()}`);
|
|
60
|
-
lines.push(` ${chalk.gray('Whale Volume:')} $${mindshare.whaleVolume24h.toLocaleString()}`);
|
|
61
|
-
}
|
|
62
|
-
else {
|
|
63
|
-
lines.push(` ${chalk.dim('No mindshare data available.')}`);
|
|
64
|
-
}
|
|
65
|
-
lines.push('');
|
|
66
|
-
// ─── Narratives section ───
|
|
67
|
-
lines.push(chalk.bold(' Narratives'));
|
|
68
|
-
if (narratives.length > 0) {
|
|
69
|
-
for (const n of narratives) {
|
|
70
|
-
const conf = `${(n.confidence * 100).toFixed(0)}%`;
|
|
71
|
-
lines.push(` ${chalk.white(n.narrativeName)} ${chalk.dim(`(${n.narrativeStatus})`)} — confidence: ${conf}`);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
lines.push(` ${chalk.dim('Not linked to any narratives.')}`);
|
|
76
|
-
}
|
|
77
|
-
lines.push('');
|
|
78
|
-
// ─── Recent signals section ───
|
|
79
|
-
lines.push(chalk.bold(' Recent Signals'));
|
|
80
|
-
if (signals.length > 0) {
|
|
81
|
-
for (const s of signals) {
|
|
82
|
-
const sentimentStr = colorSentiment(s.sentiment);
|
|
83
|
-
const author = s.authorHandle ?? s.authorId ?? chalk.dim('unknown');
|
|
84
|
-
const content = s.content ? truncate(s.content, 50) : '-';
|
|
85
|
-
lines.push(` ${sentimentStr} ${chalk.dim(s.authorTier)} ${chalk.gray(author)}: ${content}`);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
else {
|
|
89
|
-
lines.push(` ${chalk.dim('No recent signals.')}`);
|
|
90
|
-
}
|
|
91
|
-
lines.push('');
|
|
92
|
-
return lines.join('\n');
|
|
93
|
-
}
|
|
94
|
-
// ─── Command Registration ───
|
|
95
|
-
export function registerSocialCommand(program) {
|
|
96
|
-
const social = program
|
|
97
|
-
.command('social')
|
|
98
|
-
.description('Social signal management commands');
|
|
99
|
-
// ─── social query ───
|
|
100
|
-
social
|
|
101
|
-
.command('query <token>')
|
|
102
|
-
.description('Show social overview for a token (sentiment, mindshare, narratives, signals)')
|
|
103
|
-
.option('--limit <n>', 'Maximum number of recent signals to show', (v) => parseInt(v, 10), 5)
|
|
104
|
-
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
105
|
-
.action(async (token, options) => {
|
|
106
|
-
const tokenSymbol = token.toUpperCase();
|
|
107
|
-
try {
|
|
108
|
-
const result = await apiRequest(`/api/v1/tokens/${encodeURIComponent(tokenSymbol)}/social?limit=${options.limit}`);
|
|
109
|
-
if (options.format === 'json') {
|
|
110
|
-
console.log(JSON.stringify(result, null, 2));
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
console.log(formatTokenSocial(tokenSymbol, result.sentiment, result.mindshare, result.narratives, result.signals));
|
|
114
|
-
}
|
|
115
|
-
catch (error) {
|
|
116
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
117
|
-
logger.error({ error: message }, 'Failed to query social data');
|
|
118
|
-
console.error(chalk.red(`Error: ${message}`));
|
|
119
|
-
if (!(error instanceof ApiError)) {
|
|
120
|
-
const guidance = formatConnectionError(message);
|
|
121
|
-
if (guidance)
|
|
122
|
-
console.error(guidance);
|
|
123
|
-
}
|
|
124
|
-
process.exitCode = error instanceof ApiError ? 2 : 1;
|
|
125
|
-
}
|
|
126
|
-
});
|
|
127
|
-
// ─── social add ───
|
|
128
|
-
social
|
|
129
|
-
.command('add')
|
|
130
|
-
.description('Add a manual social signal')
|
|
131
|
-
.requiredOption('--token <symbols...>', 'Token symbol(s) mentioned')
|
|
132
|
-
.requiredOption('--sentiment <value>', 'Sentiment score (-1.0 to 1.0)', parseFloat)
|
|
133
|
-
.option('--tier <tier>', `Author tier (${Object.values(AuthorTier).join(', ')})`, 'TIER2_ALPHA')
|
|
134
|
-
.option('--platform <platform>', `Platform (${Object.values(Platform).join(', ')})`, 'TWITTER')
|
|
135
|
-
.option('--content <text>', 'Signal content / note')
|
|
136
|
-
.option('--author-id <id>', 'Author identifier')
|
|
137
|
-
.option('--author-handle <handle>', 'Author handle (e.g., @username)')
|
|
138
|
-
.option('--engagement <score>', 'Engagement score (0-100)', parseFloat)
|
|
139
|
-
.option('--narrative <narratives...>', 'Narrative name(s) to link')
|
|
140
|
-
.option('--protocol <protocols...>', 'Protocol name(s) to link')
|
|
141
|
-
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
142
|
-
.action(async (options) => {
|
|
143
|
-
// Validate platform
|
|
144
|
-
if (!Object.values(Platform).includes(options.platform)) {
|
|
145
|
-
console.error(`Error: Invalid platform "${options.platform}". Valid platforms: ${Object.values(Platform).join(', ')}`);
|
|
146
|
-
process.exitCode = 1;
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
// Validate author tier
|
|
150
|
-
if (!Object.values(AuthorTier).includes(options.tier)) {
|
|
151
|
-
console.error(`Error: Invalid author tier "${options.tier}". Valid tiers: ${Object.values(AuthorTier).join(', ')}`);
|
|
152
|
-
process.exitCode = 1;
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
// Validate sentiment range
|
|
156
|
-
if (isNaN(options.sentiment) || options.sentiment < -1 || options.sentiment > 1) {
|
|
157
|
-
console.error('Error: Sentiment must be a number between -1.0 and 1.0');
|
|
158
|
-
process.exitCode = 1;
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
try {
|
|
162
|
-
const tokens = options.token.map((t) => t.toUpperCase());
|
|
163
|
-
const body = {
|
|
164
|
-
platform: options.platform,
|
|
165
|
-
sentiment: options.sentiment,
|
|
166
|
-
authorTier: options.tier,
|
|
167
|
-
content: options.content,
|
|
168
|
-
authorId: options.authorId,
|
|
169
|
-
authorHandle: options.authorHandle,
|
|
170
|
-
engagementScore: options.engagement,
|
|
171
|
-
extractedEntities: tokens,
|
|
172
|
-
narrativeTags: options.narrative ?? [],
|
|
173
|
-
tokenSymbol: tokens[0],
|
|
174
|
-
protocolNames: options.protocol ?? [],
|
|
175
|
-
};
|
|
176
|
-
const result = await apiRequest('/api/v1/social/signals', { method: 'POST', body });
|
|
177
|
-
if (options.format === 'json') {
|
|
178
|
-
console.log(JSON.stringify(result, null, 2));
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
if (result.duplicate) {
|
|
182
|
-
console.log(`Duplicate signal detected, existing id=${result.id}`);
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
logger.info({ id: result.id }, 'Social signal created');
|
|
186
|
-
console.log(`Signal created: id=${result.id}`);
|
|
187
|
-
console.log(` Platform: ${options.platform}`);
|
|
188
|
-
console.log(` Sentiment: ${options.sentiment}`);
|
|
189
|
-
console.log(` Tier: ${options.tier}`);
|
|
190
|
-
console.log(` Tokens: ${tokens.join(', ')}`);
|
|
191
|
-
if (options.narrative) {
|
|
192
|
-
console.log(` Narratives: ${options.narrative.join(', ')}`);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
catch (error) {
|
|
196
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
197
|
-
logger.error({ error: message }, 'Failed to add social signal');
|
|
198
|
-
console.error(`Error: ${message}`);
|
|
199
|
-
process.exitCode = error instanceof ApiError ? 2 : 1;
|
|
200
|
-
}
|
|
201
|
-
});
|
|
202
|
-
// ─── social list ───
|
|
203
|
-
social
|
|
204
|
-
.command('list')
|
|
205
|
-
.description('List recent social signals')
|
|
206
|
-
.option('--token <symbol>', 'Filter by token symbol')
|
|
207
|
-
.option('--platform <platform>', 'Filter by platform')
|
|
208
|
-
.option('--hours <n>', 'Time window in hours', (v) => parseInt(v, 10), 24)
|
|
209
|
-
.option('--limit <n>', 'Maximum number of results', (v) => parseInt(v, 10), 20)
|
|
210
|
-
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
211
|
-
.action(async (options) => {
|
|
212
|
-
try {
|
|
213
|
-
const params = new URLSearchParams();
|
|
214
|
-
if (options.token)
|
|
215
|
-
params.set('tokenSymbol', options.token.toUpperCase());
|
|
216
|
-
if (options.platform)
|
|
217
|
-
params.set('platform', options.platform);
|
|
218
|
-
params.set('hours', String(options.hours));
|
|
219
|
-
params.set('limit', String(options.limit));
|
|
220
|
-
const records = await apiRequest(`/api/v1/social/signals?${params.toString()}`);
|
|
221
|
-
if (records.length === 0) {
|
|
222
|
-
if (options.format === 'json') {
|
|
223
|
-
console.log(JSON.stringify([], null, 2));
|
|
224
|
-
}
|
|
225
|
-
else {
|
|
226
|
-
console.log('No signals found.');
|
|
227
|
-
}
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
if (options.format === 'json') {
|
|
231
|
-
console.log(JSON.stringify(records, null, 2));
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
// Print formatted table
|
|
235
|
-
console.log('');
|
|
236
|
-
console.log(padRight('Platform', 12) +
|
|
237
|
-
padRight('Sentiment', 11) +
|
|
238
|
-
padRight('Tier', 22) +
|
|
239
|
-
padRight('Entities', 20) +
|
|
240
|
-
padRight('Content', 40));
|
|
241
|
-
console.log('-'.repeat(105));
|
|
242
|
-
for (const s of records) {
|
|
243
|
-
const sentimentStr = formatSentiment(s.sentiment);
|
|
244
|
-
const entities = s.extractedEntities
|
|
245
|
-
? s.extractedEntities.join(', ')
|
|
246
|
-
: '-';
|
|
247
|
-
console.log(padRight(s.platform, 12) +
|
|
248
|
-
padRight(sentimentStr, 11) +
|
|
249
|
-
padRight(s.authorTier, 22) +
|
|
250
|
-
padRight(truncate(entities, 18), 20) +
|
|
251
|
-
truncate(s.content ?? '-', 38));
|
|
252
|
-
}
|
|
253
|
-
console.log('');
|
|
254
|
-
console.log(`Total: ${records.length} signal(s)`);
|
|
255
|
-
}
|
|
256
|
-
catch (error) {
|
|
257
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
258
|
-
logger.error({ error: message }, 'Failed to list signals');
|
|
259
|
-
console.error(`Error: ${message}`);
|
|
260
|
-
process.exitCode = error instanceof ApiError ? 2 : 1;
|
|
261
|
-
}
|
|
262
|
-
});
|
|
263
|
-
// ─── social sentiment ───
|
|
264
|
-
social
|
|
265
|
-
.command('sentiment')
|
|
266
|
-
.description('Show aggregated sentiment for a token')
|
|
267
|
-
.requiredOption('--token <symbol>', 'Token symbol')
|
|
268
|
-
.option('--hours <n>', 'Time window in hours', (v) => parseInt(v, 10), 24)
|
|
269
|
-
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
270
|
-
.action(async (options) => {
|
|
271
|
-
try {
|
|
272
|
-
const tokenSymbol = options.token.toUpperCase();
|
|
273
|
-
// Fetch signals and compute sentiment from them
|
|
274
|
-
const params = new URLSearchParams();
|
|
275
|
-
params.set('tokenSymbol', tokenSymbol);
|
|
276
|
-
params.set('hours', String(options.hours));
|
|
277
|
-
params.set('limit', '1000');
|
|
278
|
-
const signals = await apiRequest(`/api/v1/social/signals?${params.toString()}`);
|
|
279
|
-
if (signals.length === 0) {
|
|
280
|
-
if (options.format === 'json') {
|
|
281
|
-
console.log(JSON.stringify({ tokenSymbol, hours: options.hours, signalCount: 0, average: null, min: null, max: null }, null, 2));
|
|
282
|
-
}
|
|
283
|
-
else {
|
|
284
|
-
console.log(`No signals found for ${tokenSymbol} in the last ${options.hours}h.`);
|
|
285
|
-
}
|
|
286
|
-
return;
|
|
287
|
-
}
|
|
288
|
-
// Compute sentiment from signals
|
|
289
|
-
const sentiments = signals.map(s => s.sentiment);
|
|
290
|
-
const avgSentiment = sentiments.reduce((a, b) => a + b, 0) / sentiments.length;
|
|
291
|
-
const minSentiment = Math.min(...sentiments);
|
|
292
|
-
const maxSentiment = Math.max(...sentiments);
|
|
293
|
-
if (options.format === 'json') {
|
|
294
|
-
console.log(JSON.stringify({ tokenSymbol, hours: options.hours, signalCount: signals.length, average: avgSentiment, min: minSentiment, max: maxSentiment }, null, 2));
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
|
-
console.log('');
|
|
298
|
-
console.log(`Sentiment for ${tokenSymbol} (last ${options.hours}h):`);
|
|
299
|
-
console.log(` Average: ${formatSentiment(avgSentiment)}`);
|
|
300
|
-
console.log(` Signals: ${signals.length}`);
|
|
301
|
-
console.log(` Min: ${formatSentiment(minSentiment)}`);
|
|
302
|
-
console.log(` Max: ${formatSentiment(maxSentiment)}`);
|
|
303
|
-
console.log('');
|
|
304
|
-
}
|
|
305
|
-
catch (error) {
|
|
306
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
307
|
-
logger.error({ error: message }, 'Failed to get sentiment');
|
|
308
|
-
console.error(`Error: ${message}`);
|
|
309
|
-
process.exitCode = error instanceof ApiError ? 2 : 1;
|
|
310
|
-
}
|
|
311
|
-
});
|
|
312
|
-
}
|
|
313
|
-
// ─── Helpers ───
|
|
314
|
-
function formatSentiment(value) {
|
|
315
|
-
const sign = value >= 0 ? '+' : '';
|
|
316
|
-
return `${sign}${value.toFixed(2)}`;
|
|
317
|
-
}
|
|
318
|
-
//# sourceMappingURL=social.js.map
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
export interface SoulWizardAnswers {
|
|
2
|
-
style: string;
|
|
3
|
-
riskTolerance: string;
|
|
4
|
-
assets: string[];
|
|
5
|
-
timeHorizon: string;
|
|
6
|
-
biases: string;
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* Run the interactive SOUL wizard and return the generated SOUL text.
|
|
10
|
-
* Throws if user aborts (Ctrl+C).
|
|
11
|
-
*/
|
|
12
|
-
export declare function runSoulWizard(): Promise<{
|
|
13
|
-
answers: SoulWizardAnswers;
|
|
14
|
-
soulText: string;
|
|
15
|
-
}>;
|
|
16
|
-
/**
|
|
17
|
-
* Upload a SOUL document to the trader's profile.
|
|
18
|
-
*/
|
|
19
|
-
export declare function uploadSoul(traderName: string, soulText: string): Promise<void>;
|
|
20
|
-
/**
|
|
21
|
-
* Print the generated SOUL document with chalk styling.
|
|
22
|
-
*/
|
|
23
|
-
export declare function displaySoul(soulText: string): void;
|
|
24
|
-
/**
|
|
25
|
-
* Run wizard + upload + display. Used by both onboarding and standalone command.
|
|
26
|
-
* Returns true if SOUL was uploaded successfully.
|
|
27
|
-
*/
|
|
28
|
-
export declare function runSoulWizardAndUpload(traderName: string): Promise<boolean>;
|
|
29
|
-
//# sourceMappingURL=soul-wizard.d.ts.map
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
// ─── SOUL Wizard ───
|
|
2
|
-
//
|
|
3
|
-
// Interactive wizard to generate a SOUL document from guided questions.
|
|
4
|
-
// Shared between onboarding flow and standalone `trader soul-wizard` command.
|
|
5
|
-
import chalk from 'chalk';
|
|
6
|
-
import { apiRequest, ApiError } from '../api-client.js';
|
|
7
|
-
// ─── Labels ───
|
|
8
|
-
const STYLE_LABELS = {
|
|
9
|
-
aggressive_momentum: 'Aggressive Momentum',
|
|
10
|
-
conservative_value: 'Conservative Value',
|
|
11
|
-
balanced: 'Balanced',
|
|
12
|
-
degen: 'Degen / High Risk',
|
|
13
|
-
macro: 'Macro / Fundamentals',
|
|
14
|
-
};
|
|
15
|
-
const RISK_LABELS = {
|
|
16
|
-
low: 'Low (1-3% per trade)',
|
|
17
|
-
medium: 'Medium (3-7%)',
|
|
18
|
-
high: 'High (7-15%)',
|
|
19
|
-
degen: 'Degen (15%+)',
|
|
20
|
-
};
|
|
21
|
-
const ASSET_LABELS = {
|
|
22
|
-
crypto: 'Crypto',
|
|
23
|
-
commodities: 'Commodities',
|
|
24
|
-
equities: 'Equities',
|
|
25
|
-
forex: 'Forex',
|
|
26
|
-
};
|
|
27
|
-
const HORIZON_LABELS = {
|
|
28
|
-
scalping: 'Scalping (minutes)',
|
|
29
|
-
day: 'Day trading (hours)',
|
|
30
|
-
swing: 'Swing (days)',
|
|
31
|
-
position: 'Position (weeks+)',
|
|
32
|
-
};
|
|
33
|
-
// ─── Wizard ───
|
|
34
|
-
/**
|
|
35
|
-
* Run the interactive SOUL wizard and return the generated SOUL text.
|
|
36
|
-
* Throws if user aborts (Ctrl+C).
|
|
37
|
-
*/
|
|
38
|
-
export async function runSoulWizard() {
|
|
39
|
-
const { select, checkbox, input } = await import('@inquirer/prompts');
|
|
40
|
-
const style = await select({
|
|
41
|
-
message: 'What\'s your trading style?',
|
|
42
|
-
choices: [
|
|
43
|
-
{ value: 'aggressive_momentum', name: 'Aggressive Momentum' },
|
|
44
|
-
{ value: 'conservative_value', name: 'Conservative Value' },
|
|
45
|
-
{ value: 'balanced', name: 'Balanced' },
|
|
46
|
-
{ value: 'degen', name: 'Degen / High Risk' },
|
|
47
|
-
{ value: 'macro', name: 'Macro / Fundamentals' },
|
|
48
|
-
],
|
|
49
|
-
});
|
|
50
|
-
const riskTolerance = await select({
|
|
51
|
-
message: 'Risk tolerance?',
|
|
52
|
-
choices: [
|
|
53
|
-
{ value: 'low', name: 'Low (1-3% per trade)' },
|
|
54
|
-
{ value: 'medium', name: 'Medium (3-7%)' },
|
|
55
|
-
{ value: 'high', name: 'High (7-15%)' },
|
|
56
|
-
{ value: 'degen', name: 'Degen (15%+)' },
|
|
57
|
-
],
|
|
58
|
-
});
|
|
59
|
-
const assets = await checkbox({
|
|
60
|
-
message: 'What assets do you trade?',
|
|
61
|
-
choices: [
|
|
62
|
-
{ value: 'crypto', name: 'Crypto' },
|
|
63
|
-
{ value: 'commodities', name: 'Commodities' },
|
|
64
|
-
{ value: 'equities', name: 'Equities' },
|
|
65
|
-
{ value: 'forex', name: 'Forex' },
|
|
66
|
-
],
|
|
67
|
-
});
|
|
68
|
-
const timeHorizon = await select({
|
|
69
|
-
message: 'Time horizon?',
|
|
70
|
-
choices: [
|
|
71
|
-
{ value: 'scalping', name: 'Scalping (minutes)' },
|
|
72
|
-
{ value: 'day', name: 'Day trading (hours)' },
|
|
73
|
-
{ value: 'swing', name: 'Swing (days)' },
|
|
74
|
-
{ value: 'position', name: 'Position (weeks+)' },
|
|
75
|
-
],
|
|
76
|
-
});
|
|
77
|
-
const biases = await input({
|
|
78
|
-
message: 'Any biases to watch for? (optional, press Enter to skip)',
|
|
79
|
-
});
|
|
80
|
-
const answers = { style, riskTolerance, assets, timeHorizon, biases };
|
|
81
|
-
const soulText = buildSoulDocument(answers);
|
|
82
|
-
return { answers, soulText };
|
|
83
|
-
}
|
|
84
|
-
// ─── SOUL Document Builder ───
|
|
85
|
-
function buildSoulDocument(answers) {
|
|
86
|
-
const styleLabel = STYLE_LABELS[answers.style] ?? answers.style;
|
|
87
|
-
const riskLabel = RISK_LABELS[answers.riskTolerance] ?? answers.riskTolerance;
|
|
88
|
-
const assetLabels = answers.assets.map(a => ASSET_LABELS[a] ?? a).join(', ') || 'Not specified';
|
|
89
|
-
const horizonLabel = HORIZON_LABELS[answers.timeHorizon] ?? answers.timeHorizon;
|
|
90
|
-
const lines = [];
|
|
91
|
-
lines.push(`Trading Style: ${styleLabel}`);
|
|
92
|
-
lines.push(`Risk Tolerance: ${riskLabel}`);
|
|
93
|
-
lines.push(`Asset Classes: ${assetLabels}`);
|
|
94
|
-
lines.push(`Time Horizon: ${horizonLabel}`);
|
|
95
|
-
if (answers.biases.trim()) {
|
|
96
|
-
lines.push(`Known Biases: ${answers.biases.trim()}`);
|
|
97
|
-
}
|
|
98
|
-
lines.push('');
|
|
99
|
-
lines.push(`This trader favors ${styleLabel.toLowerCase()} approaches with ${riskLabel.toLowerCase()} risk tolerance,`);
|
|
100
|
-
lines.push(`primarily trading ${assetLabels.toLowerCase()}. They operate on a ${horizonLabel.toLowerCase()} timeframe.`);
|
|
101
|
-
if (answers.biases.trim()) {
|
|
102
|
-
lines.push(`Watch for: ${answers.biases.trim()}`);
|
|
103
|
-
}
|
|
104
|
-
return lines.join('\n');
|
|
105
|
-
}
|
|
106
|
-
// ─── Upload ───
|
|
107
|
-
/**
|
|
108
|
-
* Upload a SOUL document to the trader's profile.
|
|
109
|
-
*/
|
|
110
|
-
export async function uploadSoul(traderName, soulText) {
|
|
111
|
-
await apiRequest(`/api/v1/traders/${encodeURIComponent(traderName)}/soul`, {
|
|
112
|
-
method: 'PUT',
|
|
113
|
-
body: { document: soulText },
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
// ─── Display ───
|
|
117
|
-
/**
|
|
118
|
-
* Print the generated SOUL document with chalk styling.
|
|
119
|
-
*/
|
|
120
|
-
export function displaySoul(soulText) {
|
|
121
|
-
console.log('');
|
|
122
|
-
console.log(chalk.bold.cyan(' Generated SOUL Document'));
|
|
123
|
-
console.log(chalk.gray(' ' + '\u2500'.repeat(50)));
|
|
124
|
-
console.log('');
|
|
125
|
-
for (const line of soulText.split('\n')) {
|
|
126
|
-
console.log(` ${chalk.white(line)}`);
|
|
127
|
-
}
|
|
128
|
-
console.log('');
|
|
129
|
-
}
|
|
130
|
-
// ─── Combined Flow ───
|
|
131
|
-
/**
|
|
132
|
-
* Run wizard + upload + display. Used by both onboarding and standalone command.
|
|
133
|
-
* Returns true if SOUL was uploaded successfully.
|
|
134
|
-
*/
|
|
135
|
-
export async function runSoulWizardAndUpload(traderName) {
|
|
136
|
-
const { soulText } = await runSoulWizard();
|
|
137
|
-
displaySoul(soulText);
|
|
138
|
-
try {
|
|
139
|
-
await uploadSoul(traderName, soulText);
|
|
140
|
-
console.log(chalk.green(` \u2713 SOUL uploaded for trader "${traderName}"`));
|
|
141
|
-
return true;
|
|
142
|
-
}
|
|
143
|
-
catch (error) {
|
|
144
|
-
if (error instanceof ApiError) {
|
|
145
|
-
console.log(chalk.yellow(` Could not upload SOUL: ${error.message}`));
|
|
146
|
-
}
|
|
147
|
-
else {
|
|
148
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
149
|
-
console.log(chalk.yellow(` Could not upload SOUL: ${msg}`));
|
|
150
|
-
}
|
|
151
|
-
console.log(chalk.dim(' You can upload it later: trading-boy trader soul <name> --file soul.md'));
|
|
152
|
-
return false;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
//# sourceMappingURL=soul-wizard.js.map
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
interface AgentStrategy {
|
|
3
|
-
id: string;
|
|
4
|
-
agentId: string;
|
|
5
|
-
traderId: string;
|
|
6
|
-
tenantId: string;
|
|
7
|
-
version: number;
|
|
8
|
-
name: string;
|
|
9
|
-
tokens: string[];
|
|
10
|
-
setupTypes: string[];
|
|
11
|
-
regimeBehavior: Record<string, unknown>;
|
|
12
|
-
riskLimits: Record<string, unknown>;
|
|
13
|
-
signalWeights: Record<string, number>;
|
|
14
|
-
narrativePreferences: string[];
|
|
15
|
-
active: boolean;
|
|
16
|
-
createdAt: string;
|
|
17
|
-
updatedAt: string;
|
|
18
|
-
}
|
|
19
|
-
interface StrategyListResponse {
|
|
20
|
-
strategies: AgentStrategy[];
|
|
21
|
-
total: number;
|
|
22
|
-
limit: number;
|
|
23
|
-
offset: number;
|
|
24
|
-
}
|
|
25
|
-
interface StrategyHistoryEntry {
|
|
26
|
-
id: string;
|
|
27
|
-
strategyId: string;
|
|
28
|
-
version: number;
|
|
29
|
-
name: string;
|
|
30
|
-
tokens: string[];
|
|
31
|
-
setupTypes: string[];
|
|
32
|
-
changedAt: string;
|
|
33
|
-
changedBy?: string;
|
|
34
|
-
}
|
|
35
|
-
interface StrategyHistoryResponse {
|
|
36
|
-
strategyId: string;
|
|
37
|
-
history: StrategyHistoryEntry[];
|
|
38
|
-
}
|
|
39
|
-
export declare function formatStrategyDetail(s: AgentStrategy): string;
|
|
40
|
-
export declare function formatStrategyList(response: StrategyListResponse): string;
|
|
41
|
-
export declare function formatHistoryList(response: StrategyHistoryResponse): string;
|
|
42
|
-
export declare function registerStrategyCommand(program: Command): void;
|
|
43
|
-
export {};
|
|
44
|
-
//# sourceMappingURL=strategy-cmd.d.ts.map
|