Skip to content

My Portfolio — Spec

Overview

Upload Mirae Asset (미래에셋) M-STOCK app screenshots to extract and track personal holdings. Uses a vision model to parse brokerage app screenshots into structured data, and computes portfolio-level risk metrics from the snapshot history.

Parsing

  • Screenshots sent to Gemini 2.5 Flash (vision) via the Google Generative AI SDK
  • Extracts: Korean holdings, US holdings, summary totals, cash positions
  • Returns structured JSON with summary, kr_holdings, and us_holdings keys
  • Korean stock names are mapped to KRX 6-digit codes via a static lookup table (portfolio/parser.py:KR_NAME_TO_TICKER)
  • Token usage tracked per parse: tokens_input, tokens_output, tokens_total stored on PortfolioSnapshot

Storage

  • Each upload creates a timestamped PortfolioSnapshot + PortfolioHolding rows
  • Safe to upload multiple times — each creates a new snapshot; history is preserved

Risk Metrics

Computed server-side in portfolio/risk.py from the full snapshot history. Served via GET /api/portfolio/risk.

Metric Source Notes
Sharpe Ratio Inter-snapshot returns derived from total_assets_krw Annualized using frequency inferred from median inter-snapshot gap (×√(365/median_gap_days)); risk-free = 0; requires ≥4 return observations
Historical VaR 95% / 99% Same inter-snapshot return series Worst observed percentile of period-over-period returns
Parametric VaR 95% / 99% Normal distribution fit to return series μ − z × σ; z₉₅ = 1.6449, z₉₉ = 2.3263
Max Drawdown total_assets_krw series Peak-to-trough % decline; expressed as negative %
Sector Concentration Latest snapshot holdings Buckets holdings by sector using config.py sector maps; adds cash bucket

Returns are computed as (assets[i] / assets[i-1] - 1) × 100 between consecutive snapshots with non-null total_assets_krw. This produces a series that is independent of how the brokerage computes its own same-day P&L figure. The most recent 180 snapshots are used; older snapshots are dropped to bound memory and computation.

All KRW values use latest.usd_krw_rate for USD→KRW conversion when computing the cash bucket.

Dashboard Sections

Section Content
Summary cards Total assets, today P&L, KR return %, US return % — from latest snapshot
Holdings table All holdings from latest snapshot with ticker, qty, avg cost, current price, P&L
Portfolio Trends Total assets (line), daily P&L (bar), KR vs US allocation (stacked area), return % (dual-line) — requires ≥2 snapshots
Portfolio Risk Sharpe, VaR 95%/99%, max drawdown cards + sector concentration bar chart — requires ≥2 snapshots with non-empty risk payload
Token bar Gemini token usage today vs 500k/day reference

Constraints

  • Requires GOOGLE_API_KEY in .env; portfolio parser fails if key is absent
  • Supported image formats: JPEG, PNG, WebP, HEIC
  • portfolio/parser.py:parse_screenshots() encodes images as base64 before sending
  • Risk metrics require ≥2 snapshots to compute; the Risk section is hidden until data is available
  • Sharpe / VaR calculations require ≥4 data points; they return None when below threshold