@quantbrasil/cli 0.1.0-beta.1 → 0.1.0-beta.10

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 (70) hide show
  1. package/README.md +63 -11
  2. package/dist/cli/client.js +4 -0
  3. package/dist/cli/index.d.ts +12 -4
  4. package/dist/cli/index.d.ts.map +1 -1
  5. package/dist/cli/index.js +64 -14
  6. package/dist/cli/prompt.d.ts +1 -0
  7. package/dist/cli/prompt.d.ts.map +1 -1
  8. package/dist/cli/prompt.js +17 -0
  9. package/dist/cli/skills.d.ts +9 -0
  10. package/dist/cli/skills.d.ts.map +1 -1
  11. package/dist/cli/skills.js +68 -4
  12. package/dist/commands/auth.d.ts +18 -0
  13. package/dist/commands/auth.d.ts.map +1 -1
  14. package/dist/commands/auth.js +49 -0
  15. package/dist/commands/market.d.ts +1 -0
  16. package/dist/commands/market.d.ts.map +1 -1
  17. package/dist/commands/market.js +17 -1
  18. package/dist/commands/portfolios.d.ts +148 -8
  19. package/dist/commands/portfolios.d.ts.map +1 -1
  20. package/dist/commands/portfolios.js +557 -55
  21. package/dist/commands/rankings.d.ts +82 -0
  22. package/dist/commands/rankings.d.ts.map +1 -0
  23. package/dist/commands/rankings.js +235 -0
  24. package/dist/commands/screening.d.ts +120 -0
  25. package/dist/commands/screening.d.ts.map +1 -0
  26. package/dist/commands/screening.js +361 -0
  27. package/dist/commands/skills.js +7 -7
  28. package/dist/commands/update.d.ts +23 -0
  29. package/dist/commands/update.d.ts.map +1 -0
  30. package/dist/commands/update.js +209 -0
  31. package/dist/index.d.ts +3 -1
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +3 -1
  34. package/dist/vendor/core/capabilities/index.d.ts +2 -1
  35. package/dist/vendor/core/capabilities/index.d.ts.map +1 -1
  36. package/dist/vendor/core/capabilities/index.js +2 -1
  37. package/dist/vendor/core/capabilities/market.d.ts +9 -1
  38. package/dist/vendor/core/capabilities/market.d.ts.map +1 -1
  39. package/dist/vendor/core/capabilities/market.js +10 -0
  40. package/dist/vendor/core/capabilities/portfolios.d.ts +452 -56
  41. package/dist/vendor/core/capabilities/portfolios.d.ts.map +1 -1
  42. package/dist/vendor/core/capabilities/portfolios.js +434 -116
  43. package/dist/vendor/core/capabilities/rankings.d.ts +83 -0
  44. package/dist/vendor/core/capabilities/rankings.d.ts.map +1 -0
  45. package/dist/vendor/core/capabilities/rankings.js +95 -0
  46. package/dist/vendor/core/capabilities/registry.d.ts +1279 -413
  47. package/dist/vendor/core/capabilities/registry.d.ts.map +1 -1
  48. package/dist/vendor/core/capabilities/registry.js +4 -2
  49. package/dist/vendor/core/capabilities/screening.d.ts +136 -0
  50. package/dist/vendor/core/capabilities/screening.d.ts.map +1 -0
  51. package/dist/vendor/core/capabilities/screening.js +155 -0
  52. package/dist/vendor/core/capabilities/types.d.ts +1 -1
  53. package/dist/vendor/core/capabilities/types.d.ts.map +1 -1
  54. package/package.json +3 -3
  55. package/skills/quantbrasil/SKILL.md +28 -11
  56. package/skills/quantbrasil/references/cli.md +96 -20
  57. package/skills/quantbrasil/references/costs.md +9 -4
  58. package/skills/quantbrasil/references/errors.md +16 -5
  59. package/skills/quantbrasil/references/portfolios.md +114 -0
  60. package/skills/quantbrasil/references/quality-eval-queries.json +127 -0
  61. package/skills/quantbrasil/references/rankings.md +62 -0
  62. package/skills/quantbrasil/references/screening.md +212 -0
  63. package/skills/quantbrasil/references/unsupported.md +7 -2
  64. package/skills/quantbrasil/references/workflows.md +95 -23
  65. package/dist/commands/analytics.d.ts +0 -131
  66. package/dist/commands/analytics.d.ts.map +0 -1
  67. package/dist/commands/analytics.js +0 -291
  68. package/dist/vendor/core/capabilities/analytics.d.ts +0 -187
  69. package/dist/vendor/core/capabilities/analytics.d.ts.map +0 -1
  70. package/dist/vendor/core/capabilities/analytics.js +0 -214
