from __future__ import annotations import re from typing import Any, Dict, List def parse_portfolio_input(raw_text: str) -> List[Dict[str, Any]]: """ Parse a lightweight holdings string into a list of holdings. Supported examples: - "10 AAPL, 5 MSFT, 2 VTI" - "AAPL 10; MSFT 5; VTI 2" - "10 shares of AAPL\n5 shares of MSFT" """ holdings: List[Dict[str, Any]] = [] if not raw_text: return holdings chunks = re.split(r"[,\n;]+", raw_text) for chunk in chunks: text = chunk.strip() if not text: continue match = re.search( r"(?P\d+(?:\.\d+)?)\s*(?:shares?|units?)?\s*(?:of\s+)?(?P[A-Za-z][A-Za-z0-9.\-]{0,9})", text, flags=re.IGNORECASE, ) if not match: match = re.search( r"(?P[A-Za-z][A-Za-z0-9.\-]{0,9})\s*(?:shares?|units?)?\s*(?P\d+(?:\.\d+)?)", text, flags=re.IGNORECASE, ) if not match: continue symbol = match.group("symbol").strip().upper() try: quantity = float(match.group("qty")) except ValueError: continue if symbol and quantity > 0: holdings.append({"symbol": symbol, "quantity": quantity}) return holdings