@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.
Files changed (84) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +64 -29
  3. package/dist/api-client.d.ts +4 -7
  4. package/dist/api-client.js +8 -13
  5. package/dist/cli.bundle.js +2314 -33711
  6. package/dist/credentials.js +1 -1
  7. package/dist/index.d.ts +0 -28
  8. package/dist/index.js +0 -24
  9. package/dist/logger.d.ts +8 -0
  10. package/dist/logger.js +12 -0
  11. package/dist/utils.js +3 -3
  12. package/package.json +30 -16
  13. package/dist/cli.d.ts +0 -5
  14. package/dist/cli.js +0 -157
  15. package/dist/commands/agent-cmd.d.ts +0 -9
  16. package/dist/commands/agent-cmd.js +0 -567
  17. package/dist/commands/audit.d.ts +0 -18
  18. package/dist/commands/audit.js +0 -73
  19. package/dist/commands/behavioral.d.ts +0 -73
  20. package/dist/commands/behavioral.js +0 -349
  21. package/dist/commands/benchmark-cmd.d.ts +0 -3
  22. package/dist/commands/benchmark-cmd.js +0 -191
  23. package/dist/commands/billing.d.ts +0 -12
  24. package/dist/commands/billing.js +0 -142
  25. package/dist/commands/catalysts.d.ts +0 -17
  26. package/dist/commands/catalysts.js +0 -151
  27. package/dist/commands/coaching-cmd.d.ts +0 -16
  28. package/dist/commands/coaching-cmd.js +0 -222
  29. package/dist/commands/config-cmd.d.ts +0 -30
  30. package/dist/commands/config-cmd.js +0 -515
  31. package/dist/commands/connect-chatgpt.d.ts +0 -5
  32. package/dist/commands/connect-chatgpt.js +0 -293
  33. package/dist/commands/connect-claude.d.ts +0 -5
  34. package/dist/commands/connect-claude.js +0 -280
  35. package/dist/commands/context.d.ts +0 -41
  36. package/dist/commands/context.js +0 -405
  37. package/dist/commands/cron-cmd.d.ts +0 -3
  38. package/dist/commands/cron-cmd.js +0 -305
  39. package/dist/commands/decisions.d.ts +0 -57
  40. package/dist/commands/decisions.js +0 -364
  41. package/dist/commands/edge-cmd.d.ts +0 -78
  42. package/dist/commands/edge-cmd.js +0 -183
  43. package/dist/commands/edge-guard-cmd.d.ts +0 -36
  44. package/dist/commands/edge-guard-cmd.js +0 -169
  45. package/dist/commands/events.d.ts +0 -3
  46. package/dist/commands/events.js +0 -117
  47. package/dist/commands/infra.d.ts +0 -24
  48. package/dist/commands/infra.js +0 -137
  49. package/dist/commands/journal.d.ts +0 -3
  50. package/dist/commands/journal.js +0 -302
  51. package/dist/commands/login.d.ts +0 -18
  52. package/dist/commands/login.js +0 -127
  53. package/dist/commands/logout.d.ts +0 -8
  54. package/dist/commands/logout.js +0 -108
  55. package/dist/commands/narratives.d.ts +0 -3
  56. package/dist/commands/narratives.js +0 -259
  57. package/dist/commands/onboarding.d.ts +0 -7
  58. package/dist/commands/onboarding.js +0 -281
  59. package/dist/commands/query.d.ts +0 -32
  60. package/dist/commands/query.js +0 -135
  61. package/dist/commands/replay-cmd.d.ts +0 -43
  62. package/dist/commands/replay-cmd.js +0 -184
  63. package/dist/commands/review.d.ts +0 -3
  64. package/dist/commands/review.js +0 -443
  65. package/dist/commands/risk.d.ts +0 -47
  66. package/dist/commands/risk.js +0 -158
  67. package/dist/commands/social.d.ts +0 -43
  68. package/dist/commands/social.js +0 -318
  69. package/dist/commands/soul-wizard.d.ts +0 -29
  70. package/dist/commands/soul-wizard.js +0 -155
  71. package/dist/commands/strategy-cmd.d.ts +0 -44
  72. package/dist/commands/strategy-cmd.js +0 -335
  73. package/dist/commands/subscribe.d.ts +0 -78
  74. package/dist/commands/subscribe.js +0 -552
  75. package/dist/commands/suggestions-cmd.d.ts +0 -24
  76. package/dist/commands/suggestions-cmd.js +0 -148
  77. package/dist/commands/thesis-cmd.d.ts +0 -3
  78. package/dist/commands/thesis-cmd.js +0 -129
  79. package/dist/commands/trader.d.ts +0 -30
  80. package/dist/commands/trader.js +0 -971
  81. package/dist/commands/watch.d.ts +0 -16
  82. package/dist/commands/watch.js +0 -104
  83. package/dist/commands/whoami.d.ts +0 -14
  84. 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