@@ -1,15 +1,27 @@
1
1
  # Workflows
2
2
 
3
+ ## Choose command from user intent
4
+
5
+ - asset price, quote, or daily move → `market price`
6
+ - supported assets, tickers, or market universe → `market assets`
7
+ - asset performance, technicals, risk, fundamentals, or ranking → `assets overview`
8
+ - current ordered asset lists such as Magic Formula, momentum, or user rankings → `rankings list`, then `rankings current`
9
+ - watchlist details or changes → `watchlists ...`
10
+ - holding details or changes → `holdings ...`
11
+ - holding return, beta, risk, VaR, or comparison → `holdings historical-return|beta|var`
12
+ - indicator screening over saved asset sets → `screening universes`, `screening indicators` when needed, then `screening run`
13
+
3
14
  ## Find supported ticker, then get price
4
15
 
5
16
  Use this when user gives company theme, partial name, or market universe question.
6
17
 
7
18
  ```bash
8
- quantbrasil market assets --type B3
19
+ quantbrasil market assets --search PETR
9
20
  quantbrasil market price PETR4
10
21
  ```
11
22
 
12
23
  If exact ticker already known, skip discovery and go straight to `market price`.
24
+ Use `--type` when the user asks for a whole supported universe.
13
25
 
14
26
  ## Get price on specific date
15
27
 
@@ -26,6 +38,9 @@ Use absolute ISO dates.
26
38
  - need richer analysis for one asset → `assets overview`
27
39
 
28
40
  Do not jump to `assets overview` if price-only answer is enough.
41
+ Do not use generic web or finance search for QuantBrasil-supported market data
42
+ unless the CLI is unavailable, the requested data is outside the supported
43
+ surface, or the user explicitly asks for an external source.
29
44
 
30
45
  ## Cost-aware `assets overview` section choice
31
46
 
@@ -48,52 +63,109 @@ Guidance:
48
63
 
49
64
  Avoid asking for every section unless user clearly wants full report.
50
65
 
51
- ## Analyze saved portfolio
66
+ ## Get current ranked assets
67
+
68
+ Use this when the user asks for an ordered list such as top Magic Formula,
69
+ momentum leaders, dividend-yield leaders, Momentum Double, or a saved user
70
+ ranking.
71
+
72
+ ```bash
73
+ quantbrasil rankings list
74
+ quantbrasil rankings current --system momentum-90d --top 20
75
+ quantbrasil rankings current --system magic-formula --top 10
76
+ quantbrasil rankings current --id 123 --top 20
77
+ ```
78
+
79
+ Rules:
80
+
81
+ - rankings answer order-first questions
82
+ - system rankings use their slug from `rankings list`
83
+ - user rankings use their numeric id from `rankings list`
84
+ - use exactly one selector: `--system <slug>` or `--id <id>`
85
+ - do not call user rankings "factor:" in command syntax; ranking fatorial is a saved user ranking
86
+ - use `--json` if another step needs to parse tickers, ranks, or ordering metrics
87
+ - do not route ranking-first questions through screening unless the user asks for indicator conditions
52
88
 
53
- 1. Discover portfolio id.
54
- 2. Inspect holdings if needed.
55
- 3. Run analytics.
89
+ ## Analyze saved holding
90
+
91
+ 1. Discover holding id.
92
+ 2. Inspect the holding if needed.
93
+ 3. Run the holding metric.
56
94
 
57
95
  ```bash
58
- quantbrasil portfolios list
59
- quantbrasil portfolios get 93
60
- quantbrasil analytics historical-return --portfolio 93 --from 2025-01-01 --to 2026-01-01
61
- quantbrasil analytics beta --portfolio 93 --years 1
62
- quantbrasil analytics var --portfolio 93 --years 1 --confidence 95
96
+ quantbrasil holdings list
97
+ quantbrasil holdings get 93
98
+ quantbrasil holdings historical-return 93 --period 1y
99
+ quantbrasil holdings beta 93 --years 1
100
+ quantbrasil holdings var 93 --years 1 --confidence 95
63
101
  ```
64
102
 
65
- ## Analyze ad-hoc basket
103
+ ## Run indicator screening
104
+
105
+ 1. Discover available universes.
106
+ 2. Discover available indicators when the filter JSON is not already known.
107
+ 3. Pick exactly one system portfolio, watchlist, or holding.
108
+ 4. Run screening with a full `ScreenerRequest` JSON file.
109
+
110
+ ```bash
111
+ quantbrasil screening universes
112
+ quantbrasil screening indicators
113
+ quantbrasil screening run --system acoes-mais-liquidas --query-file ./screening.json
114
+ quantbrasil screening run --watchlist 93 --query-file ./screening.json --limit 25
115
+ quantbrasil screening run --holding 182 --query-file ./screening.json --sort ticker
116
+ ```
117
+
118
+ Rules:
119
+
120
+ - screening universes are portfolio-backed only: system portfolios, watchlists, or holdings
121
+ - a holding used as a screening universe contributes only its asset set
122
+ - do not invent ad-hoc ticker-list screening commands
123
+ - load `references/screening.md` for IFR/RSI examples and full JSON payloads
124
+ - use `--json` when the result will be parsed by an agent or script
125
+
126
+ ## Analyze a theoretical composition
66
127
 
67
- Use repeated `--asset TICKER[:WEIGHT_PCT]`.
128
+ Create a holding with target weights, then run holding metrics on its id.
68
129
 
69
130
  Rules:
70
131
 
71
- - if weights omitted, backend may assume equal weights
72
- - negative weights allowed for hedge / short exposure
132
+ - `holdings create` returns the holding id
133
+ - use `holdings set-targets` to replace target weights later
134
+ - use `holdings set-positions` for actual share/unit/coin quantities
73
135
  - `beta` only accepts `--years 1|3|5`
74
136
  - `var --confidence` takes percent like `95` or `99`
75
137
 
76
138
  Example:
77
139
 
78
140
  ```bash
79
- quantbrasil analytics historical-return --asset VALE3:50 --asset PRIO3:50 --from 2025-01-01 --to 2026-01-01
80
- quantbrasil analytics beta --asset VALE3:120 --asset WINFUT:-20 --years 1
81
- quantbrasil analytics var --asset VALE3:120 --asset WINFUT:-20 --years 1 --confidence 99
141
+ quantbrasil holdings create "Teórica energia" --target VALE3:50 --target PRIO3:50
142
+ quantbrasil holdings historical-return 93 --from 2025-01-01 --to 2026-01-01
143
+ quantbrasil holdings beta 93 --years 1
144
+ quantbrasil holdings var 93 --years 1 --confidence 99
82
145
  ```
83
146
 
84
- ## Modify saved portfolio
147
+ ## Modify watchlists and holdings
85
148
 
86
149
  Use explicit mutation commands. Do not retry failed mutations automatically.
87
150
 
88
151
  ```bash
89
- quantbrasil portfolios create "Dividendos"
90
- quantbrasil portfolios rename 93 "Longo Prazo"
91
- quantbrasil portfolios add-assets 93 PETR4 VALE3
92
- quantbrasil portfolios remove-assets 93 PETR4
152
+ quantbrasil watchlists create "Dividendos"
153
+ quantbrasil watchlists rename 93 "Longo Prazo"
154
+ quantbrasil watchlists add-assets 93 PETR4 VALE3
155
+ quantbrasil watchlists remove-assets 93 PETR4
156
+
157
+ quantbrasil holdings create "Longo Prazo"
158
+ quantbrasil holdings create "Longo Prazo" --target PETR4:50 --target VALE3:50
159
+ quantbrasil holdings create "Carteira Real" --mode position
160
+ quantbrasil holdings rename 182 "Carteira Real"
161
+ quantbrasil holdings set-targets 182 PETR4:50 VALE3:50
162
+ quantbrasil holdings set-positions 182 PRIO3:1600 BTC-USD:0.25 QQQ:10
93
163
  ```
94
164
 
95
165
  Rules:
96
166
 
97
- - use `portfolios list` first if user names a portfolio instead of giving id
167
+ - use `watchlists list` or `holdings list` first if the user names a saved object instead of giving id
98
168
  - confirm destructive intent before removing assets when user request is ambiguous
169
+ - positions are quantity-only in the public CLI; do not pass monetary values as positions
170
+ - target weights and positions are independent; updating positions does not clear target weights
99
171
  - use `--json` only when structured output is needed
@@ -1,131 +0,0 @@
1
- import { Command } from "commander";
2
- import type { JsonValue } from "../vendor/core/index.js";
3
- import { type CliInvokeContext } from "../cli/client.js";
4
- import { type TerminalWriter } from "../cli/terminal.js";
5
- export interface AnalyticsCommandIO {
6
- stdout: TerminalWriter;
7
- }
8
- export interface AnalyticsCommandContext extends CliInvokeContext {
9
- io?: AnalyticsCommandIO;
10
- }
11
- export interface HistoricalReturnCommandOptions {
12
- portfolio?: number;
13
- asset?: string[];
14
- from: string;
15
- to: string;
16
- json?: boolean;
17
- }
18
- export interface BetaCommandOptions {
19
- portfolio?: number;
20
- asset?: string[];
21
- years?: string;
22
- json?: boolean;
23
- }
24
- export interface VarCommandOptions {
25
- portfolio?: number;
26
- asset?: string[];
27
- years?: string;
28
- confidence?: string;
29
- json?: boolean;
30
- }
31
- export interface HistoricalReturnHolding {
32
- [key: string]: JsonValue;
33
- ticker: string;
34
- weight_pct: number;
35
- total_return: number;
36
- contribution: number;
37
- }
38
- export interface HistoricalReturnResponse {
39
- [key: string]: JsonValue;
40
- source_type: "saved_portfolio" | "ad_hoc";
41
- portfolio_id: number | null;
42
- portfolio_name: string | null;
43
- requested_start_date: string;
44
- requested_end_date: string;
45
- effective_start_date: string;
46
- effective_end_date: string;
47
- total_return: number;
48
- annualized_return: number;
49
- max_drawdown: number;
50
- daily_volatility: number;
51
- annualized_volatility: number;
52
- sharpe_ratio: number;
53
- ibov_return: number;
54
- cdi_return: number;
55
- ipca_return: number;
56
- holdings: HistoricalReturnHolding[];
57
- summary_markdown: string;
58
- assumptions: string[];
59
- warnings: string[];
60
- }
61
- export interface BetaHolding {
62
- [key: string]: JsonValue;
63
- ticker: string;
64
- weight_pct: number;
65
- beta: number | null;
66
- weighted_beta: number | null;
67
- correlation: number | null;
68
- asset_daily_volatility: number | null;
69
- benchmark_daily_volatility: number | null;
70
- last_updated: string | null;
71
- }
72
- export interface BetaResponse {
73
- [key: string]: JsonValue;
74
- source_type: "saved_portfolio" | "ad_hoc";
75
- portfolio_id: number | null;
76
- portfolio_name: string | null;
77
- benchmark: "IBOV";
78
- lookback_years: number;
79
- beta: number;
80
- correlation: number;
81
- daily_volatility: number;
82
- annualized_volatility: number;
83
- long_exposure_pct: number;
84
- short_exposure_pct: number;
85
- total_weight_pct: number;
86
- holdings: BetaHolding[];
87
- summary_markdown: string;
88
- assumptions: string[];
89
- warnings: string[];
90
- }
91
- export interface VarHolding {
92
- [key: string]: JsonValue;
93
- ticker: string;
94
- weight_pct: number;
95
- }
96
- export interface VarResponse {
97
- [key: string]: JsonValue;
98
- source_type: "saved_portfolio" | "ad_hoc";
99
- portfolio_id: number | null;
100
- portfolio_name: string | null;
101
- lookback_years: number;
102
- confidence_pct: number;
103
- time_horizon: "1d";
104
- var: number;
105
- long_exposure_pct: number;
106
- short_exposure_pct: number;
107
- total_weight_pct: number;
108
- holdings: VarHolding[];
109
- histogram_data: JsonValue[];
110
- summary_markdown: string;
111
- assumptions: string[];
112
- warnings: string[];
113
- }
114
- interface ParsedAssetInput {
115
- [key: string]: JsonValue;
116
- ticker: string;
117
- weight_pct: number | null;
118
- }
119
- export declare function registerAnalyticsCommands(program: Command, context?: AnalyticsCommandContext): void;
120
- export declare function runHistoricalReturnCommand(options: HistoricalReturnCommandOptions, context?: AnalyticsCommandContext): Promise<void>;
121
- export declare function runBetaCommand(options: BetaCommandOptions, context?: AnalyticsCommandContext): Promise<void>;
122
- export declare function runVarCommand(options: VarCommandOptions, context?: AnalyticsCommandContext): Promise<void>;
123
- export declare function buildHistoricalReturnInput(options: HistoricalReturnCommandOptions): JsonValue;
124
- export declare function buildBetaInput(options: BetaCommandOptions): JsonValue;
125
- export declare function buildVarInput(options: VarCommandOptions): JsonValue;
126
- export declare function formatHistoricalReturnHuman(data: HistoricalReturnResponse, theme?: import("../cli/terminal.js").TerminalTheme): string;
127
- export declare function formatBetaHuman(data: BetaResponse, theme?: import("../cli/terminal.js").TerminalTheme): string;
128
- export declare function formatVarHuman(data: VarResponse, theme?: import("../cli/terminal.js").TerminalTheme): string;
129
- export declare function parseAssetSpec(rawAsset: string): ParsedAssetInput;
130
- export {};
131
- //# sourceMappingURL=analytics.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../../src/commands/analytics.ts"],"names":[],"mappings":"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"}
@@ -1,291 +0,0 @@
1
- import { invokeCliCapability } from "../cli/client.js";
2
- import { createCliValidationError } from "../cli/errors.js";
3
- import { createTerminalTheme } from "../cli/terminal.js";
4
- export function registerAnalyticsCommands(program, context = {}) {
5
- const analyticsCommand = program
6
- .command("analytics")
7
- .description("Read portfolio analytics operations");
8
- analyticsCommand
9
- .command("historical-return")
10
- .description("Calculate historical return for a saved portfolio or basket")
11
- .option("--portfolio <id>", "Saved portfolio id", parsePositiveIntegerOption)
12
- .option("--asset <asset>", "Repeatable asset input in TICKER[:WEIGHT_PCT] form", collectStringOption, [])
13
- .requiredOption("--from <date>", "Inclusive start date in ISO format")
14
- .requiredOption("--to <date>", "Inclusive end date in ISO format")
15
- .option("--json", "Show JSON output")
16
- .action(async (options) => {
17
- await runHistoricalReturnCommand(options, context);
18
- });
19
- analyticsCommand
20
- .command("beta")
21
- .description("Calculate portfolio beta against IBOV")
22
- .option("--portfolio <id>", "Saved portfolio id", parsePositiveIntegerOption)
23
- .option("--asset <asset>", "Repeatable asset input in TICKER[:WEIGHT_PCT] form", collectStringOption, [])
24
- .option("--years <years>", "Lookback window in years", "1")
25
- .option("--json", "Show JSON output")
26
- .action(async (options) => {
27
- await runBetaCommand(options, context);
28
- });
29
- analyticsCommand
30
- .command("var")
31
- .description("Calculate one-day Value-at-Risk for a portfolio or basket")
32
- .option("--portfolio <id>", "Saved portfolio id", parsePositiveIntegerOption)
33
- .option("--asset <asset>", "Repeatable asset input in TICKER[:WEIGHT_PCT] form", collectStringOption, [])
34
- .option("--years <years>", "Lookback window in years", "1")
35
- .option("--confidence <confidence>", "Confidence level in percent", "95")
36
- .option("--json", "Show JSON output")
37
- .action(async (options) => {
38
- await runVarCommand(options, context);
39
- });
40
- }
41
- export async function runHistoricalReturnCommand(options, context = {}) {
42
- const stdout = context.io?.stdout ?? process.stdout;
43
- const theme = createTerminalTheme(stdout, context.env ?? process.env);
44
- const response = await invokeCliCapability({
45
- capability: "analytics.historical-return",
46
- input: buildHistoricalReturnInput(options),
47
- env: context.env,
48
- fetch: context.fetch,
49
- });
50
- if (options.json) {
51
- stdout.write(`${JSON.stringify(response.data, null, 2)}\n`);
52
- return;
53
- }
54
- stdout.write(`${formatHistoricalReturnHuman(response.data, theme)}\n`);
55
- }
56
- export async function runBetaCommand(options, context = {}) {
57
- const stdout = context.io?.stdout ?? process.stdout;
58
- const theme = createTerminalTheme(stdout, context.env ?? process.env);
59
- const response = await invokeCliCapability({
60
- capability: "analytics.beta",
61
- input: buildBetaInput(options),
62
- env: context.env,
63
- fetch: context.fetch,
64
- });
65
- if (options.json) {
66
- stdout.write(`${JSON.stringify(response.data, null, 2)}\n`);
67
- return;
68
- }
69
- stdout.write(`${formatBetaHuman(response.data, theme)}\n`);
70
- }
71
- export async function runVarCommand(options, context = {}) {
72
- const stdout = context.io?.stdout ?? process.stdout;
73
- const theme = createTerminalTheme(stdout, context.env ?? process.env);
74
- const response = await invokeCliCapability({
75
- capability: "analytics.var",
76
- input: buildVarInput(options),
77
- env: context.env,
78
- fetch: context.fetch,
79
- });
80
- if (options.json) {
81
- stdout.write(`${JSON.stringify(response.data, null, 2)}\n`);
82
- return;
83
- }
84
- stdout.write(`${formatVarHuman(response.data, theme)}\n`);
85
- }
86
- export function buildHistoricalReturnInput(options) {
87
- const source = resolveAnalyticsSource(options.portfolio, options.asset ?? []);
88
- return {
89
- ...source,
90
- start_date: options.from,
91
- end_date: options.to,
92
- };
93
- }
94
- export function buildBetaInput(options) {
95
- const source = resolveAnalyticsSource(options.portfolio, options.asset ?? []);
96
- return {
97
- ...source,
98
- years: parseYearsOption(options.years ?? "1"),
99
- };
100
- }
101
- export function buildVarInput(options) {
102
- const source = resolveAnalyticsSource(options.portfolio, options.asset ?? []);
103
- return {
104
- ...source,
105
- years: parsePositiveIntegerOption(options.years ?? "1"),
106
- confidence_pct: parseNumberOption(options.confidence ?? "95", "confidence"),
107
- };
108
- }
109
- export function formatHistoricalReturnHuman(data, theme = createTerminalTheme(process.stdout)) {
110
- const lines = [
111
- theme.label("Historical return"),
112
- "",
113
- formatSourceLabel(data, theme),
114
- `${theme.label("Period:")} ${data.effective_start_date} → ${data.effective_end_date}`,
115
- `${theme.label("Requested:")} ${data.requested_start_date} → ${data.requested_end_date}`,
116
- "",
117
- `${theme.label("Portfolio return:")} ${formatPercentValue(data.total_return)}`,
118
- `${theme.label("Annualized return:")} ${formatPercentValue(data.annualized_return)}`,
119
- `${theme.label("Max drawdown:")} ${formatPercentValue(data.max_drawdown)}`,
120
- `${theme.label("Daily volatility:")} ${formatPercentValue(data.daily_volatility)}`,
121
- `${theme.label("Annualized volatility:")} ${formatPercentValue(data.annualized_volatility)}`,
122
- `${theme.label("Sharpe ratio:")} ${formatNumber(data.sharpe_ratio)}`,
123
- "",
124
- theme.label("Benchmarks"),
125
- ` IBOV: ${formatPercentValue(data.ibov_return)}`,
126
- ` CDI: ${formatPercentValue(data.cdi_return)}`,
127
- ` IPCA: ${formatPercentValue(data.ipca_return)}`,
128
- "",
129
- theme.label("Holdings"),
130
- ...data.holdings.map(item => ` - ${item.ticker}: weight=${formatPercentDirect(item.weight_pct)}, return=${formatPercentValue(item.total_return)}, contribution=${formatPercentValue(item.contribution)}`),
131
- ];
132
- pushNotes(lines, "Assumptions", data.assumptions, theme);
133
- pushNotes(lines, "Warnings", data.warnings, theme);
134
- return lines.join("\n");
135
- }
136
- export function formatBetaHuman(data, theme = createTerminalTheme(process.stdout)) {
137
- const lines = [
138
- theme.label("Portfolio beta"),
139
- "",
140
- formatSourceLabel(data, theme),
141
- `${theme.label("Benchmark:")} ${data.benchmark}`,
142
- `${theme.label("Lookback:")} ${data.lookback_years}Y`,
143
- "",
144
- `${theme.label("Beta:")} ${formatNumber(data.beta)}`,
145
- `${theme.label("Correlation:")} ${formatNumber(data.correlation)}`,
146
- `${theme.label("Daily volatility:")} ${formatPercentValue(data.daily_volatility)}`,
147
- `${theme.label("Annualized volatility:")} ${formatPercentValue(data.annualized_volatility)}`,
148
- `${theme.label("Long exposure:")} ${formatPercentDirect(data.long_exposure_pct)}`,
149
- `${theme.label("Short exposure:")} ${formatPercentDirect(data.short_exposure_pct)}`,
150
- `${theme.label("Net exposure:")} ${formatPercentDirect(data.total_weight_pct)}`,
151
- "",
152
- theme.label("Holdings"),
153
- ...data.holdings.map(item => ` - ${item.ticker}: weight=${formatPercentDirect(item.weight_pct)}, beta=${formatNullableNumber(item.beta)}, weighted=${formatNullableNumber(item.weighted_beta)}, corr=${formatNullableNumber(item.correlation)}`),
154
- ];
155
- pushNotes(lines, "Assumptions", data.assumptions, theme);
156
- pushNotes(lines, "Warnings", data.warnings, theme);
157
- return lines.join("\n");
158
- }
159
- export function formatVarHuman(data, theme = createTerminalTheme(process.stdout)) {
160
- const lines = [
161
- theme.label("Portfolio VaR"),
162
- "",
163
- formatSourceLabel(data, theme),
164
- `${theme.label("Lookback:")} ${data.lookback_years}Y`,
165
- `${theme.label("Confidence:")} ${formatPercentDirect(data.confidence_pct)}`,
166
- `${theme.label("Horizon:")} ${data.time_horizon}`,
167
- "",
168
- `${theme.label("VaR:")} ${formatPercentValue(data.var)}`,
169
- `${theme.label("Long exposure:")} ${formatPercentDirect(data.long_exposure_pct)}`,
170
- `${theme.label("Short exposure:")} ${formatPercentDirect(data.short_exposure_pct)}`,
171
- `${theme.label("Net exposure:")} ${formatPercentDirect(data.total_weight_pct)}`,
172
- "",
173
- theme.label("Holdings"),
174
- ...data.holdings.map(item => ` - ${item.ticker}: weight=${formatPercentDirect(item.weight_pct)}`),
175
- "",
176
- `${theme.label("Histogram bins:")} ${formatInteger(data.histogram_data.length)}`,
177
- ];
178
- pushNotes(lines, "Assumptions", data.assumptions, theme);
179
- pushNotes(lines, "Warnings", data.warnings, theme);
180
- return lines.join("\n");
181
- }
182
- export function parseAssetSpec(rawAsset) {
183
- const normalized = rawAsset.trim();
184
- if (!normalized) {
185
- throw createCliValidationError("Asset input cannot be empty.");
186
- }
187
- const parts = normalized.split(":");
188
- if (parts.length > 2) {
189
- throw createCliValidationError(`Invalid asset input "${rawAsset}". Use TICKER[:WEIGHT_PCT].`);
190
- }
191
- const [rawTicker, rawWeight] = parts;
192
- const ticker = rawTicker?.trim().toUpperCase();
193
- if (!ticker) {
194
- throw createCliValidationError(`Invalid asset input "${rawAsset}". Use TICKER[:WEIGHT_PCT].`);
195
- }
196
- if (rawWeight === undefined) {
197
- return { ticker, weight_pct: null };
198
- }
199
- const weight_pct = Number(rawWeight.trim());
200
- if (!Number.isFinite(weight_pct)) {
201
- throw createCliValidationError(`Invalid asset weight in "${rawAsset}". Use TICKER[:WEIGHT_PCT].`);
202
- }
203
- return { ticker, weight_pct };
204
- }
205
- function resolveAnalyticsSource(portfolioId, assets) {
206
- const hasPortfolio = portfolioId !== undefined;
207
- const hasAssets = assets.length > 0;
208
- if (hasPortfolio === hasAssets) {
209
- throw createCliValidationError("Use exactly one source: --portfolio <id> or one or more --asset TICKER[:WEIGHT_PCT].");
210
- }
211
- if (hasPortfolio) {
212
- return { portfolio_id: portfolioId };
213
- }
214
- return {
215
- assets: assets.map(parseAssetSpec),
216
- };
217
- }
218
- function collectStringOption(value, previous) {
219
- previous.push(value);
220
- return previous;
221
- }
222
- function parsePositiveIntegerOption(value) {
223
- const normalized = value.trim();
224
- if (!/^\d+$/.test(normalized)) {
225
- throw createCliValidationError("Value must be a positive integer.");
226
- }
227
- const parsed = Number(normalized);
228
- if (!Number.isSafeInteger(parsed) || parsed <= 0) {
229
- throw createCliValidationError("Value must be a positive integer.");
230
- }
231
- return parsed;
232
- }
233
- function parseYearsOption(value) {
234
- const parsed = parsePositiveIntegerOption(value);
235
- if (![1, 3, 5].includes(parsed)) {
236
- throw createCliValidationError("Years must be one of: 1, 3, 5.");
237
- }
238
- return parsed;
239
- }
240
- function parseNumberOption(value, fieldName) {
241
- const parsed = Number(value.trim());
242
- if (!Number.isFinite(parsed)) {
243
- throw createCliValidationError(`${fieldName} must be a number.`);
244
- }
245
- return parsed;
246
- }
247
- function formatSourceLabel(data, theme) {
248
- if (data.source_type === "saved_portfolio" && data.portfolio_id !== null) {
249
- const name = data.portfolio_name ? ` · ${data.portfolio_name}` : "";
250
- return `${theme.bold(String(data.portfolio_id))}${theme.dim(name)}`;
251
- }
252
- return theme.bold("Ad-hoc basket");
253
- }
254
- function pushNotes(lines, title, items, theme) {
255
- if (items.length === 0) {
256
- return;
257
- }
258
- lines.push("");
259
- lines.push(theme.label(title));
260
- for (const item of items) {
261
- lines.push(` - ${item}`);
262
- }
263
- }
264
- function formatNumber(value) {
265
- return new Intl.NumberFormat("en-US", {
266
- maximumFractionDigits: 4,
267
- }).format(value);
268
- }
269
- function formatInteger(value) {
270
- return new Intl.NumberFormat("en-US", {
271
- maximumFractionDigits: 0,
272
- }).format(value);
273
- }
274
- function formatNullableNumber(value) {
275
- if (value === null) {
276
- return "n/a";
277
- }
278
- return formatNumber(value);
279
- }
280
- function formatPercentValue(value) {
281
- return `${new Intl.NumberFormat("en-US", {
282
- maximumFractionDigits: 2,
283
- minimumFractionDigits: 2,
284
- }).format(value * 100)}%`;
285
- }
286
- function formatPercentDirect(value) {
287
- return `${new Intl.NumberFormat("en-US", {
288
- maximumFractionDigits: 2,
289
- minimumFractionDigits: 2,
290
- }).format(value)}%`;
291
- }