csv-parser: keep LLM-mapped tickers; don't pass them through T212 mapping
The route's resolve-slice loop is T212-specific — it looks tickers up against the InstrumentMap, which only has T212's universe. For the LLM path the ticker is already Yahoo-ready (e.g. VOD.L, ASML.AS), so sending it through resolve_slice produced spurious "could not be resolved" warnings and dropped the positions. Fix: ParsedPie gains a ``tickers_resolved`` flag (default False for T212 backward-compat); _apply_mapping in the LLM path sets it True and also extracts currency from the LLM-mapped currency_col into a new ``ParsedPosition.currency`` field. The route branches on the flag: LLM-path positions are kept verbatim with a best-effort InstrumentMap lookup for nicer name/currency overrides, never dropped. Integration test tightened to assert all 5 IBKR fixture positions round-trip with the right currencies (USD / GBP / EUR). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
b8ebba9503
commit
bc55ab7d26
4 changed files with 74 additions and 13 deletions
|
|
@ -225,19 +225,42 @@ async def parse_portfolio(
|
|||
unmapped: list[str] = []
|
||||
|
||||
for p in pie.positions:
|
||||
resolved = await resolve_slice(session, p.slice)
|
||||
if resolved is None or not resolved.yahoo_ticker:
|
||||
unmapped.append(p.slice or p.name or "?")
|
||||
continue
|
||||
if pie.tickers_resolved:
|
||||
# LLM path: ``p.slice`` is already a Yahoo-ready ticker. We
|
||||
# still do a best-effort InstrumentMap lookup so we can use
|
||||
# the canonical name + currency when we happen to have one;
|
||||
# but unlike the T212 path we never *drop* a position just
|
||||
# because resolve_slice missed.
|
||||
yahoo_ticker = p.slice.strip().upper()
|
||||
if not yahoo_ticker:
|
||||
unmapped.append(p.name or "?")
|
||||
continue
|
||||
resolved = await resolve_slice(session, yahoo_ticker)
|
||||
name = (resolved.name if resolved else None) or p.name
|
||||
currency = (
|
||||
(resolved.currency if resolved else None)
|
||||
or p.currency or "USD"
|
||||
)
|
||||
else:
|
||||
# T212 path: ``p.slice`` is a shortcode that MUST round-trip
|
||||
# through the InstrumentMap. Drop unmapped positions — the
|
||||
# warnings block surfaces them to the user.
|
||||
resolved = await resolve_slice(session, p.slice)
|
||||
if resolved is None or not resolved.yahoo_ticker:
|
||||
unmapped.append(p.slice or p.name or "?")
|
||||
continue
|
||||
yahoo_ticker = resolved.yahoo_ticker
|
||||
name = resolved.name or p.name
|
||||
currency = resolved.currency
|
||||
positions_out.append({
|
||||
"yahoo_ticker": resolved.yahoo_ticker,
|
||||
"yahoo_ticker": yahoo_ticker,
|
||||
"t212_slice": p.slice,
|
||||
"name": resolved.name or p.name,
|
||||
"name": name,
|
||||
"qty": p.quantity,
|
||||
"avg_cost": p.average_price, # @property — no call parens
|
||||
"currency": resolved.currency,
|
||||
"currency": currency,
|
||||
})
|
||||
yahoo_tickers.append(resolved.yahoo_ticker)
|
||||
yahoo_tickers.append(yahoo_ticker)
|
||||
|
||||
# Synchronous upsert: bypass the Redis buffer so the dashboard has
|
||||
# live prices immediately. The buffer + flush machinery remains for
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue