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

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 (77) hide show
  1. package/README.md +64 -11
  2. package/dist/cli/client.js +4 -0
  3. package/dist/cli/index.d.ts +14 -4
  4. package/dist/cli/index.d.ts.map +1 -1
  5. package/dist/cli/index.js +72 -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/cointegration.d.ts +52 -0
  16. package/dist/commands/cointegration.d.ts.map +1 -0
  17. package/dist/commands/cointegration.js +118 -0
  18. package/dist/commands/market.d.ts +1 -0
  19. package/dist/commands/market.d.ts.map +1 -1
  20. package/dist/commands/market.js +17 -1
  21. package/dist/commands/portfolios.d.ts +148 -8
  22. package/dist/commands/portfolios.d.ts.map +1 -1
  23. package/dist/commands/portfolios.js +557 -55
  24. package/dist/commands/rankings.d.ts +82 -0
  25. package/dist/commands/rankings.d.ts.map +1 -0
  26. package/dist/commands/rankings.js +235 -0
  27. package/dist/commands/screening.d.ts +120 -0
  28. package/dist/commands/screening.d.ts.map +1 -0
  29. package/dist/commands/screening.js +361 -0
  30. package/dist/commands/skills.js +7 -7
  31. package/dist/commands/update.d.ts +23 -0
  32. package/dist/commands/update.d.ts.map +1 -0
  33. package/dist/commands/update.js +209 -0
  34. package/dist/index.d.ts +3 -1
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +3 -1
  37. package/dist/vendor/core/capabilities/cointegration.d.ts +52 -0
  38. package/dist/vendor/core/capabilities/cointegration.d.ts.map +1 -0
  39. package/dist/vendor/core/capabilities/cointegration.js +63 -0
  40. package/dist/vendor/core/capabilities/index.d.ts +3 -1
  41. package/dist/vendor/core/capabilities/index.d.ts.map +1 -1
  42. package/dist/vendor/core/capabilities/index.js +3 -1
  43. package/dist/vendor/core/capabilities/market.d.ts +9 -1
  44. package/dist/vendor/core/capabilities/market.d.ts.map +1 -1
  45. package/dist/vendor/core/capabilities/market.js +10 -0
  46. package/dist/vendor/core/capabilities/portfolios.d.ts +452 -56
  47. package/dist/vendor/core/capabilities/portfolios.d.ts.map +1 -1
  48. package/dist/vendor/core/capabilities/portfolios.js +434 -116
  49. package/dist/vendor/core/capabilities/rankings.d.ts +83 -0
  50. package/dist/vendor/core/capabilities/rankings.d.ts.map +1 -0
  51. package/dist/vendor/core/capabilities/rankings.js +96 -0
  52. package/dist/vendor/core/capabilities/registry.d.ts +1380 -414
  53. package/dist/vendor/core/capabilities/registry.d.ts.map +1 -1
  54. package/dist/vendor/core/capabilities/registry.js +6 -2
  55. package/dist/vendor/core/capabilities/screening.d.ts +136 -0
  56. package/dist/vendor/core/capabilities/screening.d.ts.map +1 -0
  57. package/dist/vendor/core/capabilities/screening.js +155 -0
  58. package/dist/vendor/core/capabilities/types.d.ts +1 -1
  59. package/dist/vendor/core/capabilities/types.d.ts.map +1 -1
  60. package/package.json +3 -3
  61. package/skills/quantbrasil/SKILL.md +31 -11
  62. package/skills/quantbrasil/references/cli.md +112 -19
  63. package/skills/quantbrasil/references/cointegration.md +40 -0
  64. package/skills/quantbrasil/references/costs.md +10 -4
  65. package/skills/quantbrasil/references/errors.md +16 -5
  66. package/skills/quantbrasil/references/portfolios.md +114 -0
  67. package/skills/quantbrasil/references/quality-eval-queries.json +147 -0
  68. package/skills/quantbrasil/references/rankings.md +64 -0
  69. package/skills/quantbrasil/references/screening.md +212 -0
  70. package/skills/quantbrasil/references/unsupported.md +9 -2
  71. package/skills/quantbrasil/references/workflows.md +117 -23
  72. package/dist/commands/analytics.d.ts +0 -131
  73. package/dist/commands/analytics.d.ts.map +0 -1
  74. package/dist/commands/analytics.js +0 -291
  75. package/dist/vendor/core/capabilities/analytics.d.ts +0 -187
  76. package/dist/vendor/core/capabilities/analytics.d.ts.map +0 -1
  77. package/dist/vendor/core/capabilities/analytics.js +0 -214
@@ -1,15 +1,28 @@
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, Low Risk, 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
+ - cointegration or Long & Short between two explicit assets → `cointegration pair`
14
+
3
15
  ## Find supported ticker, then get price
4
16
 
5
17
  Use this when user gives company theme, partial name, or market universe question.
6
18
 
7
19
  ```bash
8
- quantbrasil market assets --type B3
20
+ quantbrasil market assets --search PETR
9
21
  quantbrasil market price PETR4
10
22
  ```
11
23
 
12
24
  If exact ticker already known, skip discovery and go straight to `market price`.
25
+ Use `--type` when the user asks for a whole supported universe.
13
26
 
14
27
  ## Get price on specific date
15
28
 
@@ -26,6 +39,9 @@ Use absolute ISO dates.
26
39
  - need richer analysis for one asset → `assets overview`
27
40
 
28
41
  Do not jump to `assets overview` if price-only answer is enough.
42
+ Do not use generic web or finance search for QuantBrasil-supported market data
43
+ unless the CLI is unavailable, the requested data is outside the supported
44
+ surface, or the user explicitly asks for an external source.
29
45
 
30
46
  ## Cost-aware `assets overview` section choice
31
47
 
@@ -48,52 +64,130 @@ Guidance:
48
64
 
49
65
  Avoid asking for every section unless user clearly wants full report.
50
66
 
51
- ## Analyze saved portfolio
67
+ ## Get current ranked assets
52
68
 
53
- 1. Discover portfolio id.
54
- 2. Inspect holdings if needed.
55
- 3. Run analytics.
69
+ Use this when the user asks for an ordered list such as top Magic Formula,
70
+ momentum leaders, dividend-yield leaders, Low Risk, Momentum Double, or a saved
71
+ user ranking.
56
72
 
57
73
  ```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
74
+ quantbrasil rankings list
75
+ quantbrasil rankings current --system momentum-90d --top 20
76
+ quantbrasil rankings current --system magic-formula --top 10
77
+ quantbrasil rankings current --system low-risk --top 30
78
+ quantbrasil rankings current --id 123 --top 20
63
79
  ```
64
80
 
