@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,443 +0,0 @@
1
- // ─── Journal Review Commands ───
2
- //
3
- // trading-boy journal review daily|weekly|monthly
4
- // Structured review cadence for trade journaling.
5
- import { createLogger } from '@trading-boy/core';
6
- import { formatConnectionError, padRight, truncate } from '../utils.js';
7
- import { apiRequest, ApiError } from '../api-client.js';
8
- // ─── Logger ───
9
- const logger = createLogger('cli-review');
10
- // ─── Default Trader ───
11
- const DEFAULT_TRADER_ID = 'default';
12
- // ─── Helpers ───
13
- function formatUsd(n) {
14
- const sign = n >= 0 ? '+' : '';
15
- return `${sign}$${Math.abs(n).toFixed(0)}`;
16
- }
17
- function formatPct(n) {
18
- const sign = n >= 0 ? '+' : '';
19
- return `${sign}${n.toFixed(1)}%`;
20
- }
21
- function formatMs(ms) {
22
- if (ms < 60_000)
23
- return `${(ms / 1000).toFixed(0)}s`;
24
- if (ms < 3_600_000)
25
- return `${(ms / 60_000).toFixed(0)}m`;
26
- if (ms < 86_400_000)
27
- return `${(ms / 3_600_000).toFixed(1)}h`;
28
- return `${(ms / 86_400_000).toFixed(1)}d`;
29
- }
30
- function brierLabel(score) {
31
- if (score <= 0.1)
32
- return 'Excellent';
33
- if (score <= 0.2)
34
- return 'Good';
35
- if (score <= 0.3)
36
- return 'Fair';
37
- return 'Poor';
38
- }
39
- // ─── Formatters ───
40
- function printDailyReview(review) {
41
- console.log('');
42
- console.log(`═══ Daily Review — ${review.date} ═══`);
43
- console.log('');
44
- console.log(`Trades Today: ${review.entries.length} entries, ${review.exits.length} exits`);
45
- console.log(`PnL Today: ${formatUsd(review.pnlToday)} (${formatPct(review.pnlPercent)})`);
46
- // Behavioral flags
47
- if (review.behavioralFlags.length > 0) {
48
- console.log('');
49
- console.log('Behavioral Flags:');
50
- for (const { flag, count } of review.behavioralFlags) {
51
- console.log(` ⚠ ${flag}: ${count} occurrence(s)`);
52
- }
53
- }
54
- // Commitment stats
55
- if (review.commitmentStats.total > 0) {
56
- console.log('');
57
- console.log('Process Adherence:');
58
- console.log(` Honored: ${review.commitmentStats.honored}/${review.commitmentStats.total} (${(review.commitmentStats.honorRate * 100).toFixed(0)}%)`);
59
- if (review.commitmentStats.violated > 0) {
60
- console.log(` Violated: ${review.commitmentStats.violated}`);
61
- }
62
- }
63
- // Emotional timeline
64
- if (review.emotionalTimeline.length > 0) {
65
- console.log('');
66
- console.log('Emotional Timeline:');
67
- for (const { time, emotion, token } of review.emotionalTimeline) {
68
- console.log(` ${time} ${emotion} ${token}`);
69
- }
70
- }
71
- // Reflection questions
72
- console.log('');
73
- console.log('Questions for reflection:');
74
- for (let i = 0; i < review.reflectionQuestions.length; i++) {
75
- console.log(` ${i + 1}. "${review.reflectionQuestions[i]}"`);
76
- }
77
- console.log('');
78
- }
79
- function printWeeklyReview(review) {
80
- console.log('');
81
- console.log(`═══ Weekly Review — ${review.weekStart} to ${review.weekEnd} ═══`);
82
- console.log('');
83
- console.log(`Total Trades: ${review.tradeCount}`);
84
- if (review.stats.totalTrades > 0) {
85
- console.log(`Win Rate: ${(review.stats.winRate * 100).toFixed(1)}%`);
86
- console.log(`Total PnL: ${formatUsd(review.stats.totalPnl)}`);
87
- console.log(`Avg PnL: ${formatUsd(review.stats.avgPnl)}`);
88
- console.log(`Avg Hold: ${formatMs(review.stats.avgHoldDurationMs)}`);
89
- }
90
- // Top setups
91
- if (review.topSetups.length > 0) {
92
- console.log('');
93
- console.log('Top Setups (by PnL):');
94
- for (const { setup, stats } of review.topSetups) {
95
- console.log(` ${padRight(setup, 20)} ${padRight(`${stats.totalTrades} trades`, 12)} ` +
96
- `WR: ${(stats.winRate * 100).toFixed(0)}% PnL: ${formatUsd(stats.totalPnl)}`);
97
- }
98
- }
99
- // Worst setup
100
- if (review.worstSetup && review.topSetups.length > 1) {
101
- console.log('');
102
- console.log('Worst Setup:');
103
- const { setup, stats } = review.worstSetup;
104
- console.log(` ${setup} — ${stats.totalTrades} trades, WR: ${(stats.winRate * 100).toFixed(0)}%, PnL: ${formatUsd(stats.totalPnl)}`);
105
- }
106
- // Commitment trend
107
- if (review.commitmentTrend.some((w) => w.honorRate > 0)) {
108
- console.log('');
109
- console.log('Commitment Honor Rate Trend (5 weeks):');
110
- for (const { week, honorRate } of review.commitmentTrend) {
111
- const bar = '█'.repeat(Math.round(honorRate * 20));
112
- console.log(` ${week} ${bar} ${(honorRate * 100).toFixed(0)}%`);
113
- }
114
- }
115
- // Behavioral trend
116
- console.log('');
117
- console.log('Behavioral Flag Trend (5 weeks):');
118
- for (const { week, flagCount } of review.behavioralHealthTrend) {
119
- console.log(` ${week} ${flagCount} flag(s)`);
120
- }
121
- console.log('');
122
- }
123
- // Formatter for monthly review — will be used once the API endpoint ships
124
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
125
- function printMonthlyReview(review) {
126
- console.log('');
127
- console.log(`═══ Monthly Review — ${review.monthStart} to ${review.monthEnd} ═══`);
128
- console.log('');
129
- if (review.stats.totalTrades === 0) {
130
- console.log('No completed trades this month.');
131
- return;
132
- }
133
- console.log(`Total Trades: ${review.stats.totalTrades}`);
134
- console.log(`Win Rate: ${(review.stats.winRate * 100).toFixed(1)}%`);
135
- console.log(`Total PnL: ${formatUsd(review.stats.totalPnl)}`);
136
- console.log(`Avg PnL: ${formatUsd(review.stats.avgPnl)}`);
137
- // Setup breakdown with progressive confidence
138
- if (review.setupBreakdown.length > 0) {
139
- console.log('');
140
- console.log('Setup Breakdown:');
141
- console.log(padRight('Setup', 20) + padRight('N', 6) + padRight('Win Rate', 12) +
142
- padRight('Avg PnL', 12) + padRight('Total PnL', 12) + 'Confidence');
143
- console.log('-'.repeat(74));
144
- for (const { setup, stats, sampleSize } of review.setupBreakdown) {
145
- let confidence = '';
146
- if (sampleSize < 10)
147
- confidence = 'low (N<10)';
148
- else if (sampleSize < 30)
149
- confidence = 'moderate';
150
- else
151
- confidence = 'high';
152
- console.log(padRight(truncate(setup, 18), 20) +
153
- padRight(String(sampleSize), 6) +
154
- padRight(`${(stats.winRate * 100).toFixed(0)}%`, 12) +
155
- padRight(formatUsd(stats.avgPnl), 12) +
156
- padRight(formatUsd(stats.totalPnl), 12) +
157
- confidence);
158
- }
159
- }
160
- // Confidence calibration
161
- printCalibration(review.calibration);
162
- // Process-outcome matrix
163
- printProcessOutcomeMatrix(review.processOutcomeMatrix);
164
- // Regime breakdown
165
- if (review.regimeBreakdown.length > 0) {
166
- console.log('');
167
- console.log('Performance by Market Regime:');
168
- for (const { regime, stats } of review.regimeBreakdown) {
169
- console.log(` ${padRight(regime, 16)} ${stats.totalTrades} trades ` +
170
- `WR: ${(stats.winRate * 100).toFixed(0)}% PnL: ${formatUsd(stats.totalPnl)}`);
171
- }
172
- }
173
- // Edge detection
174
- if (review.edgeDetection.length > 0) {
175
- console.log('');
176
- console.log('Edge Detection (z-score test, p<0.05):');
177
- for (const edge of review.edgeDetection) {
178
- const symbol = edge.hasEdge ? '✓' : '✗';
179
- console.log(` ${symbol} ${padRight(edge.setup, 20)} z=${edge.zScore.toFixed(2)} p=${edge.pValue.toFixed(3)} ` +
180
- `WR: ${(edge.winRate * 100).toFixed(0)}% N=${edge.sampleSize}`);
181
- }
182
- }
183
- console.log('');
184
- }
185
- function printCalibration(cal) {
186
- if (cal.totalTrades === 0) {
187
- console.log('');
188
- console.log('No decisions found. Log trades with `journal log entry` first.');
189
- return;
190
- }
191
- console.log('');
192
- console.log(`═══ Confidence Calibration (${cal.totalTrades} trades) ═══`);
193
- console.log('');
194
- console.log(padRight('Confidence', 14) + padRight('Trades', 9) + padRight('Predicted', 11) +
195
- padRight('Actual', 9) + padRight('Delta', 10) + 'Avg PnL');
196
- console.log('-'.repeat(65));
197
- for (const bucket of cal.buckets) {
198
- if (bucket.tradeCount === 0) {
199
- console.log(padRight(`${bucket.range.min.toFixed(1)} - ${bucket.range.max.toFixed(1)}`, 14) +
200
- padRight('-', 9) + padRight('-', 11) + padRight('-', 9) + padRight('-', 10) + '-');
201
- continue;
202
- }
203
- let deltaIndicator = '✓';
204
- if (Math.abs(bucket.delta) > 0.15)
205
- deltaIndicator = bucket.delta > 0 ? '⚠' : '↑';
206
- else if (Math.abs(bucket.delta) > 0.05)
207
- deltaIndicator = bucket.delta > 0 ? '↓' : '↑';
208
- console.log(padRight(`${bucket.range.min.toFixed(1)} - ${bucket.range.max.toFixed(1)}`, 14) +
209
- padRight(String(bucket.tradeCount), 9) +
210
- padRight(`${(bucket.predictedWinRate * 100).toFixed(0)}%`, 11) +
211
- padRight(`${(bucket.actualWinRate * 100).toFixed(0)}%`, 9) +
212
- padRight(`${formatPct(bucket.delta * 100)} ${deltaIndicator}`, 10) +
213
- formatUsd(bucket.avgPnl));
214
- }
215
- console.log('');
216
- console.log(`Brier Score: ${cal.brierScore.toFixed(2)} (${brierLabel(cal.brierScore)})`);
217
- if (cal.overconfidenceIndex > 0.1) {
218
- console.log(`⚠ You are overconfident (index: ${formatPct(cal.overconfidenceIndex * 100)})`);
219
- }
220
- else if (cal.overconfidenceIndex < -0.1) {
221
- console.log(`You are underconfident (index: ${formatPct(cal.overconfidenceIndex * 100)})`);
222
- }
223
- if (cal.optimalConfidenceRange) {
224
- console.log(`Best-calibrated range: ${cal.optimalConfidenceRange.min.toFixed(1)}-${cal.optimalConfidenceRange.max.toFixed(1)}`);
225
- }
226
- }
227
- function printProcessOutcomeMatrix(matrix) {
228
- const total = matrix.earnedReward.count + matrix.variance.count +
229
- matrix.dumbLuck.count + matrix.mistake.count;
230
- if (total === 0) {
231
- console.log('');
232
- console.log('No decisions found. Log trades with `journal log entry` first.');
233
- return;
234
- }
235
- console.log('');
236
- console.log(`═══ Process-Outcome Matrix (${total} trades) ═══`);
237
- console.log('');
238
- console.log(' GOOD OUTCOME (+PnL) BAD OUTCOME (-PnL)');
239
- console.log(' ───────────────────── ──────────────────');
240
- console.log(`GOOD PROCESS ✅ Earned: ${padRight(String(matrix.earnedReward.count), 12)}` +
241
- `📊 Variance: ${matrix.variance.count}`);
242
- console.log(`(correct thesis) Avg: ${padRight(formatUsd(matrix.earnedReward.avgPnl), 16)}` +
243
- `Avg: ${formatUsd(matrix.variance.avgPnl)}`);
244
- console.log('');
245
- console.log(`BAD PROCESS ⚠ Dumb Luck: ${padRight(String(matrix.dumbLuck.count), 9)}` +
246
- `💀 Mistake: ${matrix.mistake.count}`);
247
- console.log(`(wrong thesis) Avg: ${padRight(formatUsd(matrix.dumbLuck.avgPnl), 16)}` +
248
- `Avg: ${formatUsd(matrix.mistake.avgPnl)}`);
249
- console.log('');
250
- console.log(`Process Win Rate: ${(matrix.processWinRate * 100).toFixed(0)}%`);
251
- console.log(`Outcome Win Rate: ${(matrix.outcomeWinRate * 100).toFixed(0)}%`);
252
- console.log(`Lucky Profit Rate: ${(matrix.luckyProfitRate * 100).toFixed(0)}% of wins from wrong thesis`);
253
- if (matrix.luckyProfitRate > 0.2) {
254
- console.log('');
255
- console.log('⚠ CRITICAL: >20% of your wins came from wrong thesis.');
256
- console.log(' These are reinforcing bad habits. Review your Dumb Luck trades.');
257
- if (matrix.dumbLuck.trades.length > 0) {
258
- const shown = matrix.dumbLuck.trades.slice(0, 3);
259
- for (const t of shown) {
260
- console.log(` - ${t.tokenSymbol} ${t.direction ?? ''} ${new Date(t.eventTime).toLocaleDateString()} ` +
261
- `(${formatUsd(t.pnlAbsolute ?? 0)}): "${truncate(t.thesis ?? 'no thesis', 50)}"`);
262
- }
263
- }
264
- }
265
- }
266
- // ─── Command Registration ───
267
- export function registerReviewCommand(journal) {
268
- const review = journal
269
- .command('review')
270
- .description('Structured trading review');
271
- // ─── journal review daily ───
272
- review
273
- .command('daily')
274
- .description('Daily review — process adherence + emotional recalibration')
275
- .option('--date <date>', 'Review date (YYYY-MM-DD, defaults to today)')
276
- .option('--trader <traderId>', 'Trader ID', DEFAULT_TRADER_ID)
277
- .option('--process-first', 'Hide PnL until process is graded')
278
- .option('--format <format>', 'Output format (text or json)', 'text')
279
- .action(async (options) => {
280
- try {
281
- const date = options.date ?? new Date().toISOString().slice(0, 10);
282
- const review = await apiRequest(`/api/v1/reviews/daily?traderId=${encodeURIComponent(options.trader)}&date=${encodeURIComponent(date)}`);
283
- if (options.format === 'json') {
284
- console.log(JSON.stringify(review, null, 2));
285
- }
286
- else if (options.processFirst) {
287
- printProcessFirstReview(review);
288
- }
289
- else {
290
- printDailyReview(review);
291
- }
292
- }
293
- catch (error) {
294
- const message = error instanceof Error ? error.message : String(error);
295
- logger.error({ error: message }, 'Failed to generate daily review');
296
- console.error(`Error: ${message}`);
297
- if (!(error instanceof ApiError)) {
298
- const guidance = formatConnectionError(message);
299
- if (guidance)
300
- console.error(guidance);
301
- }
302
- process.exitCode = error instanceof ApiError ? 2 : 1;
303
- }
304
- });
305
- // ─── journal review weekly ───
306
- review
307
- .command('weekly')
308
- .description('Weekly review — pattern identification + strengths audit')
309
- .option('--date <date>', 'A date in the target week (YYYY-MM-DD, defaults to today)')
310
- .option('--trader <traderId>', 'Trader ID', DEFAULT_TRADER_ID)
311
- .option('--format <format>', 'Output format (text or json)', 'text')
312
- .action(async (options) => {
313
- try {
314
- const date = options.date ?? new Date().toISOString().slice(0, 10);
315
- const review = await apiRequest(`/api/v1/reviews/weekly?traderId=${encodeURIComponent(options.trader)}&date=${encodeURIComponent(date)}`);
316
- if (options.format === 'json') {
317
- console.log(JSON.stringify(review, null, 2));
318
- }
319
- else {
320
- printWeeklyReview(review);
321
- }
322
- }
323
- catch (error) {
324
- const message = error instanceof Error ? error.message : String(error);
325
- logger.error({ error: message }, 'Failed to generate weekly review');
326
- console.error(`Error: ${message}`);
327
- if (!(error instanceof ApiError)) {
328
- const guidance = formatConnectionError(message);
329
- if (guidance)
330
- console.error(guidance);
331
- }
332
- process.exitCode = error instanceof ApiError ? 2 : 1;
333
- }
334
- });
335
- // ─── journal review monthly ───
336
- review
337
- .command('monthly')
338
- .description('Monthly review — statistical validation + strategy refinement')
339
- .option('--date <date>', 'A date in the target month (YYYY-MM-DD, defaults to today)')
340
- .option('--trader <traderId>', 'Trader ID', DEFAULT_TRADER_ID)
341
- .option('--format <format>', 'Output format (text or json)', 'text')
342
- .action(async (options) => {
343
- try {
344
- const date = options.date || new Date().toISOString().slice(0, 10);
345
- const data = await apiRequest(`/api/v1/reviews/monthly?traderId=${encodeURIComponent(options.trader)}&date=${date}`);
346
- if (options.format === 'json') {
347
- console.log(JSON.stringify(data, null, 2));
348
- }
349
- else {
350
- printMonthlyReview(data);
351
- }
352
- }
353
- catch (error) {
354
- const message = error instanceof Error ? error.message : String(error);
355
- logger.error({ error: message }, 'Failed to generate monthly review');
356
- console.error(`Error: ${message}`);
357
- process.exitCode = error instanceof ApiError ? 2 : 1;
358
- }
359
- });
360
- // ─── journal review matrix ───
361
- review
362
- .command('matrix')
363
- .description('Process-outcome matrix — thesis accuracy analysis')
364
- .option('--trader <traderId>', 'Trader ID', DEFAULT_TRADER_ID)
365
- .option('--format <format>', 'Output format (text or json)', 'text')
366
- .action(async (options) => {
367
- try {
368
- const data = await apiRequest(`/api/v1/reviews/matrix?traderId=${encodeURIComponent(options.trader)}`);
369
- if (options.format === 'json') {
370
- console.log(JSON.stringify(data, null, 2));
371
- }
372
- else {
373
- console.log('\n Process-Outcome Matrix:\n');
374
- console.log(JSON.stringify(data, null, 2));
375
- console.log('');
376
- }
377
- }
378
- catch (error) {
379
- const message = error instanceof Error ? error.message : String(error);
380
- logger.error({ error: message }, 'Failed to generate matrix');
381
- console.error(`Error: ${message}`);
382
- process.exitCode = error instanceof ApiError ? 2 : 1;
383
- }
384
- });
385
- // ─── journal review calibration ───
386
- review
387
- .command('calibration')
388
- .description('Confidence calibration — predicted vs actual win rates')
389
- .option('--trader <traderId>', 'Trader ID', DEFAULT_TRADER_ID)
390
- .option('--format <format>', 'Output format (text or json)', 'text')
391
- .action(async (options) => {
392
- try {
393
- const data = await apiRequest(`/api/v1/reviews/calibration?traderId=${encodeURIComponent(options.trader)}`);
394
- if (options.format === 'json') {
395
- console.log(JSON.stringify(data, null, 2));
396
- }
397
- else {
398
- console.log('\n Confidence Calibration:\n');
399
- console.log(JSON.stringify(data, null, 2));
400
- console.log('');
401
- }
402
- }
403
- catch (error) {
404
- const message = error instanceof Error ? error.message : String(error);
405
- logger.error({ error: message }, 'Failed to generate calibration');
406
- console.error(`Error: ${message}`);
407
- process.exitCode = error instanceof ApiError ? 2 : 1;
408
- }
409
- });
410
- }
411
- // ─── Process-First Mode ───
412
- function printProcessFirstReview(review) {
413
- console.log('');
414
- console.log(`═══ Daily Review (Process-First) — ${review.date} ═══`);
415
- console.log('');
416
- console.log('PnL is hidden. Grade each trade on process before seeing results.');
417
- console.log('');
418
- if (review.exits.length === 0) {
419
- console.log('No exits today. Nothing to grade.');
420
- return;
421
- }
422
- for (let i = 0; i < review.exits.length; i++) {
423
- const t = review.exits[i];
424
- console.log(`Trade ${i + 1}/${review.exits.length}:`);
425
- console.log(` Token: ${t.tokenSymbol} Direction: ${t.direction ?? '-'} Setup: ${t.setupType ?? '-'}`);
426
- console.log(` Emotion: ${t.emotionalTag} Confidence: ${t.confidence.toFixed(2)}`);
427
- if (t.thesis)
428
- console.log(` Thesis: "${truncate(t.thesis, 60)}"`);
429
- console.log(` Process grade: [A] [B] [C] [D] [F]`);
430
- console.log('');
431
- }
432
- console.log('─── Revealing PnL ───');
433
- console.log('');
434
- for (let i = 0; i < review.exits.length; i++) {
435
- const t = review.exits[i];
436
- console.log(`Trade ${i + 1}: ${t.tokenSymbol} — PnL: ${formatUsd(t.pnlAbsolute ?? 0)} ` +
437
- `(${formatPct(t.pnlPercent ?? 0)}) Thesis: ${t.thesisAccuracy}`);
438
- }
439
- console.log('');
440
- console.log(`Total PnL: ${formatUsd(review.pnlToday)}`);
441
- console.log('');
442
- }
443
- //# sourceMappingURL=review.js.map
@@ -1,47 +0,0 @@
1
- import { Command } from 'commander';
2
- interface ProtocolRiskFactors {
3
- oracleConcentration: number;
4
- bridgeDependency: number;
5
- adminCentralization: number;
6
- auditStatus: number;
7
- tvlConcentration: number;
8
- dependencyDepth: number;
9
- [key: string]: number;
10
- }
11
- interface ProtocolRiskResult {
12
- protocolName: string;
13
- protocolId: string;
14
- riskScore: number;
15
- factors: ProtocolRiskFactors;
16
- }
17
- interface ContagionSimulationResult {
18
- sourceProtocol: string;
19
- sourceProtocolName: string;
20
- totalAffected: number;
21
- maxDepthReached: number;
22
- affected: Array<{
23
- protocolName: string;
24
- impactFactor: number;
25
- pathLength: number;
26
- transmissionEdge: string;
27
- path: string[];
28
- }>;
29
- }
30
- /**
31
- * Format a risk score (0-100) with color coding.
32
- * - 0-30: green (low risk)
33
- * - 31-60: yellow (moderate risk)
34
- * - 61-100: red (high risk)
35
- */
36
- export declare function formatRiskScore(score: number): string;
37
- /**
38
- * Format the full risk assessment output for a protocol.
39
- */
40
- export declare function formatRiskOutput(result: ProtocolRiskResult): string;
41
- /**
42
- * Format contagion simulation output.
43
- */
44
- export declare function formatContagionOutput(result: ContagionSimulationResult): string;
45
- export declare function registerRiskCommand(program: Command): void;
46
- export {};
47
- //# sourceMappingURL=risk.d.ts.map
@@ -1,158 +0,0 @@
1
- import { Option } from 'commander';
2
- import chalk from 'chalk';
3
- import { createLogger } from '@trading-boy/core';
4
- import { formatConnectionError, padRight, ensureRemote } from '../utils.js';
5
- import { apiRequest, ApiError } from '../api-client.js';
6
- // ─── Logger ───
7
- const logger = createLogger('cli-risk');
8
- // ─── Formatters ───
9
- /**
10
- * Format a risk score (0-100) with color coding.
11
- * - 0-30: green (low risk)
12
- * - 31-60: yellow (moderate risk)
13
- * - 61-100: red (high risk)
14
- */
15
- export function formatRiskScore(score) {
16
- const label = `${score}/100`;
17
- if (score <= 30) {
18
- return chalk.green(label);
19
- }
20
- if (score <= 60) {
21
- return chalk.yellow(label);
22
- }
23
- return chalk.red(label);
24
- }
25
- /**
26
- * Format a factor score with a visual bar.
27
- */
28
- function formatFactorBar(score, width = 20) {
29
- const filled = Math.round((score / 100) * width);
30
- const empty = width - filled;
31
- const bar = '\u2588'.repeat(filled) + '\u2591'.repeat(empty);
32
- if (score <= 30) {
33
- return chalk.green(bar);
34
- }
35
- if (score <= 60) {
36
- return chalk.yellow(bar);
37
- }
38
- return chalk.red(bar);
39
- }
40
- /**
41
- * Format the full risk assessment output for a protocol.
42
- */
43
- export function formatRiskOutput(result) {
44
- const lines = [];
45
- lines.push('');
46
- lines.push(chalk.bold.cyan(` Protocol: ${result.protocolName}`));
47
- lines.push(chalk.gray(' ' + '\u2500'.repeat(50)));
48
- lines.push('');
49
- lines.push(` ${chalk.gray('Risk Score:')} ${formatRiskScore(result.riskScore)}`);
50
- lines.push('');
51
- lines.push(chalk.bold(' Factor Breakdown:'));
52
- lines.push('');
53
- const factors = [
54
- { name: 'Oracle Concentration', score: result.factors.oracleConcentration, weight: '25%' },
55
- { name: 'Bridge Dependency', score: result.factors.bridgeDependency, weight: '15%' },
56
- { name: 'Admin Centralization', score: result.factors.adminCentralization, weight: '20%' },
57
- { name: 'Audit Status', score: result.factors.auditStatus, weight: '15%' },
58
- { name: 'TVL Concentration', score: result.factors.tvlConcentration, weight: '10%' },
59
- { name: 'Dependency Depth', score: result.factors.dependencyDepth, weight: '15%' },
60
- ];
61
- for (const factor of factors) {
62
- const nameStr = padRight(factor.name, 24);
63
- const scoreStr = padRight(String(factor.score), 4);
64
- const bar = formatFactorBar(factor.score);
65
- lines.push(` ${chalk.gray(nameStr)} ${bar} ${scoreStr} ${chalk.dim(`(${factor.weight})`)}`);
66
- }
67
- lines.push('');
68
- return lines.join('\n');
69
- }
70
- /**
71
- * Format contagion simulation output.
72
- */
73
- export function formatContagionOutput(result) {
74
- const lines = [];
75
- lines.push('');
76
- lines.push(chalk.bold.red(` Contagion Simulation: ${result.sourceProtocolName} failure`));
77
- lines.push(chalk.gray(' ' + '\u2500'.repeat(50)));
78
- lines.push('');
79
- lines.push(` ${chalk.gray('Affected Protocols:')} ${chalk.yellow(String(result.totalAffected))}`);
80
- lines.push(` ${chalk.gray('Max Depth Reached:')} ${result.maxDepthReached}`);
81
- lines.push('');
82
- if (result.affected.length === 0) {
83
- lines.push(` ${chalk.dim('No downstream protocols affected.')}`);
84
- }
85
- else {
86
- lines.push(' ' +
87
- padRight('Protocol', 24) +
88
- padRight('Impact', 10) +
89
- padRight('Hops', 6) +
90
- padRight('Edge', 22) +
91
- 'Path');
92
- lines.push(' ' + '\u2500'.repeat(90));
93
- for (const affected of result.affected) {
94
- const impactStr = (affected.impactFactor * 100).toFixed(1) + '%';
95
- const impactColor = affected.impactFactor >= 0.5
96
- ? chalk.red
97
- : affected.impactFactor >= 0.25
98
- ? chalk.yellow
99
- : chalk.dim;
100
- lines.push(' ' +
101
- padRight(affected.protocolName, 24) +
102
- impactColor(padRight(impactStr, 10)) +
103
- padRight(String(affected.pathLength), 6) +
104
- padRight(affected.transmissionEdge, 22) +
105
- chalk.dim(affected.path.join(' -> ')));
106
- }
107
- }
108
- lines.push('');
109
- return lines.join('\n');
110
- }
111
- // ─── Command Registration ───
112
- export function registerRiskCommand(program) {
113
- program
114
- .command('risk <protocol>')
115
- .description('Assess DeFi protocol risk — dependency tree, risk score, concentration risks')
116
- .option('--contagion', 'Run contagion simulation to show affected protocols')
117
- .option('--update-all', 'Update risk scores for all protocols in the graph')
118
- .addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
119
- .action(async (protocol, options) => {
120
- if (options.updateAll) {
121
- console.error(chalk.yellow('--update-all is not yet supported via the API. Use the admin interface.'));
122
- process.exitCode = 1;
123
- return;
124
- }
125
- if (options.contagion) {
126
- console.error(chalk.yellow('--contagion is not yet supported via the API.'));
127
- process.exitCode = 1;
128
- return;
129
- }
130
- if (!(await ensureRemote()))
131
- return;
132
- try {
133
- const apiResult = await apiRequest(`/api/v1/protocols/${encodeURIComponent(protocol.toLowerCase())}/risk`);
134
- const result = {
135
- protocolName: apiResult.protocol,
136
- protocolId: protocol.toLowerCase(),
137
- riskScore: apiResult.riskScore,
138
- factors: apiResult.factors,
139
- };
140
- if (options.format === 'json') {
141
- console.log(JSON.stringify(result, null, 2));
142
- }
143
- else {
144
- console.log(formatRiskOutput(result));
145
- }
146
- }
147
- catch (error) {
148
- const message = error instanceof ApiError ? error.message : (error instanceof Error ? error.message : String(error));
149
- logger.error({ error: message }, 'Risk assessment failed');
150
- console.error(chalk.red(`Error: ${message}`));
151
- const guidance = formatConnectionError(message);
152
- if (guidance)
153
- console.error(guidance);
154
- process.exitCode = error instanceof ApiError ? 2 : 1;
155
- }
156
- });
157
- }
158
- //# sourceMappingURL=risk.js.map