Spaces:
Sleeping
Sleeping
| 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<qty>\d+(?:\.\d+)?)\s*(?:shares?|units?)?\s*(?:of\s+)?(?P<symbol>[A-Za-z][A-Za-z0-9.\-]{0,9})", | |
| text, | |
| flags=re.IGNORECASE, | |
| ) | |
| if not match: | |
| match = re.search( | |
| r"(?P<symbol>[A-Za-z][A-Za-z0-9.\-]{0,9})\s*(?:shares?|units?)?\s*(?P<qty>\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 | |