@trading-boy/cli 1.11.0 → 2.0.0

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.
Files changed (83) hide show
  1. package/README.md +50 -22
  2. package/dist/api-client.d.ts +4 -7
  3. package/dist/api-client.js +8 -13
  4. package/dist/cli.bundle.js +1977 -33976
  5. package/dist/credentials.js +1 -1
  6. package/dist/index.d.ts +0 -28
  7. package/dist/index.js +0 -24
  8. package/dist/logger.d.ts +8 -0
  9. package/dist/logger.js +12 -0
  10. package/dist/utils.js +3 -3
  11. package/package.json +20 -5
  12. package/dist/cli.d.ts +0 -5
  13. package/dist/cli.js +0 -157
  14. package/dist/commands/agent-cmd.d.ts +0 -9
  15. package/dist/commands/agent-cmd.js +0 -572
  16. package/dist/commands/audit.d.ts +0 -18
  17. package/dist/commands/audit.js +0 -73
  18. package/dist/commands/behavioral.d.ts +0 -73
  19. package/dist/commands/behavioral.js +0 -349
  20. package/dist/commands/benchmark-cmd.d.ts +0 -3
  21. package/dist/commands/benchmark-cmd.js +0 -191
  22. package/dist/commands/billing.d.ts +0 -12
  23. package/dist/commands/billing.js +0 -142
  24. package/dist/commands/catalysts.d.ts +0 -17
  25. package/dist/commands/catalysts.js +0 -151
  26. package/dist/commands/coaching-cmd.d.ts +0 -16
  27. package/dist/commands/coaching-cmd.js +0 -222
  28. package/dist/commands/config-cmd.d.ts +0 -30
  29. package/dist/commands/config-cmd.js +0 -515
  30. package/dist/commands/connect-chatgpt.d.ts +0 -5
  31. package/dist/commands/connect-chatgpt.js +0 -293
  32. package/dist/commands/connect-claude.d.ts +0 -5
  33. package/dist/commands/connect-claude.js +0 -280
  34. package/dist/commands/context.d.ts +0 -41
  35. package/dist/commands/context.js +0 -405
  36. package/dist/commands/cron-cmd.d.ts +0 -3
  37. package/dist/commands/cron-cmd.js +0 -305
  38. package/dist/commands/decisions.d.ts +0 -57
  39. package/dist/commands/decisions.js +0 -364
  40. package/dist/commands/edge-cmd.d.ts +0 -78
  41. package/dist/commands/edge-cmd.js +0 -183
  42. package/dist/commands/edge-guard-cmd.d.ts +0 -36
  43. package/dist/commands/edge-guard-cmd.js +0 -169
  44. package/dist/commands/events.d.ts +0 -3
  45. package/dist/commands/events.js +0 -117
  46. package/dist/commands/infra.d.ts +0 -24
  47. package/dist/commands/infra.js +0 -137
  48. package/dist/commands/journal.d.ts +0 -3
  49. package/dist/commands/journal.js +0 -302
  50. package/dist/commands/login.d.ts +0 -18
  51. package/dist/commands/login.js +0 -127
  52. package/dist/commands/logout.d.ts +0 -8
  53. package/dist/commands/logout.js +0 -108
  54. package/dist/commands/narratives.d.ts +0 -3
  55. package/dist/commands/narratives.js +0 -259
  56. package/dist/commands/onboarding.d.ts +0 -7
  57. package/dist/commands/onboarding.js +0 -298
  58. package/dist/commands/query.d.ts +0 -32
  59. package/dist/commands/query.js +0 -135
  60. package/dist/commands/replay-cmd.d.ts +0 -43
  61. package/dist/commands/replay-cmd.js +0 -184
  62. package/dist/commands/review.d.ts +0 -3
  63. package/dist/commands/review.js +0 -443
  64. package/dist/commands/risk.d.ts +0 -47
  65. package/dist/commands/risk.js +0 -158
  66. package/dist/commands/social.d.ts +0 -43
  67. package/dist/commands/social.js +0 -318
  68. package/dist/commands/soul-wizard.d.ts +0 -29
  69. package/dist/commands/soul-wizard.js +0 -155
  70. package/dist/commands/strategy-cmd.d.ts +0 -44
  71. package/dist/commands/strategy-cmd.js +0 -340
  72. package/dist/commands/subscribe.d.ts +0 -78
  73. package/dist/commands/subscribe.js +0 -552
  74. package/dist/commands/suggestions-cmd.d.ts +0 -24
  75. package/dist/commands/suggestions-cmd.js +0 -148
  76. package/dist/commands/thesis-cmd.d.ts +0 -3
  77. package/dist/commands/thesis-cmd.js +0 -129
  78. package/dist/commands/trader.d.ts +0 -30
  79. package/dist/commands/trader.js +0 -971
  80. package/dist/commands/watch.d.ts +0 -16
  81. package/dist/commands/watch.js +0 -104
  82. package/dist/commands/whoami.d.ts +0 -14
  83. package/dist/commands/whoami.js +0 -105
@@ -1,73 +0,0 @@
1
- import { Command } from 'commander';
2
- /**
3
- * Parse a period string like "30d", "7d", "90d" into days.
4
- * Returns default 30 if the string cannot be parsed.
5
- */
6
- export declare function parsePeriodDays(period: string): number;
7
- /** Behavioral pattern enum values (kept local to avoid context-engine dep) */
8
- declare const BehavioralPatternValues: {
9
- readonly REVENGE_TRADING: "REVENGE_TRADING";
10
- readonly FOMO_ENTRY: "FOMO_ENTRY";
11
- readonly OVERTRADING: "OVERTRADING";
12
- readonly STOP_VIOLATION: "STOP_VIOLATION";
13
- readonly TILT_CASCADE: "TILT_CASCADE";
14
- };
15
- type BehavioralPatternValue = (typeof BehavioralPatternValues)[keyof typeof BehavioralPatternValues];
16
- interface BehavioralProfile {
17
- traderId: string;
18
- period: string;
19
- revengeTradeCount: number;
20
- fomoEntryCount: number;
21
- overtradingDays: number;
22
- stopViolationCount: number;
23
- tiltCascadeCount: number;
24
- commitmentHonorRate: number;
25
- overallScore: number;
26
- }
27
- interface PatternCostEntry {
28
- pattern: BehavioralPatternValue;
29
- occurrences: number;
30
- totalCost: number;
31
- winRate: number;
32
- }
33
- interface PatternCostAnalysis {
34
- entries: PatternCostEntry[];
35
- totalBehavioralCost: number;
36
- baselineWinRate: number;
37
- totalPnl: number;
38
- pnlWithoutBehavioralTrades: number;
39
- }
40
- /**
41
- * Color-code a behavioral health score.
42
- * 0-30: green (healthy), 31-60: yellow (watch), 61+: red (concerning)
43
- */
44
- export declare function colorScore(score: number): string;
45
- /**
46
- * Format a commitment honor rate as a colored percentage.
47
- */
48
- export declare function colorHonorRate(rate: number): string;
49
- /**
50
- * Return the label for a behavioral health score.
51
- */
52
- export declare function scoreLabel(score: number): string;
53
- /**
54
- * Format a full behavioral profile as a human-readable string.
55
- */
56
- export declare function formatBehavioralProfile(profile: BehavioralProfile): string;
57
- /**
58
- * Format a list of decisions with behavioral flags.
59
- */
60
- export declare function formatBehavioralFlags(decisions: Array<{
61
- id: string;
62
- eventTime: string;
63
- tokenSymbol: string;
64
- decisionType: string;
65
- behavioralFlags: string[];
66
- }>): string;
67
- /**
68
- * Format a pattern cost analysis as a human-readable table.
69
- */
70
- export declare function formatCostAnalysis(analysis: PatternCostAnalysis, period: string): string;
71
- export declare function registerBehavioralCommand(program: Command): void;
72
- export {};
73
- //# sourceMappingURL=behavioral.d.ts.map
@@ -1,349 +0,0 @@
1
- import { Option } from 'commander';
2
- import chalk from 'chalk';
3
- import { createLogger } from '@trading-boy/core';
4
- import { formatConnectionError, padRight, parseIntOption } from '../utils.js';
5
- import { apiRequest, ApiError } from '../api-client.js';
6
- // ─── Logger ───
7
- const logger = createLogger('cli-behavioral');
8
- // ─── Default Trader ───
9
- const DEFAULT_TRADER_ID = 'default';
10
- // ─── Period Parsing ───
11
- /**
12
- * Parse a period string like "30d", "7d", "90d" into days.
13
- * Returns default 30 if the string cannot be parsed.
14
- */
15
- export function parsePeriodDays(period) {
16
- const match = period.match(/^(\d+)d$/);
17
- if (!match) {
18
- return 30;
19
- }
20
- return parseInt(match[1], 10);
21
- }
22
- // ─── Local Types (mirror API response shapes) ───
23
- /** Behavioral pattern enum values (kept local to avoid context-engine dep) */
24
- const BehavioralPatternValues = {
25
- REVENGE_TRADING: 'REVENGE_TRADING',
26
- FOMO_ENTRY: 'FOMO_ENTRY',
27
- OVERTRADING: 'OVERTRADING',
28
- STOP_VIOLATION: 'STOP_VIOLATION',
29
- TILT_CASCADE: 'TILT_CASCADE',
30
- };
31
- // ─── Formatters ───
32
- /**
33
- * Color-code a behavioral health score.
34
- * 0-30: green (healthy), 31-60: yellow (watch), 61+: red (concerning)
35
- */
36
- export function colorScore(score) {
37
- const str = String(score);
38
- if (score <= 30)
39
- return chalk.green(str);
40
- if (score <= 60)
41
- return chalk.yellow(str);
42
- return chalk.red(str);
43
- }
44
- /**
45
- * Format a commitment honor rate as a colored percentage.
46
- */
47
- export function colorHonorRate(rate) {
48
- const pct = `${(rate * 100).toFixed(1)}%`;
49
- if (rate >= 0.8)
50
- return chalk.green(pct);
51
- if (rate >= 0.5)
52
- return chalk.yellow(pct);
53
- return chalk.red(pct);
54
- }
55
- /**
56
- * Return the label for a behavioral health score.
57
- */
58
- export function scoreLabel(score) {
59
- if (score <= 30)
60
- return 'Healthy';
61
- if (score <= 60)
62
- return 'Watch';
63
- return 'Concerning';
64
- }
65
- /**
66
- * Format a full behavioral profile as a human-readable string.
67
- */
68
- export function formatBehavioralProfile(profile) {
69
- const lines = [];
70
- lines.push('');
71
- lines.push(chalk.bold.cyan(` Behavioral Profile: ${profile.traderId}`));
72
- lines.push(chalk.gray(` Period: ${profile.period}`));
73
- lines.push(chalk.gray(' ' + '\u2500'.repeat(50)));
74
- lines.push('');
75
- // Overall score with color coding
76
- lines.push(` ${chalk.gray('Overall Score:')} ${colorScore(profile.overallScore)} / 100 ${chalk.dim(`(${scoreLabel(profile.overallScore)})`)}`);
77
- lines.push(` ${chalk.gray('Commitment Honor:')} ${colorHonorRate(profile.commitmentHonorRate)}`);
78
- lines.push('');
79
- // Pattern breakdown table
80
- lines.push(chalk.bold(' Pattern Breakdown:'));
81
- lines.push('');
82
- lines.push(' ' +
83
- padRight('Pattern', 22) +
84
- padRight('Count', 8) +
85
- 'Status');
86
- lines.push(' ' + '\u2500'.repeat(50));
87
- const patterns = [
88
- { name: 'Revenge Trades', count: profile.revengeTradeCount, threshold: 2 },
89
- { name: 'FOMO Entries', count: profile.fomoEntryCount, threshold: 3 },
90
- { name: 'Overtrading Days', count: profile.overtradingDays, threshold: 3 },
91
- { name: 'Stop Violations', count: profile.stopViolationCount, threshold: 1 },
92
- { name: 'Tilt Cascades', count: profile.tiltCascadeCount, threshold: 1 },
93
- ];
94
- for (const p of patterns) {
95
- const countStr = String(p.count);
96
- const status = p.count === 0
97
- ? chalk.green('OK')
98
- : p.count <= p.threshold
99
- ? chalk.yellow('Watch')
100
- : chalk.red('Action needed');
101
- lines.push(' ' +
102
- padRight(p.name, 22) +
103
- padRight(countStr, 8) +
104
- status);
105
- }
106
- lines.push('');
107
- return lines.join('\n');
108
- }
109
- /**
110
- * Format a list of decisions with behavioral flags.
111
- */
112
- export function formatBehavioralFlags(decisions) {
113
- const lines = [];
114
- lines.push('');
115
- lines.push(chalk.bold.cyan(' Decisions with Behavioral Flags'));
116
- lines.push(chalk.gray(' ' + '\u2500'.repeat(80)));
117
- lines.push('');
118
- if (decisions.length === 0) {
119
- lines.push(` ${chalk.dim('No decisions with behavioral flags found.')}`);
120
- lines.push('');
121
- return lines.join('\n');
122
- }
123
- // Header
124
- lines.push(' ' +
125
- padRight('Date', 14) +
126
- padRight('Token', 8) +
127
- padRight('Type', 14) +
128
- 'Flags');
129
- lines.push(' ' + '\u2500'.repeat(80));
130
- for (const d of decisions) {
131
- const date = formatShortDate(d.eventTime);
132
- const flags = d.behavioralFlags.map(colorFlag).join(', ');
133
- lines.push(' ' +
134
- padRight(date, 14) +
135
- padRight(d.tokenSymbol, 8) +
136
- padRight(d.decisionType, 14) +
137
- flags);
138
- }
139
- lines.push('');
140
- lines.push(` ${chalk.gray(`Total: ${decisions.length} flagged decision(s)`)}`);
141
- lines.push('');
142
- return lines.join('\n');
143
- }
144
- /**
145
- * Format a pattern cost analysis as a human-readable table.
146
- */
147
- export function formatCostAnalysis(analysis, period) {
148
- const lines = [];
149
- lines.push('');
150
- lines.push(chalk.bold.cyan(' Behavioral Cost Analysis'));
151
- lines.push(chalk.gray(` Period: ${period}`));
152
- lines.push(chalk.gray(' ' + '\u2500'.repeat(70)));
153
- lines.push('');
154
- if (analysis.entries.length === 0) {
155
- lines.push(` ${chalk.green('No behavioral patterns detected in this period.')}`);
156
- lines.push('');
157
- return lines.join('\n');
158
- }
159
- // Header
160
- lines.push(' ' +
161
- padRight('Pattern', 20) +
162
- padRight('Count', 8) +
163
- padRight('Cost', 12) +
164
- padRight('Win Rate', 10) +
165
- 'vs Baseline');
166
- lines.push(' ' + '\u2500'.repeat(70));
167
- const baselinePct = `${(analysis.baselineWinRate * 100).toFixed(0)}%`;
168
- for (const entry of analysis.entries) {
169
- const name = patternDisplayName(entry.pattern);
170
- const costStr = formatUsd(entry.totalCost);
171
- const winRateStr = `${(entry.winRate * 100).toFixed(0)}%`;
172
- const costColor = entry.totalCost < 0 ? chalk.red(costStr) : chalk.green(costStr);
173
- lines.push(' ' +
174
- padRight(name, 20) +
175
- padRight(String(entry.occurrences), 8) +
176
- padRight(costColor, 12 + 10) + // chalk adds escape chars
177
- padRight(winRateStr, 10) +
178
- chalk.dim(`${baselinePct} baseline`));
179
- }
180
- lines.push(' ' + '\u2500'.repeat(70));
181
- lines.push('');
182
- // Summary
183
- const totalCostStr = formatUsd(analysis.totalBehavioralCost);
184
- const totalCostColor = analysis.totalBehavioralCost < 0
185
- ? chalk.red(totalCostStr)
186
- : chalk.green(totalCostStr);
187
- lines.push(` ${chalk.gray('Total behavioral cost:')} ${totalCostColor}`);
188
- const diff = analysis.pnlWithoutBehavioralTrades - analysis.totalPnl;
189
- if (diff > 0) {
190
- lines.push(` ${chalk.gray('Without these patterns, your PnL would be')} ${chalk.green(formatUsd(analysis.pnlWithoutBehavioralTrades))} ${chalk.gray('instead of')} ${formatUsd(analysis.totalPnl)}`);
191
- }
192
- lines.push('');
193
- return lines.join('\n');
194
- }
195
- /**
196
- * Display name for a behavioral pattern.
197
- */
198
- function patternDisplayName(pattern) {
199
- switch (pattern) {
200
- case BehavioralPatternValues.REVENGE_TRADING: return 'Revenge Trading';
201
- case BehavioralPatternValues.FOMO_ENTRY: return 'FOMO Entry';
202
- case BehavioralPatternValues.OVERTRADING: return 'Overtrading';
203
- case BehavioralPatternValues.STOP_VIOLATION: return 'Stop Violation';
204
- case BehavioralPatternValues.TILT_CASCADE: return 'Tilt Cascade';
205
- default: return pattern;
206
- }
207
- }
208
- /**
209
- * Format a USD amount.
210
- */
211
- function formatUsd(amount) {
212
- const sign = amount >= 0 ? '+' : '-';
213
- return `${sign}$${Math.abs(amount).toFixed(2)}`;
214
- }
215
- /**
216
- * Color-code a behavioral flag name.
217
- */
218
- function colorFlag(flag) {
219
- switch (flag) {
220
- case BehavioralPatternValues.REVENGE_TRADING:
221
- return chalk.red('REVENGE_TRADING');
222
- case BehavioralPatternValues.TILT_CASCADE:
223
- return chalk.red('TILT_CASCADE');
224
- case BehavioralPatternValues.STOP_VIOLATION:
225
- return chalk.red('STOP_VIOLATION');
226
- case BehavioralPatternValues.FOMO_ENTRY:
227
- return chalk.yellow('FOMO_ENTRY');
228
- case BehavioralPatternValues.OVERTRADING:
229
- return chalk.yellow('OVERTRADING');
230
- default:
231
- return chalk.dim(flag);
232
- }
233
- }
234
- // ─── Command Registration ───
235
- export function registerBehavioralCommand(program) {
236
- const behavioral = program
237
- .command('behavioral')
238
- .description('Behavioral pattern analysis commands');
239
- // ─── behavioral profile ───
240
- behavioral
241
- .command('profile')
242
- .description('Show behavioral profile (revenge trades, FOMO rate, commitment honor rate)')
243
- .option('--period <period>', 'Analysis period (e.g., 7d, 30d, 90d)', '30d')
244
- .option('--trader <traderId>', 'Trader ID', DEFAULT_TRADER_ID)
245
- .addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
246
- .action(async (options) => {
247
- const periodDays = parsePeriodDays(options.period);
248
- try {
249
- const response = await apiRequest(`/api/v1/behavioral/profile?traderId=${encodeURIComponent(options.trader)}&days=${periodDays}`);
250
- const profile = response.profile;
251
- if (options.format === 'json') {
252
- console.log(JSON.stringify(profile, null, 2));
253
- }
254
- else {
255
- console.log(formatBehavioralProfile(profile));
256
- }
257
- }
258
- catch (error) {
259
- const message = error instanceof Error ? error.message : String(error);
260
- logger.error({ error: message }, 'Failed to compute behavioral profile');
261
- console.error(chalk.red(`Error: ${message}`));
262
- if (!(error instanceof ApiError)) {
263
- const guidance = formatConnectionError(message);
264
- if (guidance)
265
- console.error(guidance);
266
- }
267
- process.exitCode = error instanceof ApiError ? 2 : 1;
268
- }
269
- });
270
- // ─── behavioral flags ───
271
- behavioral
272
- .command('flags')
273
- .description('Show recent decisions with behavioral flags')
274
- .option('--limit <n>', 'Maximum number of results', parseIntOption, 20)
275
- .option('--trader <traderId>', 'Trader ID', DEFAULT_TRADER_ID)
276
- .addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
277
- .action(async (options) => {
278
- try {
279
- const response = await apiRequest(`/api/v1/behavioral/flags?traderId=${encodeURIComponent(options.trader)}`);
280
- // Map API flag detections to the format the formatter expects
281
- const flagged = (response.flags ?? []).map((f) => ({
282
- id: f.id ?? '',
283
- eventTime: f.eventTime ?? '',
284
- tokenSymbol: f.tokenSymbol ?? '',
285
- decisionType: f.decisionType ?? f.pattern ?? '',
286
- behavioralFlags: f.behavioralFlags ?? [f.pattern].filter(Boolean),
287
- }));
288
- const limited = flagged.slice(0, options.limit);
289
- if (options.format === 'json') {
290
- console.log(JSON.stringify(limited, null, 2));
291
- }
292
- else {
293
- console.log(formatBehavioralFlags(limited));
294
- }
295
- }
296
- catch (error) {
297
- const message = error instanceof Error ? error.message : String(error);
298
- logger.error({ error: message }, 'Failed to fetch behavioral flags');
299
- console.error(chalk.red(`Error: ${message}`));
300
- if (!(error instanceof ApiError)) {
301
- const guidance = formatConnectionError(message);
302
- if (guidance)
303
- console.error(guidance);
304
- }
305
- process.exitCode = error instanceof ApiError ? 2 : 1;
306
- }
307
- });
308
- // ─── behavioral costs ───
309
- behavioral
310
- .command('costs')
311
- .description('Show behavioral cost analysis — how much each pattern costs you')
312
- .option('--period <period>', 'Analysis period (e.g., 7d, 30d, 90d)', '30d')
313
- .option('--trader <traderId>', 'Trader ID', DEFAULT_TRADER_ID)
314
- .addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
315
- .action(async (options) => {
316
- const periodDays = parsePeriodDays(options.period);
317
- try {
318
- const response = await apiRequest(`/api/v1/behavioral/costs?traderId=${encodeURIComponent(options.trader)}&days=${periodDays}`);
319
- const costAnalysis = response.costs;
320
- if (options.format === 'json') {
321
- console.log(JSON.stringify(costAnalysis, null, 2));
322
- }
323
- else {
324
- console.log(formatCostAnalysis(costAnalysis, `${periodDays}d`));
325
- }
326
- }
327
- catch (error) {
328
- const message = error instanceof Error ? error.message : String(error);
329
- logger.error({ error: message }, 'Failed to compute behavioral costs');
330
- console.error(chalk.red(`Error: ${message}`));
331
- if (!(error instanceof ApiError)) {
332
- const guidance = formatConnectionError(message);
333
- if (guidance)
334
- console.error(guidance);
335
- }
336
- process.exitCode = error instanceof ApiError ? 2 : 1;
337
- }
338
- });
339
- }
340
- // ─── Helpers ───
341
- function formatShortDate(isoString) {
342
- try {
343
- return new Date(isoString).toISOString().slice(0, 10);
344
- }
345
- catch {
346
- return isoString;
347
- }
348
- }
349
- //# sourceMappingURL=behavioral.js.map
@@ -1,3 +0,0 @@
1
- import { Command } from 'commander';
2
- export declare function registerBenchmarkCommand(program: Command): void;
3
- //# sourceMappingURL=benchmark-cmd.d.ts.map
@@ -1,191 +0,0 @@
1
- // ─── Benchmark CLI Command ───
2
- //
3
- // trading-boy benchmark # Leaderboard (30d, Sharpe)
4
- // trading-boy benchmark --window 90d # 90-day window
5
- // trading-boy benchmark --sort winRate # Sort by win rate
6
- // trading-boy benchmark --setup BREAKOUT # Filter by setup
7
- // trading-boy benchmark --entity <id> # Single entity
8
- // trading-boy benchmark recompute # Trigger recompute
9
- import { Option } from 'commander';
10
- import chalk from 'chalk';
11
- import { apiRequest, ApiError } from '../api-client.js';
12
- import { ensureRemote } from '../utils.js';
13
- // ─── Formatters ───
14
- function formatMetric(value, decimals = 2) {
15
- if (value === null)
16
- return chalk.dim('—');
17
- const str = value.toFixed(decimals);
18
- if (value > 0)
19
- return chalk.green(str);
20
- if (value < 0)
21
- return chalk.red(str);
22
- return str;
23
- }
24
- function formatPct(value, decimals = 1) {
25
- const str = `${(value * 100).toFixed(decimals)}%`;
26
- if (value > 0.5)
27
- return chalk.green(str);
28
- if (value < 0.3)
29
- return chalk.red(str);
30
- return chalk.yellow(str);
31
- }
32
- function formatDuration(ms) {
33
- if (ms === null)
34
- return chalk.dim('—');
35
- const hours = ms / (1000 * 60 * 60);
36
- if (hours < 1)
37
- return `${(ms / (1000 * 60)).toFixed(0)}m`;
38
- if (hours < 24)
39
- return `${hours.toFixed(1)}h`;
40
- return `${(hours / 24).toFixed(1)}d`;
41
- }
42
- function formatLeaderboard(data) {
43
- const lines = [];
44
- lines.push('');
45
- lines.push(chalk.bold.cyan(` Benchmark Leaderboard — ${data.window} / ${data.sortBy}`));
46
- lines.push(chalk.gray(` ${data.total} entities ranked`));
47
- lines.push(chalk.gray(' ' + '─'.repeat(90)));
48
- if (data.entries.length === 0) {
49
- lines.push(` ${chalk.dim('No benchmark data available. Run `trading-boy benchmark recompute` first.')}`);
50
- lines.push('');
51
- return lines.join('\n');
52
- }
53
- // Header
54
- lines.push(` ${chalk.gray('#'.padEnd(4))}` +
55
- `${chalk.gray('Entity'.padEnd(20))}` +
56
- `${chalk.gray('Sharpe'.padStart(8))}` +
57
- `${chalk.gray('Sortino'.padStart(9))}` +
58
- `${chalk.gray('MaxDD'.padStart(8))}` +
59
- `${chalk.gray('PF'.padStart(7))}` +
60
- `${chalk.gray('WinRate'.padStart(9))}` +
61
- `${chalk.gray('Return'.padStart(9))}` +
62
- `${chalk.gray('Trades'.padStart(8))}`);
63
- lines.push(chalk.gray(' ' + '─'.repeat(90)));
64
- for (const entry of data.entries) {
65
- const entityLabel = (entry.publicAlias ?? entry.entityId).substring(0, 18);
66
- lines.push(` ${String(entry.rank).padEnd(4)}` +
67
- `${entityLabel.padEnd(20)}` +
68
- `${formatMetric(entry.sharpeRatio).padStart(8)}` +
69
- `${formatMetric(entry.sortinoRatio).padStart(9)}` +
70
- `${formatMetric(entry.maxDrawdownPct, 1).padStart(8)}` +
71
- `${formatMetric(entry.profitFactor).padStart(7)}` +
72
- `${formatPct(entry.winRate).padStart(9)}` +
73
- `${formatMetric(entry.totalReturnPct, 1).padStart(9)}` +
74
- `${String(entry.totalTrades).padStart(8)}`);
75
- }
76
- lines.push('');
77
- return lines.join('\n');
78
- }
79
- function formatSingleEntity(snapshot) {
80
- const lines = [];
81
- lines.push('');
82
- lines.push(chalk.bold.cyan(` Benchmark — ${snapshot.entityId}`));
83
- lines.push(chalk.gray(` ${snapshot.entityType} / ${snapshot.window}`));
84
- lines.push(chalk.gray(' ' + '─'.repeat(50)));
85
- lines.push(` ${chalk.gray('Sharpe Ratio:')} ${formatMetric(snapshot.sharpeRatio)}`);
86
- lines.push(` ${chalk.gray('Sortino Ratio:')} ${formatMetric(snapshot.sortinoRatio)}`);
87
- lines.push(` ${chalk.gray('Max Drawdown:')} ${formatMetric(snapshot.maxDrawdownPct, 1)}%`);
88
- lines.push(` ${chalk.gray('Calmar Ratio:')} ${formatMetric(snapshot.calmarRatio)}`);
89
- lines.push(` ${chalk.gray('Profit Factor:')} ${formatMetric(snapshot.profitFactor)}`);
90
- lines.push(` ${chalk.gray('Win Rate:')} ${formatPct(snapshot.winRate)}`);
91
- lines.push(` ${chalk.gray('Total Return:')} ${formatMetric(snapshot.totalReturnPct, 1)}%`);
92
- lines.push(` ${chalk.gray('TWR:')} ${formatMetric(snapshot.timeWeightedReturn, 1)}%`);
93
- lines.push('');
94
- lines.push(` ${chalk.gray('Total Trades:')} ${snapshot.totalTrades}`);
95
- lines.push(` ${chalk.gray('Avg Reward:')} ${formatMetric(snapshot.avgCompositeReward)}`);
96
- lines.push(` ${chalk.gray('Avg Edge Ratio:')} ${formatMetric(snapshot.avgEdgeRatio, 3)}`);
97
- lines.push(` ${chalk.gray('Avg Hold Time:')} ${formatDuration(snapshot.avgHoldDurationMs)}`);
98
- lines.push(` ${chalk.gray('Flag Rate:')} ${formatPct(snapshot.behavioralFlagRate)}`);
99
- lines.push(` ${chalk.gray('Commitment Honor:')} ${snapshot.commitmentHonorRate !== null ? formatPct(snapshot.commitmentHonorRate) : chalk.dim('—')}`);
100
- lines.push('');
101
- return lines.join('\n');
102
- }
103
- // ─── Command Registration ───
104
- export function registerBenchmarkCommand(program) {
105
- const benchCmd = program
106
- .command('benchmark')
107
- .description('View benchmark leaderboard and performance metrics')
108
- .addOption(new Option('--window <window>', 'Time window').choices(['7d', '30d', '90d', 'all']).default('30d'))
109
- .addOption(new Option('--sort <field>', 'Sort leaderboard by').choices([
110
- 'sharpeRatio', 'sortinoRatio', 'maxDrawdown', 'calmarRatio',
111
- 'profitFactor', 'winRate', 'totalReturn', 'timeWeightedReturn',
112
- ]).default('sharpeRatio'))
113
- .addOption(new Option('--entity-type <type>', 'Entity type').choices(['trader', 'agent', 'strategy']).default('trader'))
114
- .option('--setup <setupType>', 'Filter by setup type')
115
- .option('--regime <regime>', 'Filter by market regime')
116
- .option('--token <symbol>', 'Filter by token symbol')
117
- .option('--entity <entityId>', 'Show single entity benchmark')
118
- .option('--limit <n>', 'Number of entries', '20')
119
- .addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
120
- .action(async (options) => {
121
- try {
122
- if (!(await ensureRemote()))
123
- return;
124
- if (options.entity) {
125
- // Single entity
126
- const query = new URLSearchParams();
127
- query.set('window', options.window);
128
- if (options.setup)
129
- query.set('setupType', options.setup);
130
- if (options.regime)
131
- query.set('regime', options.regime);
132
- if (options.token)
133
- query.set('tokenSymbol', options.token.toUpperCase());
134
- const data = await apiRequest(`/api/v1/benchmark/${encodeURIComponent(options.entity)}?${query.toString()}`);
135
- if (options.format === 'json') {
136
- console.log(JSON.stringify(data.snapshot, null, 2));
137
- }
138
- else {
139
- console.log(formatSingleEntity(data.snapshot));
140
- }
141
- }
142
- else {
143
- // Leaderboard
144
- const query = new URLSearchParams();
145
- query.set('window', options.window);
146
- query.set('sortBy', options.sort);
147
- query.set('entityType', options.entityType);
148
- query.set('limit', options.limit);
149
- if (options.setup)
150
- query.set('setupType', options.setup);
151
- if (options.regime)
152
- query.set('regime', options.regime);
153
- if (options.token)
154
- query.set('tokenSymbol', options.token.toUpperCase());
155
- const data = await apiRequest(`/api/v1/benchmark/leaderboard?${query.toString()}`);
156
- if (options.format === 'json') {
157
- console.log(JSON.stringify(data, null, 2));
158
- }
159
- else {
160
- console.log(formatLeaderboard(data));
161
- }
162
- }
163
- }
164
- catch (error) {
165
- const message = error instanceof ApiError ? error.message : (error instanceof Error ? error.message : String(error));
166
- console.error(chalk.red(`Error: ${message}`));
167
- process.exitCode = error instanceof ApiError ? 2 : 1;
168
- }
169
- });
170
- // Subcommand: recompute
171
- benchCmd
172
- .command('recompute')
173
- .description('Trigger on-demand benchmark recompute')
174
- .action(async () => {
175
- try {
176
- if (!(await ensureRemote()))
177
- return;
178
- console.log(chalk.dim('Recomputing benchmarks...'));
179
- const data = await apiRequest('/api/v1/benchmark/recompute', { method: 'POST', body: {} });
180
- console.log(chalk.green(`\n ${data.message}`));
181
- console.log(chalk.dim(' Recompute runs in the background. Check leaderboard shortly.'));
182
- console.log('');
183
- }
184
- catch (error) {
185
- const message = error instanceof ApiError ? error.message : (error instanceof Error ? error.message : String(error));
186
- console.error(chalk.red(`Error: ${message}`));
187
- process.exitCode = error instanceof ApiError ? 2 : 1;
188
- }
189
- });
190
- }
191
- //# sourceMappingURL=benchmark-cmd.js.map
@@ -1,12 +0,0 @@
1
- import { Command } from 'commander';
2
- export interface BillingStatusResponse {
3
- email: string;
4
- plan: string;
5
- status: string;
6
- keyPrefix?: string;
7
- nextBillingDate?: string;
8
- currentPeriodEnd?: string;
9
- }
10
- export declare function formatBillingStatus(status: BillingStatusResponse): string;
11
- export declare function registerBillingCommand(program: Command): void;
12
- //# sourceMappingURL=billing.d.ts.map