@trading-boy/cli 1.12.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 +1896 -33657
  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 -567
  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 -281
  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 -335
  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,364 +0,0 @@
1
- import { Option } from 'commander';
2
- import chalk from 'chalk';
3
- import { createLogger } from '@trading-boy/core';
4
- import { padRight, parseIntOption, formatDate, formatConnectionError } from '../utils.js';
5
- import { apiRequest, ApiError } from '../api-client.js';
6
- // ─── Logger ───
7
- const logger = createLogger('cli-decisions');
8
- // ─── Default Trader ───
9
- const DEFAULT_TRADER_ID = 'default';
10
- // ─── Formatters ───
11
- /**
12
- * Format a PnL value with color coding.
13
- */
14
- function formatPnl(pnl) {
15
- if (pnl === null) {
16
- return chalk.dim('-');
17
- }
18
- const sign = pnl >= 0 ? '+' : '';
19
- const str = `${sign}$${pnl.toFixed(2)}`;
20
- return pnl > 0 ? chalk.green(str) : pnl < 0 ? chalk.red(str) : chalk.dim(str);
21
- }
22
- /**
23
- * Format a percentage with color coding.
24
- */
25
- function formatPercent(pct) {
26
- if (pct === null) {
27
- return chalk.dim('-');
28
- }
29
- const sign = pct >= 0 ? '+' : '';
30
- const str = `${sign}${pct.toFixed(2)}%`;
31
- return pct > 0 ? chalk.green(str) : pct < 0 ? chalk.red(str) : chalk.dim(str);
32
- }
33
- /**
34
- * Format a win rate as a colored percentage.
35
- */
36
- function formatWinRate(rate) {
37
- const str = `${(rate * 100).toFixed(1)}%`;
38
- if (rate >= 0.6) {
39
- return chalk.green(str);
40
- }
41
- if (rate >= 0.4) {
42
- return chalk.yellow(str);
43
- }
44
- return chalk.red(str);
45
- }
46
- /**
47
- * Format milliseconds into a human-readable duration.
48
- */
49
- function formatDuration(ms) {
50
- if (ms === 0) {
51
- return chalk.dim('-');
52
- }
53
- const hours = Math.floor(ms / (1000 * 60 * 60));
54
- const days = Math.floor(hours / 24);
55
- const remainingHours = hours % 24;
56
- if (days > 0) {
57
- return `${days}d ${remainingHours}h`;
58
- }
59
- if (hours > 0) {
60
- return `${hours}h`;
61
- }
62
- const minutes = Math.floor(ms / (1000 * 60));
63
- return `${minutes}m`;
64
- }
65
- /**
66
- * Format a decision record row for the decisions table.
67
- */
68
- function formatDecisionRow(d) {
69
- const date = d.eventTime ? formatDate(d.eventTime) : '-';
70
- const token = d.tokenSymbol ?? '-';
71
- const setup = d.setupType ?? '-';
72
- const dir = d.direction ?? '-';
73
- const formatPrice = (p) => p < 0.01 ? p.toPrecision(3) : p.toFixed(2);
74
- const price = d.entryPrice !== null
75
- ? `$${formatPrice(d.entryPrice)}`
76
- : d.exitPrice !== null
77
- ? `$${formatPrice(d.exitPrice)}`
78
- : '-';
79
- const pnl = formatPnl(d.pnlAbsolute);
80
- const pnlPct = formatPercent(d.pnlPercent);
81
- return (' ' +
82
- padRight(date, 14) +
83
- padRight(token, 8) +
84
- padRight(d.decisionType, 14) +
85
- padRight(dir, 8) +
86
- padRight(setup, 18) +
87
- padRight(price, 12) +
88
- padRight(pnl, 14) +
89
- pnlPct);
90
- }
91
- /**
92
- * Format the decisions list output.
93
- */
94
- export function formatDecisionsOutput(decisions) {
95
- const lines = [];
96
- lines.push('');
97
- lines.push(chalk.bold.cyan(' Trade Decisions'));
98
- lines.push(chalk.gray(' ' + '\u2500'.repeat(100)));
99
- lines.push('');
100
- if (decisions.length === 0) {
101
- lines.push(` ${chalk.dim('No trade decisions found.')}`);
102
- lines.push('');
103
- return lines.join('\n');
104
- }
105
- // Header
106
- lines.push(' ' +
107
- padRight('Date', 14) +
108
- padRight('Token', 8) +
109
- padRight('Type', 14) +
110
- padRight('Dir', 8) +
111
- padRight('Setup', 18) +
112
- padRight('Price', 12) +
113
- padRight('PnL', 14) +
114
- 'PnL %');
115
- lines.push(' ' + '\u2500'.repeat(100));
116
- for (const d of decisions) {
117
- lines.push(formatDecisionRow(d));
118
- }
119
- lines.push('');
120
- lines.push(` ${chalk.gray(`Total: ${decisions.length} decision(s)`)}`);
121
- lines.push('');
122
- return lines.join('\n');
123
- }
124
- /**
125
- * Format the stats output.
126
- */
127
- export function formatStatsOutput(stats, label = 'Overall') {
128
- const lines = [];
129
- lines.push('');
130
- lines.push(chalk.bold.cyan(` Trading Statistics: ${label}`));
131
- lines.push(chalk.gray(' ' + '\u2500'.repeat(50)));
132
- lines.push('');
133
- if (stats.totalTrades === 0) {
134
- lines.push(` ${chalk.dim('No completed trades with outcome data.')}`);
135
- lines.push('');
136
- return lines.join('\n');
137
- }
138
- lines.push(` ${chalk.gray('Total Trades:')} ${chalk.white(String(stats.totalTrades))}`);
139
- lines.push(` ${chalk.gray('Win Rate:')} ${formatWinRate(stats.winRate)}`);
140
- lines.push(` ${chalk.gray('Wins / Losses:')} ${chalk.green(String(stats.wins))} / ${chalk.red(String(stats.losses))}${stats.breakeven > 0 ? ` / ${chalk.dim(String(stats.breakeven) + ' BE')}` : ''}`);
141
- lines.push('');
142
- lines.push(` ${chalk.gray('Total PnL:')} ${formatPnl(stats.totalPnl)}`);
143
- lines.push(` ${chalk.gray('Avg PnL:')} ${formatPnl(stats.avgPnl)}`);
144
- lines.push(` ${chalk.gray('Avg PnL %:')} ${formatPercent(stats.avgPnlPercent)}`);
145
- lines.push(` ${chalk.gray('Avg Hold Time:')} ${formatDuration(stats.avgHoldDurationMs)}`);
146
- if (stats.bestTrade) {
147
- const best = stats.bestTrade;
148
- lines.push('');
149
- lines.push(` ${chalk.gray('Best Trade:')} ${chalk.green(`+$${best.pnlAbsolute.toFixed(2)}`)} ${chalk.dim(`(${best.tokenSymbol} ${best.setupType ?? ''})`).trim()}`);
150
- }
151
- if (stats.worstTrade) {
152
- const worst = stats.worstTrade;
153
- lines.push(` ${chalk.gray('Worst Trade:')} ${chalk.red(`$${worst.pnlAbsolute.toFixed(2)}`)} ${chalk.dim(`(${worst.tokenSymbol} ${worst.setupType ?? ''})`).trim()}`);
154
- }
155
- lines.push('');
156
- return lines.join('\n');
157
- }
158
- /**
159
- * Format stats grouped by setup type as a summary table.
160
- */
161
- export function formatSetupTypeStats(statsBySetup) {
162
- const lines = [];
163
- lines.push('');
164
- lines.push(chalk.bold(' Performance by Setup Type:'));
165
- lines.push('');
166
- if (statsBySetup.size === 0) {
167
- lines.push(` ${chalk.dim('No setup type data available.')}`);
168
- lines.push('');
169
- return lines.join('\n');
170
- }
171
- lines.push(' ' +
172
- padRight('Setup', 20) +
173
- padRight('Trades', 8) +
174
- padRight('Win Rate', 10) +
175
- padRight('Avg PnL', 12) +
176
- padRight('Total PnL', 14));
177
- lines.push(' ' + '\u2500'.repeat(64));
178
- // Sort by total PnL descending
179
- const sorted = [...statsBySetup.entries()].sort((a, b) => b[1].totalPnl - a[1].totalPnl);
180
- for (const [setup, s] of sorted) {
181
- lines.push(' ' +
182
- padRight(setup, 20) +
183
- padRight(String(s.totalTrades), 8) +
184
- padRight(formatWinRate(s.winRate), 10) +
185
- padRight(formatPnl(s.avgPnl), 12) +
186
- formatPnl(s.totalPnl));
187
- }
188
- lines.push('');
189
- return lines.join('\n');
190
- }
191
- // ─── Command Registration ───
192
- export function registerDecisionsCommand(program) {
193
- // ─── decisions command ───
194
- program
195
- .command('decisions')
196
- .description('Show trade decision history with outcomes')
197
- .option('--token <symbol>', 'Filter by token symbol')
198
- .option('--setup <type>', 'Filter by setup type (BREAKOUT, MOMENTUM, etc.)')
199
- .option('--limit <n>', 'Maximum number of results', parseIntOption, 20)
200
- .option('--trader <traderId>', 'Trader ID')
201
- .addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
202
- .action(async (options) => {
203
- try {
204
- const params = new URLSearchParams();
205
- if (options.trader)
206
- params.set('traderId', options.trader);
207
- if (options.token)
208
- params.set('tokenSymbol', options.token.toUpperCase());
209
- if (options.setup)
210
- params.set('setupType', options.setup);
211
- if (options.limit)
212
- params.set('limit', String(options.limit));
213
- const qs = params.toString() ? `?${params.toString()}` : '';
214
- const result = await apiRequest(`/api/v1/decisions${qs}`);
215
- const decisions = result.decisions ?? [];
216
- if (options.format === 'json') {
217
- console.log(JSON.stringify(decisions, null, 2));
218
- }
219
- else {
220
- console.log(formatDecisionsOutput(decisions));
221
- }
222
- }
223
- catch (error) {
224
- const message = error instanceof ApiError ? error.message : (error instanceof Error ? error.message : String(error));
225
- const connErr = formatConnectionError(message);
226
- if (connErr) {
227
- console.error(connErr);
228
- process.exitCode = 1;
229
- return;
230
- }
231
- logger.error({ error: message }, 'Failed to fetch decisions');
232
- console.error(chalk.red(`Error: ${message}`));
233
- process.exitCode = error instanceof ApiError ? 2 : 1;
234
- }
235
- });
236
- // ─── stats command ───
237
- program
238
- .command('stats')
239
- .description('Show aggregate trading statistics (win rate, avg PnL, best/worst setups)')
240
- .option('--period <period>', 'Time period (e.g., 7d, 30d, 90d)', '30d')
241
- .option('--trader <traderId>', 'Trader ID', DEFAULT_TRADER_ID)
242
- .addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
243
- .action(async (options) => {
244
- try {
245
- const params = new URLSearchParams();
246
- if (options.trader && options.trader !== DEFAULT_TRADER_ID)
247
- params.set('traderId', options.trader);
248
- if (options.limit)
249
- params.set('limit', String(options.limit));
250
- const qs = params.toString() ? `?${params.toString()}` : '';
251
- const result = await apiRequest(`/api/v1/decisions${qs}`);
252
- const decisions = result.decisions ?? [];
253
- // Compute stats client-side from decisions with outcomes
254
- const stats = computeStatsFromDecisions(decisions, options.period);
255
- if (options.format === 'json') {
256
- const bySetupType = {};
257
- for (const [key, value] of stats.bySetupType) {
258
- bySetupType[key] = value;
259
- }
260
- console.log(JSON.stringify({ overall: stats.overall, bySetupType }, null, 2));
261
- }
262
- else {
263
- console.log(formatStatsOutput(stats.overall, `Last ${options.period}`));
264
- if (stats.bySetupType.size > 0) {
265
- console.log(formatSetupTypeStats(stats.bySetupType));
266
- }
267
- }
268
- }
269
- catch (error) {
270
- const message = error instanceof ApiError ? error.message : (error instanceof Error ? error.message : String(error));
271
- const connErr = formatConnectionError(message);
272
- if (connErr) {
273
- console.error(connErr);
274
- process.exitCode = 1;
275
- return;
276
- }
277
- logger.error({ error: message }, 'Failed to fetch stats');
278
- console.error(chalk.red(`Error: ${message}`));
279
- process.exitCode = error instanceof ApiError ? 2 : 1;
280
- }
281
- });
282
- }
283
- // ─── Client-Side Stats Computation ───
284
- function computeStatsFromDecisions(decisions, _periodLabel) {
285
- // Filter to decisions with outcome data
286
- const withOutcomes = decisions.filter((d) => d.pnlAbsolute !== null && d.pnlAbsolute !== undefined);
287
- const overall = computeStats(withOutcomes);
288
- const bySetupType = new Map();
289
- // Group by setup type
290
- const grouped = new Map();
291
- for (const d of withOutcomes) {
292
- const setup = d.setupType ?? 'UNKNOWN';
293
- if (!grouped.has(setup))
294
- grouped.set(setup, []);
295
- grouped.get(setup).push(d);
296
- }
297
- for (const [setup, group] of grouped) {
298
- bySetupType.set(setup, computeStats(group));
299
- }
300
- return { overall, bySetupType };
301
- }
302
- function computeStats(decisions) {
303
- if (decisions.length === 0) {
304
- return {
305
- totalTrades: 0, wins: 0, losses: 0, breakeven: 0, winRate: 0,
306
- totalPnl: 0, avgPnl: 0, avgPnlPercent: 0, avgHoldDurationMs: 0,
307
- bestTrade: null, worstTrade: null,
308
- };
309
- }
310
- let wins = 0, losses = 0, breakeven = 0;
311
- let totalPnl = 0, totalPnlPct = 0, totalHold = 0;
312
- let best = null;
313
- let worst = null;
314
- for (const d of decisions) {
315
- const pnl = d.pnlAbsolute ?? 0;
316
- totalPnl += pnl;
317
- totalPnlPct += d.pnlPercent ?? 0;
318
- totalHold += d.holdDurationMs ?? 0;
319
- if (pnl > 0)
320
- wins++;
321
- else if (pnl < 0)
322
- losses++;
323
- else
324
- breakeven++;
325
- if (!best || pnl > (best.pnlAbsolute ?? 0))
326
- best = d;
327
- if (!worst || pnl < (worst.pnlAbsolute ?? 0))
328
- worst = d;
329
- }
330
- return {
331
- totalTrades: decisions.length,
332
- wins, losses, breakeven,
333
- winRate: decisions.length > 0 ? wins / decisions.length : 0,
334
- totalPnl,
335
- avgPnl: totalPnl / decisions.length,
336
- avgPnlPercent: totalPnlPct / decisions.length,
337
- avgHoldDurationMs: totalHold / decisions.length,
338
- bestTrade: best,
339
- worstTrade: worst,
340
- };
341
- }
342
- // ─── Helpers ───
343
- /**
344
- * Parse a period string like "7d", "30d", "90d" into milliseconds.
345
- */
346
- export function parsePeriod(period) {
347
- const match = period.match(/^(\d+)([dhm])$/);
348
- if (!match) {
349
- return 30 * 24 * 60 * 60 * 1000; // Default 30 days
350
- }
351
- const value = parseInt(match[1], 10);
352
- const unit = match[2];
353
- switch (unit) {
354
- case 'd':
355
- return value * 24 * 60 * 60 * 1000;
356
- case 'h':
357
- return value * 60 * 60 * 1000;
358
- case 'm':
359
- return value * 60 * 1000;
360
- default:
361
- return 30 * 24 * 60 * 60 * 1000;
362
- }
363
- }
364
- //# sourceMappingURL=decisions.js.map
@@ -1,78 +0,0 @@
1
- import { Command } from 'commander';
2
- interface EdgeRatioResult {
3
- edgeRatio: number;
4
- avgMfeAtr: number;
5
- avgMaeAtr: number;
6
- sampleSize: number;
7
- }
8
- interface EfficiencyResult {
9
- entryEfficiency: number;
10
- exitEfficiency: number;
11
- combinedEfficiency: number;
12
- }
13
- interface EdgeAnalysis {
14
- traderId: string;
15
- analysis: {
16
- edgeRatio: {
17
- overall: EdgeRatioResult;
18
- bySetupType: Record<string, EdgeRatioResult>;
19
- byRegime: Record<string, EdgeRatioResult>;
20
- };
21
- efficiency: {
22
- overall: EfficiencyResult;
23
- bySetupType: Record<string, EfficiencyResult>;
24
- confidenceCorrelation: number | null;
25
- };
26
- trades: Array<{
27
- decisionId: string;
28
- tokenSymbol: string;
29
- setupType: string | null;
30
- direction: string | null;
31
- entryPrice: number;
32
- exitPrice: number;
33
- mfeAtr: number;
34
- maeAtr: number;
35
- entryEfficiency: number;
36
- exitEfficiency: number;
37
- combinedEfficiency: number;
38
- pnlPercent: number;
39
- }>;
40
- excludedCount: number;
41
- } | null;
42
- message?: string;
43
- }
44
- interface TradeAttribution {
45
- decisionId: string;
46
- tokenSymbol: string;
47
- direction: string;
48
- realizedPnl: number;
49
- signalAlpha: number;
50
- executionCost: number;
51
- timingVariance: number;
52
- }
53
- interface AttributionAnalysis {
54
- traderId: string;
55
- analysis: {
56
- attributions: TradeAttribution[];
57
- overall: {
58
- trades: number;
59
- avgSignalAlphaPct: number;
60
- avgExecutionCostPct: number;
61
- avgTimingVariancePct: number;
62
- totalPnlUsd: number;
63
- };
64
- bySetupType: Record<string, {
65
- trades: number;
66
- avgSignalAlphaPct: number;
67
- avgExecutionCostPct: number;
68
- avgTimingVariancePct: number;
69
- }>;
70
- excludedCount: number;
71
- } | null;
72
- message?: string;
73
- }
74
- export declare function formatEdgeOutput(data: EdgeAnalysis): string;
75
- export declare function formatAttributionOutput(data: AttributionAnalysis): string;
76
- export declare function registerEdgeCommand(program: Command): void;
77
- export {};
78
- //# sourceMappingURL=edge-cmd.d.ts.map
@@ -1,183 +0,0 @@
1
- import { Option } from 'commander';
2
- import chalk from 'chalk';
3
- import { apiRequest, ApiError } from '../api-client.js';
4
- import { ensureRemote } from '../utils.js';
5
- // ─── Formatters ───
6
- export function formatEdgeOutput(data) {
7
- const lines = [];
8
- lines.push('');
9
- lines.push(chalk.bold.cyan(` Edge Ratio Analysis — ${data.traderId}`));
10
- lines.push(chalk.gray(' ' + '─'.repeat(50)));
11
- if (!data.analysis) {
12
- lines.push(` ${chalk.dim(data.message ?? 'No data available')}`);
13
- lines.push('');
14
- return lines.join('\n');
15
- }
16
- const a = data.analysis;
17
- const overall = a.edgeRatio?.overall;
18
- const eff = a.efficiency?.overall;
19
- // Overall edge ratio
20
- const edgeVal = overall?.edgeRatio ?? 0;
21
- lines.push(` ${chalk.gray('Edge Ratio:')} ${colorEdge(edgeVal)}`);
22
- lines.push(` ${chalk.gray('Avg MFE/ATR:')} ${(overall?.avgMfeAtr ?? 0).toFixed(3)}`);
23
- lines.push(` ${chalk.gray('Avg MAE/ATR:')} ${(overall?.avgMaeAtr ?? 0).toFixed(3)}`);
24
- lines.push(` ${chalk.gray('Sample Size:')} ${overall?.sampleSize ?? 0}`);
25
- lines.push('');
26
- // Efficiency
27
- if (eff) {
28
- lines.push(chalk.bold(' Efficiency'));
29
- lines.push(` ${chalk.gray('Entry:')} ${formatPct(eff.entryEfficiency)}`);
30
- lines.push(` ${chalk.gray('Exit:')} ${formatPct(eff.exitEfficiency)}`);
31
- lines.push(` ${chalk.gray('Combined:')} ${formatPct(eff.combinedEfficiency)}`);
32
- if (a.efficiency.confidenceCorrelation !== null && a.efficiency.confidenceCorrelation !== undefined) {
33
- lines.push(` ${chalk.gray('Confidence Corr:')} ${a.efficiency.confidenceCorrelation.toFixed(3)}`);
34
- }
35
- lines.push('');
36
- }
37
- // By Setup Type
38
- const setupEntries = Object.entries(a.edgeRatio?.bySetupType ?? {});
39
- if (setupEntries.length > 0) {
40
- lines.push(chalk.bold(' By Setup Type'));
41
- lines.push(` ${chalk.gray('Setup'.padEnd(20))}${'Edge'.padStart(8)}${'MFE/ATR'.padStart(10)}${'MAE/ATR'.padStart(10)}${'N'.padStart(5)}`);
42
- lines.push(chalk.gray(' ' + '─'.repeat(53)));
43
- for (const [setup, er] of setupEntries) {
44
- lines.push(` ${setup.padEnd(20)}${colorEdge(er.edgeRatio).padStart(8)}${er.avgMfeAtr.toFixed(3).padStart(10)}${er.avgMaeAtr.toFixed(3).padStart(10)}${String(er.sampleSize).padStart(5)}`);
45
- }
46
- lines.push('');
47
- }
48
- // By Regime
49
- const regimeEntries = Object.entries(a.edgeRatio?.byRegime ?? {});
50
- if (regimeEntries.length > 0) {
51
- lines.push(chalk.bold(' By Regime'));
52
- lines.push(` ${chalk.gray('Regime'.padEnd(20))}${'Edge'.padStart(8)}${'MFE/ATR'.padStart(10)}${'MAE/ATR'.padStart(10)}${'N'.padStart(5)}`);
53
- lines.push(chalk.gray(' ' + '─'.repeat(53)));
54
- for (const [regime, er] of regimeEntries) {
55
- lines.push(` ${regime.padEnd(20)}${colorEdge(er.edgeRatio).padStart(8)}${er.avgMfeAtr.toFixed(3).padStart(10)}${er.avgMaeAtr.toFixed(3).padStart(10)}${String(er.sampleSize).padStart(5)}`);
56
- }
57
- lines.push('');
58
- }
59
- // Trade summary
60
- if (a.trades.length > 0) {
61
- lines.push(` ${chalk.dim(`${a.trades.length} trades analyzed, ${a.excludedCount} excluded (missing ATR)`)}`);
62
- lines.push('');
63
- }
64
- return lines.join('\n');
65
- }
66
- export function formatAttributionOutput(data) {
67
- const lines = [];
68
- lines.push('');
69
- lines.push(chalk.bold.cyan(` PnL Attribution — ${data.traderId}`));
70
- lines.push(chalk.gray(' ' + '─'.repeat(50)));
71
- if (!data.analysis) {
72
- lines.push(` ${chalk.dim(data.message ?? 'No data available')}`);
73
- lines.push('');
74
- return lines.join('\n');
75
- }
76
- const s = data.analysis.overall;
77
- if (!s) {
78
- lines.push(` ${chalk.dim('No attribution data available')}`);
79
- lines.push('');
80
- return lines.join('\n');
81
- }
82
- lines.push(chalk.bold(' Summary'));
83
- lines.push(` ${chalk.gray('Total PnL:')} ${colorPnl(s.totalPnlUsd ?? 0)}`);
84
- lines.push(` ${chalk.gray('Signal Alpha:')} ${formatPct(s.avgSignalAlphaPct ?? 0)}`);
85
- lines.push(` ${chalk.gray('Execution Cost:')} ${formatPct(s.avgExecutionCostPct ?? 0)}`);
86
- lines.push(` ${chalk.gray('Timing Variance:')} ${formatPct(s.avgTimingVariancePct ?? 0)}`);
87
- lines.push(` ${chalk.gray('Trade Count:')} ${s.trades ?? 0}`);
88
- // By Setup Type
89
- const setupEntries = Object.entries(data.analysis.bySetupType ?? {});
90
- if (setupEntries.length > 0) {
91
- lines.push('');
92
- lines.push(chalk.bold(' By Setup Type'));
93
- lines.push(` ${chalk.gray('Setup'.padEnd(20))}${'Signal%'.padStart(10)}${'Exec%'.padStart(10)}${'Timing%'.padStart(10)}${'N'.padStart(5)}`);
94
- lines.push(chalk.gray(' ' + '─'.repeat(55)));
95
- for (const [setup, v] of setupEntries) {
96
- lines.push(` ${setup.padEnd(20)}${formatPct(v.avgSignalAlphaPct).padStart(10)}${formatPct(v.avgExecutionCostPct).padStart(10)}${formatPct(v.avgTimingVariancePct).padStart(10)}${String(v.trades).padStart(5)}`);
97
- }
98
- }
99
- if (data.analysis.attributions.length > 0) {
100
- lines.push('');
101
- lines.push(chalk.bold(' Per-Trade Breakdown'));
102
- lines.push(` ${chalk.gray('Token'.padEnd(10))}${'PnL'.padStart(12)}${'Signal'.padStart(12)}${'Exec'.padStart(12)}${'Timing'.padStart(12)}`);
103
- lines.push(chalk.gray(' ' + '─'.repeat(58)));
104
- const shown = data.analysis.attributions.slice(0, 20);
105
- for (const t of shown) {
106
- lines.push(` ${t.tokenSymbol.padEnd(10)}${formatUsd(t.realizedPnl ?? 0).padStart(12)}${formatUsd(t.signalAlpha ?? 0).padStart(12)}${formatUsd(t.executionCost ?? 0).padStart(12)}${formatUsd(t.timingVariance ?? 0).padStart(12)}`);
107
- }
108
- if (data.analysis.attributions.length > 20) {
109
- lines.push(chalk.dim(` ... and ${data.analysis.attributions.length - 20} more`));
110
- }
111
- }
112
- lines.push('');
113
- return lines.join('\n');
114
- }
115
- function colorEdge(ratio) {
116
- const r = ratio ?? 0;
117
- const str = r.toFixed(3);
118
- if (r >= 1.0)
119
- return chalk.green(str);
120
- if (r >= 0.5)
121
- return chalk.yellow(str);
122
- return chalk.red(str);
123
- }
124
- function colorPnl(usd) {
125
- const v = usd ?? 0;
126
- const str = formatUsd(v);
127
- return v >= 0 ? chalk.green(str) : chalk.red(str);
128
- }
129
- function formatUsd(val) {
130
- const v = val ?? 0;
131
- const sign = v >= 0 ? '+' : '';
132
- return `${sign}$${v.toFixed(2)}`;
133
- }
134
- function formatPct(val) {
135
- const v = val ?? 0;
136
- return `${v >= 0 ? '+' : ''}${(v * 100).toFixed(1)}%`;
137
- }
138
- // ─── Command Registration ───
139
- export function registerEdgeCommand(program) {
140
- program
141
- .command('edge <traderId>')
142
- .description('Analyze trade quality — edge ratio and PnL attribution')
143
- .option('--attribution', 'Show PnL attribution breakdown instead of edge ratio')
144
- .option('--token <symbol>', 'Filter by token symbol')
145
- .option('--limit <n>', 'Number of trades to analyze (1-500)', '100')
146
- .addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
147
- .action(async (traderId, options) => {
148
- try {
149
- if (!(await ensureRemote()))
150
- return;
151
- const query = new URLSearchParams();
152
- if (options.token)
153
- query.set('tokenSymbol', options.token.toUpperCase());
154
- if (options.limit !== '100')
155
- query.set('limit', options.limit);
156
- const qs = query.toString() ? `?${query.toString()}` : '';
157
- if (options.attribution) {
158
- const data = await apiRequest(`/api/v1/traders/${encodeURIComponent(traderId)}/attribution${qs}`);
159
- if (options.format === 'json') {
160
- console.log(JSON.stringify(data, null, 2));
161
- }
162
- else {
163
- console.log(formatAttributionOutput(data));
164
- }
165
- }
166
- else {
167
- const data = await apiRequest(`/api/v1/traders/${encodeURIComponent(traderId)}/edge${qs}`);
168
- if (options.format === 'json') {
169
- console.log(JSON.stringify(data, null, 2));
170
- }
171
- else {
172
- console.log(formatEdgeOutput(data));
173
- }
174
- }
175
- }
176
- catch (error) {
177
- const message = error instanceof ApiError ? error.message : (error instanceof Error ? error.message : String(error));
178
- console.error(chalk.red(`Error: ${message}`));
179
- process.exitCode = error instanceof ApiError ? 2 : 1;
180
- }
181
- });
182
- }
183
- //# sourceMappingURL=edge-cmd.js.map
@@ -1,36 +0,0 @@
1
- import { Command } from 'commander';
2
- interface EdgeGuardAssessment {
3
- traderId: string;
4
- assessment: {
5
- status: string;
6
- escalation: string;
7
- portfolioEV: number;
8
- portfolioEVLower: number;
9
- portfolioEVUpper: number;
10
- negativeCells: number;
11
- totalCells: number;
12
- frictionActive: boolean;
13
- message: string;
14
- assessedAt: string;
15
- cellEstimates: Array<{
16
- setupType: string;
17
- regime: string;
18
- evPoint: number;
19
- evLower: number;
20
- evUpper: number;
21
- winRate: number;
22
- }>;
23
- };
24
- }
25
- interface FrictionResult {
26
- traderId: string;
27
- requiresAcknowledgement: boolean;
28
- escalation: string;
29
- message: string;
30
- acknowledged: boolean;
31
- }
32
- export declare function formatAssessmentOutput(data: EdgeGuardAssessment): string;
33
- export declare function formatFrictionOutput(data: FrictionResult): string;
34
- export declare function registerEdgeGuardCommand(program: Command): void;
35
- export {};
36
- //# sourceMappingURL=edge-guard-cmd.d.ts.map