Portfolio API
Auto-generated from
openapi-snapshot.json. Do not edit by hand — changes are overwritten byscripts/docs/generate_api_reference.py.
19 endpoints in this group.
GET /api/portfolio/archive-stats
Archive Stats
Row counts and date range for the snapshot and intraday tables.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id | query | — | optional | |
account_id | query | — | optional | |
account_ids | query | — | optional |
Responses
| Code | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
POST /api/portfolio/backfill/csv
Backfill From Csv
Deprecated stub — use /api/portfolio/backfill/upload instead.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id | query | — | optional |
Responses
| Code | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
POST /api/portfolio/backfill/save
Backfill Save
Request body: application/json
Responses
| Code | Description |
|---|---|
201 | Successful Response |
422 | Validation Error |
POST /api/portfolio/backfill/upload
Backfill Upload
Upload a CSV of historical position snapshots and upsert into daily_position_snapshots.
Expected CSV columns (flexible header matching): symbol, date, quantity, avg_cost, market_price, market_value Optional: unrealized_pnl, day_change_pct, source
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
year | query | integer | optional | |
source_type | query | string | optional | |
tenant_id | query | — | optional |
Request body: multipart/form-data
Responses
| Code | Description |
|---|---|
201 | Successful Response |
422 | Validation Error |
DELETE /api/portfolio/data
Delete Year Data
Soft-delete a year’s realized_trades + daily_position_snapshots.
Both tables get deleted_at = now() on still-active rows. No row is
physically removed (see migration 0115_soft_delete_realized_dps).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id | query | — | optional | |
year | query | integer | required |
Responses
| Code | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
POST /api/portfolio/eod
Run Eod Ingestion
Trigger end-of-day broker position fetch + snapshot save.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id | query | — | optional |
Responses
| Code | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
GET /api/portfolio/eod/run-log
Get Eod Run Log
Return recent EOD snapshot runs grouped by snapshot_date.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id | query | — | optional | |
account_id | query | — | optional | |
account_ids | query | — | optional | |
limit | query | integer | optional |
Responses
| Code | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
POST /api/portfolio/eod/trigger
Run Eod Ingestion
Trigger end-of-day broker position fetch + snapshot save.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id | query | — | optional |
Responses
| Code | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
GET /api/portfolio/fees-summary
Fees Summary
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
days | query | integer | optional | |
account_hash | query | — | optional |
Responses
| Code | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
GET /api/portfolio/pnl
Get Pnl Series
Daily P&L time series — P0-8: limit capped at 100.
G10 (mobile#150): supports keyset cursor pagination for infinite-scroll.
The series is grouped by snapshot_date and ordered ASC (chronological).
Pass cursor from the previous page’s next_cursor to fetch the
next chunk; the server emits next_cursor only when a full page was
returned (len(rows) == limit). Cursor is opaque — a base64url JSON
blob whose payload is {"d": "YYYY-MM-DD"} (date-only, since rows are
grouped by date and have no row-id).
The page dependency provides limit (1..100) + a generic
cursor query param for shape-parity with the blotter endpoint;
we decode it locally rather than via decode_cursor because pnl
keyset is one-column not two.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id | query | — | optional | |
account_id | query | — | optional | |
account_ids | query | — | optional | |
start_date | query | — | optional | |
end_date | query | — | optional | |
limit | query | integer | optional | Page size (max 100). Legacy — prefer ?page_size=. |
page_size | query | — | optional | Canonical page size. Must be one of 25, 50, 100. When supplied, overrides ?limit=. |
cursor | query | — | optional | Opaque pagination cursor from a previous page’s next_cursor. |
Responses
| Code | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
GET /api/portfolio/positions/{symbol}
Get Position By Symbol
Latest daily snapshot row for one symbol.
Returns 404 when the tenant has no snapshot for that symbol so
the mobile client can render an empty-state without parsing. Returns
200 with the same shape as /snapshots (singular snapshot
key + snapshot_date echo) so PositionDetailScreen reuses the
existing dashboard summary-row parser.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
symbol | path | string | required | |
tenant_id | query | — | optional | |
account_id | query | — | optional | |
account_ids | query | — | optional |
Responses
| Code | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
GET /api/portfolio/realized-from-orders
Get Realized From Orders
Return FIFO realized lots derived from filled local orders.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id | query | — | optional | |
account_id | query | — | optional | |
account_ids | query | — | optional | |
year | query | — | optional | |
symbol | query | — | optional |
Responses
| Code | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
GET /api/portfolio/realized-kpis
Get Realized Kpis
Day/MTD/YTD realized gain/loss for the live Portfolio KPI strip.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id | query | — | optional | |
account_id | query | — | optional | |
account_ids | query | — | optional | |
source | query | string | optional | broker |
Responses
| Code | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
GET /api/portfolio/realized-trades
Get Realized Trades
Return realized trades from uploads or connected-broker lot projections.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id | query | — | optional | |
account_id | query | — | optional | |
account_ids | query | — | optional | |
year | query | — | optional | |
symbol | query | — | optional | |
source | query | — | optional | |
limit | query | — | optional | |
page | query | integer | optional | 1-based page index. |
page_size | query | integer | optional | Page size — must be one of 25, 50, 100. |
Responses
| Code | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
POST /api/portfolio/realized-trades/sync
Sync Realized Trades
Repair Schwab realized-trade rows from already-stored broker data.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
account_id | query | — | optional | |
account_ids | query | — | optional | |
force_rebuild_closed_lots | query | boolean | optional |
Responses
| Code | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
GET /api/portfolio/snapshots
Get Snapshots
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id | query | — | optional | |
account_id | query | — | optional | |
account_ids | query | — | optional | |
account_number | query | — | optional | |
start_date | query | — | optional | |
end_date | query | — | optional | |
as_of_date | query | — | optional | |
latest_only | query | boolean | optional | |
refresh_live | query | boolean | optional | When true with latest_only, foreground-refresh connected broker positions before reading. |
limit | query | integer | optional |
Responses
| Code | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
POST /api/portfolio/snapshots
Save Snapshots
Request body: application/json
Responses
| Code | Description |
|---|---|
201 | Successful Response |
422 | Validation Error |
POST /api/portfolio/snapshots/archive
Archive Snapshots
- Move daily_position_snapshots older than keep_snapshot_years to archive table.
- Roll up 5-min intraday bars older than keep_intraday_years into market_data_daily (preserving OHLCV + VWAP) before deleting them.
- Purge intraday bars beyond the retention window.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id | query | — | optional | |
keep_snapshot_years | query | integer | optional | |
keep_intraday_years | query | integer | optional |
Responses
| Code | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
DELETE /api/portfolio/trades
Delete Trades
Soft-delete realized_trades for the given tenant + year.
Sets deleted_at = now() on already-active rows (idempotent — rows
that are already soft-deleted are skipped). Returns the number of rows
that transitioned to soft-deleted in this call.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id | query | — | optional | |
year | query | integer | required |
Responses
| Code | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |