4.4 KiB
Charts Module
This module covers the chart subsystem: selectable time range (zoom/pan), the value/invested-capital/cash toggle, total-return vs. price-return comparison, the contribution/waterfall chart, benchmark comparison, and the drawdown chart. Full design docs for each live in /docs/design/, this README is the quick-reference API contract.
Shared range contract
Every endpoint below accepts the same range params, so client code only needs to implement range handling once.
?range=1d|wtd|mtd|ytd|1y|5y|max
or
?startDate=YYYY-MM-DD&endDate=YYYY-MM-DD
Rules:
rangeandstartDate/endDateare mutually exclusive, sending both returns 400.startDate/endDatemust both be present if either is, returns 400 otherwise.endDatebeforestartDatereturns 400.endDatebeyond today is clamped to today.startDatebefore the account's earliest activity is clamped to that date.- Granularity is resolved server-side from range width (daily up to 2 years, weekly beyond) and returned in the response, not requested by the client.
Endpoints
GET /api/v1/portfolio/performance
Portfolio value, invested capital, or cash as a time series.
Extra param: kind = portfolioValue (default) | investedCapital | cash
interface ChartSeriesResponse {
kind: 'portfolioValue' | 'investedCapital' | 'cash';
granularity: 'daily' | 'weekly';
startDate: string;
endDate: string;
points: Array<{ date: string; value: number }>;
}
GET /api/v1/portfolio/return-comparison
Total return vs. price return, indexed to base 100 at startDate.
Extra param: symbol (optional, omit for whole-portfolio scope)
interface ReturnComparisonResponse {
granularity: 'daily' | 'weekly';
startDate: string;
endDate: string;
points: Array<{ date: string; priceReturn: number; totalReturn: number }>;
}
GET /api/v1/portfolio/benchmark-comparison
Portfolio total return vs. a benchmark symbol, both indexed to base 100.
Required param: benchmark (a market-data symbol, same lookup as adding a holding)
interface BenchmarkComparisonResponse {
granularity: 'daily' | 'weekly';
startDate: string;
endDate: string;
benchmarkSymbol: string;
benchmarkAvailableFrom: string;
points: Array<{
date: string;
portfolioReturn: number;
benchmarkReturn: number;
}>;
}
GET /api/v1/portfolio/drawdown
Percentage decline from the portfolio's running all-time peak. Peak tracking always uses full account history, even when the requested range starts later.
interface DrawdownResponse {
granularity: 'daily' | 'weekly';
startDate: string;
endDate: string;
maxDrawdown: number;
maxDrawdownDate: string;
points: Array<{ date: string; drawdown: number; isNewPeak: boolean }>;
}
GET /api/v1/portfolio/waterfall
Discrete start/contribution/end segments explaining a period's value change.
Extra param: breakdown = portfolio (default) | holding
type WaterfallSegment =
| { kind: 'start'; label: string; value: number }
| {
kind: 'contribution';
label: string;
value: number;
category: 'netCashFlow' | 'marketGainLoss' | 'dividends';
holdingSymbol?: string;
}
| { kind: 'end'; label: string; value: number };
interface WaterfallResponse {
startDate: string;
endDate: string;
currency: string;
segments: WaterfallSegment[];
}
Design docs
| Area | Doc |
|---|---|
| Zoom / pan / selectable time range | docs/design/zoom-pan-time-range-data-spec.md |
| Value / invested capital / cash toggle | docs/design/portfolio-value-invested-capital-cash-toggle-spec.md |
| Total return vs. price return | docs/design/total-return-vs-price-return-comparison-spec.md |
| Contribution / waterfall | docs/design/contribution-waterfall-chart-data-structure.md |
| Benchmark comparison | docs/design/benchmark-comparison-data-model.md |
| Drawdown | docs/design/drawdown-chart-calculation-spec.md |
| Overall architecture | docs/design/design-architecture-charts.md |
Adjust the paths above to wherever the docs actually land in the repo before merging.