My Portfolio — Implementation¶
How It Works¶
- User uploads screenshot(s) via the My Portfolio page
portfolio/parser.py:parse_screenshots()encodes images as base64 and calls Gemini 2.5 Flash with a structured extraction prompt- Returns JSON with
summary,kr_holdings,us_holdingskeys and_tokensmetadata save_snapshot()persistsPortfolioSnapshot+PortfolioHoldingrows; each upload is a new timestamped snapshot
Korean Name → Ticker Mapping¶
KR_NAME_TO_TICKER in portfolio/parser.py is a static lookup table mapping Korean stock names (as shown in the M-STOCK app) to KRX 6-digit codes. If a name is not in the table, ticker is stored as None on the PortfolioHolding row.
Token Tracking¶
tokens_input, tokens_output, tokens_total stored on PortfolioSnapshot from usage_metadata in the Gemini response. Exposed via GET /portfolio/token-usage endpoint.
System Overlap Tab¶
Cross-references PortfolioHolding.ticker values against load_predictions() — shows 1d ML direction + confidence alongside each holding position.
Gotchas¶
ANTHROPIC_API_KEYis required — the upload button is disabled with an error message if the key is absent. The rest of the portfolio page still works (viewing past snapshots).- Each upload is a new snapshot — there is no upsert/replace logic. Old snapshots are preserved.
ticker=Noneholdings appear in the UI but have no ML prediction or sentiment data to show.- Supported formats: JPEG, PNG, WebP. Large images are sent as-is (base64 encoded).