@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.
- package/README.md +50 -22
- package/dist/api-client.d.ts +4 -7
- package/dist/api-client.js +8 -13
- package/dist/cli.bundle.js +1977 -33976
- package/dist/credentials.js +1 -1
- package/dist/index.d.ts +0 -28
- package/dist/index.js +0 -24
- package/dist/logger.d.ts +8 -0
- package/dist/logger.js +12 -0
- package/dist/utils.js +3 -3
- package/package.json +20 -5
- package/dist/cli.d.ts +0 -5
- package/dist/cli.js +0 -157
- package/dist/commands/agent-cmd.d.ts +0 -9
- package/dist/commands/agent-cmd.js +0 -572
- package/dist/commands/audit.d.ts +0 -18
- package/dist/commands/audit.js +0 -73
- package/dist/commands/behavioral.d.ts +0 -73
- package/dist/commands/behavioral.js +0 -349
- package/dist/commands/benchmark-cmd.d.ts +0 -3
- package/dist/commands/benchmark-cmd.js +0 -191
- package/dist/commands/billing.d.ts +0 -12
- package/dist/commands/billing.js +0 -142
- package/dist/commands/catalysts.d.ts +0 -17
- package/dist/commands/catalysts.js +0 -151
- package/dist/commands/coaching-cmd.d.ts +0 -16
- package/dist/commands/coaching-cmd.js +0 -222
- package/dist/commands/config-cmd.d.ts +0 -30
- package/dist/commands/config-cmd.js +0 -515
- package/dist/commands/connect-chatgpt.d.ts +0 -5
- package/dist/commands/connect-chatgpt.js +0 -293
- package/dist/commands/connect-claude.d.ts +0 -5
- package/dist/commands/connect-claude.js +0 -280
- package/dist/commands/context.d.ts +0 -41
- package/dist/commands/context.js +0 -405
- package/dist/commands/cron-cmd.d.ts +0 -3
- package/dist/commands/cron-cmd.js +0 -305
- package/dist/commands/decisions.d.ts +0 -57
- package/dist/commands/decisions.js +0 -364
- package/dist/commands/edge-cmd.d.ts +0 -78
- package/dist/commands/edge-cmd.js +0 -183
- package/dist/commands/edge-guard-cmd.d.ts +0 -36
- package/dist/commands/edge-guard-cmd.js +0 -169
- package/dist/commands/events.d.ts +0 -3
- package/dist/commands/events.js +0 -117
- package/dist/commands/infra.d.ts +0 -24
- package/dist/commands/infra.js +0 -137
- package/dist/commands/journal.d.ts +0 -3
- package/dist/commands/journal.js +0 -302
- package/dist/commands/login.d.ts +0 -18
- package/dist/commands/login.js +0 -127
- package/dist/commands/logout.d.ts +0 -8
- package/dist/commands/logout.js +0 -108
- package/dist/commands/narratives.d.ts +0 -3
- package/dist/commands/narratives.js +0 -259
- package/dist/commands/onboarding.d.ts +0 -7
- package/dist/commands/onboarding.js +0 -298
- package/dist/commands/query.d.ts +0 -32
- package/dist/commands/query.js +0 -135
- package/dist/commands/replay-cmd.d.ts +0 -43
- package/dist/commands/replay-cmd.js +0 -184
- package/dist/commands/review.d.ts +0 -3
- package/dist/commands/review.js +0 -443
- package/dist/commands/risk.d.ts +0 -47
- package/dist/commands/risk.js +0 -158
- package/dist/commands/social.d.ts +0 -43
- package/dist/commands/social.js +0 -318
- package/dist/commands/soul-wizard.d.ts +0 -29
- package/dist/commands/soul-wizard.js +0 -155
- package/dist/commands/strategy-cmd.d.ts +0 -44
- package/dist/commands/strategy-cmd.js +0 -340
- package/dist/commands/subscribe.d.ts +0 -78
- package/dist/commands/subscribe.js +0 -552
- package/dist/commands/suggestions-cmd.d.ts +0 -24
- package/dist/commands/suggestions-cmd.js +0 -148
- package/dist/commands/thesis-cmd.d.ts +0 -3
- package/dist/commands/thesis-cmd.js +0 -129
- package/dist/commands/trader.d.ts +0 -30
- package/dist/commands/trader.js +0 -971
- package/dist/commands/watch.d.ts +0 -16
- package/dist/commands/watch.js +0 -104
- package/dist/commands/whoami.d.ts +0 -14
- package/dist/commands/whoami.js +0 -105
package/dist/commands/review.js
DELETED
|
@@ -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
|
package/dist/commands/risk.d.ts
DELETED
|
@@ -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
|
package/dist/commands/risk.js
DELETED
|
@@ -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
|