65
- ## Analyze ad-hoc basket
81
+ Rules:
82
+
83
+ - rankings answer order-first questions
84
+ - system rankings use their slug from `rankings list`
85
+ - user rankings use their numeric id from `rankings list`
86
+ - use exactly one selector: `--system <slug>` or `--id <id>`
87
+ - do not call user rankings "factor:" in command syntax; ranking fatorial is a saved user ranking
88
+ - use `--json` if another step needs to parse tickers, ranks, or ordering metrics
89
+ - do not route ranking-first questions through screening unless the user asks for indicator conditions
90
+
91
+ ## Analyze saved holding
92
+
93
+ 1. Discover holding id.
94
+ 2. Inspect the holding if needed.
95
+ 3. Run the holding metric.
96
+
97
+ ```bash
98
+ quantbrasil holdings list
99
+ quantbrasil holdings get 93
100
+ quantbrasil holdings historical-return 93 --period 1y
101
+ quantbrasil holdings beta 93 --years 1
102
+ quantbrasil holdings var 93 --years 1 --confidence 95
103
+ ```
104
+
105
+ ## Run indicator screening
106
+
107
+ 1. Discover available universes.
108
+ 2. Discover available indicators when the filter JSON is not already known.
109
+ 3. Pick exactly one system portfolio, watchlist, or holding.
110
+ 4. Run screening with a full `ScreenerRequest` JSON file.
111
+
112
+ ```bash
113
+ quantbrasil screening universes
114
+ quantbrasil screening indicators
115
+ quantbrasil screening run --system acoes-mais-liquidas --query-file ./screening.json
116
+ quantbrasil screening run --watchlist 93 --query-file ./screening.json --limit 25
117
+ quantbrasil screening run --holding 182 --query-file ./screening.json --sort ticker
118
+ ```
119
+
120
+ Rules:
121
+
122
+ - screening universes are portfolio-backed only: system portfolios, watchlists, or holdings
123
+ - a holding used as a screening universe contributes only its asset set
124
+ - do not invent ad-hoc ticker-list screening commands
125
+ - load `references/screening.md` for IFR/RSI examples and full JSON payloads
126
+ - use `--json` when the result will be parsed by an agent or script
127
+
128
+ ## Run pair cointegration / Long & Short
129
+
130
+ Use this when the user asks to run cointegração, cointegration, Long & Short,
131
+ long and short, long-short, pair trading statistics, z-score, p-value, or
132
+ half-life for two explicit assets.
133
+
134
+ ```bash
135
+ quantbrasil cointegration pair PETR4 VALE3
136
+ quantbrasil cointegration pair PETR4 VALE3 --window 120
137
+ quantbrasil cointegration pair PETR4 VALE3 --json
138
+ ```
139
+
140
+ Rules:
141
+
142
+ - require two explicit tickers
143
+ - frame Long & Short phrasing as pair analysis, not order execution or a guaranteed trade recommendation
144
+ - use `market assets --search` first only if a ticker is ambiguous
145
+ - do not invent a universe scan command for "quais pares cointegrados"; the public CLI currently supports explicit pairs
146
+ - use `--json` if the z-score or beta series will feed another calculation
147
+
148
+ ## Analyze a theoretical composition
66
149
 
67
- Use repeated `--asset TICKER[:WEIGHT_PCT]`.
150
+ Create a holding with target weights, then run holding metrics on its id.
68
151
 
69
152
  Rules:
70
153
 
71
- - if weights omitted, backend may assume equal weights
72
- - negative weights allowed for hedge / short exposure
154
+ - `holdings create` returns the holding id
155
+ - use `holdings set-targets` to replace target weights later
156
+ - use `holdings set-positions` for actual share/unit/coin quantities
73
157
  - `beta` only accepts `--years 1|3|5`
74
158
  - `var --confidence` takes percent like `95` or `99`
75
159
 
76
160
  Example:
77
161
 
78
162
  ```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
163
+ quantbrasil holdings create "Teórica energia" --target VALE3:50 --target PRIO3:50
164
+ quantbrasil holdings historical-return 93 --from 2025-01-01 --to 2026-01-01
165
+ quantbrasil holdings beta 93 --years 1
166
+ quantbrasil holdings var 93 --years 1 --confidence 99
82
167
  ```
83
168
 
84
- ## Modify saved portfolio
169
+ ## Modify watchlists and holdings
85
170
 
86
171
  Use explicit mutation commands. Do not retry failed mutations automatically.
87
172
 
88
173
  ```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
174
+ quantbrasil watchlists create "Dividendos"
175
+ quantbrasil watchlists rename 93 "Longo Prazo"
176
+ quantbrasil watchlists add-assets 93 PETR4 VALE3
177
+ quantbrasil watchlists remove-assets 93 PETR4
178
+
179
+ quantbrasil holdings create "Longo Prazo"
180
+ quantbrasil holdings create "Longo Prazo" --target PETR4:50 --target VALE3:50
181
+ quantbrasil holdings create "Carteira Real" --mode position
182
+ quantbrasil holdings rename 182 "Carteira Real"
183
+ quantbrasil holdings set-targets 182 PETR4:50 VALE3:50
184
+ quantbrasil holdings set-positions 182 PRIO3:1600 BTC-USD:0.25 QQQ:10
93
185
  ```
94
186
 
95
187
  Rules:
96
188
 
97
- - use `portfolios list` first if user names a portfolio instead of giving id
189
+ - use `watchlists list` or `holdings list` first if the user names a saved object instead of giving id
98
190
  - confirm destructive intent before removing assets when user request is ambiguous
191
+ - positions are quantity-only in the public CLI; do not pass monetary values as positions
192
+ - target weights and positions are independent; updating positions does not clear target weights
99
193
  - 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
- }