Output:
Chunk ID: 1f1416
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 2128
Output:
   100	    data_cutoff: str | None = None,
   101	) -> list[OfficialCashComponent]:
   102	    roles = postfinance_account_roles(conn)
   103	    account_id = roles.get("etrading_cash")
   104	    if not account_id:
   105	        return []
   106	    conditions: list[str] = []
   107	    params: list[str] = []
   108	    if as_of:
   109	        conditions.append("date(snapshot_at)<=date(?)")
   110	        params.append(as_of)
   111	    if data_cutoff:
   112	        conditions.append("datetime(created_at)<=datetime(?)")
   113	        params.append(data_cutoff)
   114	    where = f"WHERE {' AND '.join(conditions)}" if conditions else ""
   115	    snapshot = conn.execute(
   116	        f"""SELECT snapshot_id,snapshot_at,cash_chf
   117	            FROM postfinance_snapshots {where}
   118	            ORDER BY snapshot_at DESC,datetime(created_at) DESC LIMIT 1""",
   119	        params,
   120	    ).fetchone()
   121	    if not snapshot:
   122	        return []
   123	    rows = conn.execute(
   124	        """SELECT currency,amount_original,fx_rate_to_chf
   125	           FROM postfinance_snapshot_cash WHERE snapshot_id=? ORDER BY currency""",
   126	        (snapshot["snapshot_id"],),
   127	    ).fetchall()
   128	    raw_values = [
   129	        _decimal(row["amount_original"]) * _decimal(row["fx_rate_to_chf"])
   130	        for row in rows
   131	    ]
   132	    target = _money(_decimal(snapshot["cash_chf"]))
   133	    allocated = _allocate_pro_rata(raw_values, target)
   134	    valuation_at = str(snapshot["snapshot_at"])
   135	    return [
   155	    roles = postfinance_account_roles(conn)
   156	    account_id = roles.get("etrading_depot")
   157	    if not account_id:
   158	        return {}
   159	    conditions: list[str] = []
   160	    params: list[str] = []
   161	    if as_of:
   162	        conditions.append("date(snapshot_at)<=date(?)")
   163	        params.append(as_of)
   164	    if data_cutoff:
   165	        conditions.append("datetime(created_at)<=datetime(?)")
   166	        params.append(data_cutoff)
   167	    where = f"WHERE {' AND '.join(conditions)}" if conditions else ""
   168	    snapshot = conn.execute(
   169	        f"""SELECT snapshot_id,snapshot_at,stocks_chf,etfs_chf
   170	            FROM postfinance_snapshots {where}
   171	            ORDER BY snapshot_at DESC,datetime(created_at) DESC LIMIT 1""",
   172	        params,
   173	    ).fetchone()
   174	    if not snapshot:
   175	        return {}
   176	    rows = conn.execute(
   177	        """SELECT instrument_id,asset_class,quantity,price_original,price_currency,value_chf
   178	           FROM postfinance_snapshot_positions
   179	           WHERE snapshot_id=? ORDER BY asset_class,instrument_id""",
   180	        (snapshot["snapshot_id"],),
   181	    ).fetchall()
   182	    targets = {
   183	        "stock": _money(_decimal(snapshot["stocks_chf"])),
   184	        "etf": _money(_decimal(snapshot["etfs_chf"])),
   185	    }
   230	    ]
   231	    if not account_ids:
   232	        return {}
   233	    snapshot_date = min(item.snapshot_date for item in positions.values())
   234	    effective_as_of = as_of or "9999-12-31"
   235	    placeholders = ",".join("?" for _ in account_ids)
   236	    rows = conn.execute(
   237	        f"""SELECT instrument_id,lower(transaction_type) transaction_type,quantity
   238	            FROM transactions
   239	            WHERE account_id IN ({placeholders})
   240	              AND instrument_id IS NOT NULL
   241	              AND COALESCE(is_confirmed,0)=1
   242	              AND COALESCE(is_voided,0)=0
   243	              AND lower(transaction_type) IN ('buy','sell','partial_sell','full_sell')
   244	              AND trade_date>? AND trade_date<=?""",
   245	        (*account_ids, snapshot_date, effective_as_of),
   246	    ).fetchall()
   247	    deltas: dict[str, Decimal] = {}
   248	    for row in rows:
   249	        instrument_id = str(row["instrument_id"])
   250	        quantity = abs(_decimal(row["quantity"]))
   251	        if str(row["transaction_type"]) in {"sell", "partial_sell", "full_sell"}:
   252	            quantity = -quantity
   253	        deltas[instrument_id] = deltas.get(instrument_id, Decimal("0")) + quantity
   254	    return deltas
   255
   320	                positions.append(
   321	                    CashPosition(
   322	                        id=f"{account_id}:{currency}",
   323	                        account_id=account_id,
   324	                        platform=row["platform"],
   325	                        account_label=row["account_name"],
   326	                        account_type=row["account_type"],
   327	                        currency=currency,
   328	                        balance_mode="official_components",
   329	                        amount=decimal_text(amount_original, 2),
   330	                        amount_chf=decimal_text(amount_chf, 2),
   331	                        calculated_balance_chf=decimal_text(amount_chf, 2),
   332	                        manual_balance_chf=None,
   333	                        csv_anchor_balance_chf=None,
   334	                        used_value_chf=decimal_text(amount_chf, 2),
   335	                        difference_chf="0.00",
   336	                        last_manual_reconciliation=snapshot_date,
   337	                        last_imported_booking=_last_imported_booking(conn, account_id),
   338	                        status=status,
   339	                    )
   340	                )
   341	            continue
   342	        manual = _latest_snapshot(conn, account_id, "manual_balance")
   343	        anchor = _latest_snapshot(conn, account_id, "csv_anchor_balance")
   344	        mode = row["balance_mode"] or "manual"
   345	        default_currency = str(row["currency"])
   346	        materialized_rows = conn.execute(
   347	            """SELECT currency,amount_original,amount_chf,source_type
   348	                 FROM cash_balances WHERE account_id=?
   349	                 ORDER BY balance_date,created_at,cash_balance_id""",
   350	            (account_id,),
   351	        ).fetchall()
   352	        materialized_by_currency: dict[str, list[Row]] = {}
   353	        for balance_row in materialized_rows:
   354	            materialized_by_currency.setdefault(
   355	                str(balance_row["currency"]), []
   356	            ).append(balance_row)
   357	        ledger_currencies = {
   358	            currency
   359	            for ledger_account_id, currency in ledger.balances
   360	            if ledger_account_id == account_id
   361	        }
   362	        # Manual balances and CSV anchors are account-level CHF controls. They must
   363	        # not be added to per-currency ledger rows from the same account.
   364	        currencies = (
   365	            {default_currency}
   366	            if manual or anchor
   367	            else set(ledger_currencies) | set(materialized_by_currency)
   368	            or {default_currency}
   369	        )
   370	        for currency in sorted(currencies):
   371	            ledger_balance = ledger.balances.get((account_id, currency))
   372	            currency_rows = materialized_by_currency.get(currency, [])
   373	            # Equity actions write their cash leg only to cash_balances. Other
   374	            # materialized rows are used only when no same-currency ledger exists,
   375	            # avoiding duplicate vue_manual_cash transaction/materialization pairs.
   376	            additional_rows = (
   377	                [
   378	                    balance_row
   379	                    for balance_row in currency_rows
   380	                    if str(balance_row["source_type"] or "").startswith(
   381	                        ("vue_equity_sale:", "vue_equity_dividend")
   382	                    )
   383	                ]
   384	                if ledger_balance is not None
   385	                else currency_rows
   386	            )
   387	            if manual or anchor:
   388	                calculated = _calculated_balance(conn, account_id)
   389	                amount_original = (
   390	                    calculated if currency.upper() == "CHF" else Decimal("0")
   391	                )
   392	            elif ledger_balance is not None:
   393	                calculated = ledger_balance.amount_chf or Decimal("0")
   394	                amount_original = ledger_balance.amount_original
   395	            else:


pytest -q tests/unit/test_dashboard_functional_actions_v2.py tests/unit/test_equity_manage.py


Output:
Chunk ID: cf5bd8
Wall time: 30.0015 seconds
Process running with session ID 43246
Original token count: 0
Output:


Output:
Chunk ID: 1d4b2e
Wall time: 30.0014 seconds
Process running with session ID 43246
Original token count: 0
Output:


pkill -f "pytest -q tests/unit/test_dashboard_functional_actions_v2.py" || true


Output:
Chunk ID: 13ad1f
Wall time: 0.0000 seconds
Process exited with code 143
Original token count: 0
Output:


grep -n "confirm_equity_buy" -A70 src/jarvis_finance/services/manual_entry_service.py


Output:
Chunk ID: 0485ac
Wall time: 0.0000 seconds
Process exited with code 1
Original token count: 0
Output:


grep -R "def confirm_equity_buy\|vue_equity_buy" -n src/jarvis_finance/services src/jarvis_finance/equity


Output:
Chunk ID: e196ba
Wall time: 0.0000 seconds
Process exited with code 1
Original token count: 0
Output:


grep -R "EquityBuy\|equity.*buy\|transaction_type.*buy" -n src/jarvis_finance/services src/jarvis_finance/equity | head -80


Output:
Chunk ID: 22839c
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 235
Output:
grep: src/jarvis_finance/services/__pycache__/portfolio_aggregation.cpython-311.pyc: binary file matches
grep: src/jarvis_finance/services/__pycache__/portfolio_analytics.cpython-312.pyc: binary file matches
grep: src/jarvis_finance/services/__pycache__/portfolio_analytics.cpython-311.pyc: binary file matches
src/jarvis_finance/services/portfolio_aggregation.py:243:              AND lower(transaction_type) IN ('buy','sell','partial_sell','full_sell')
src/jarvis_finance/services/portfolio_analytics.py:220:          AND lower(t.transaction_type) IN ('initial_position_snapshot','buy','sell','partial_sell','full_sell')
src/jarvis_finance/equity/manage.py:546:    if transaction_type in {"buy", "partial_sell", "full_sell", "initial_position_snapshot"} and qty <= 0:
src/jarvis_finance/equity/manage.py:679:    if transaction_type in {"initial_position_snapshot", "buy", "partial_sell", "full_sell", "manual_adjustment"} and qty <= 0:


grep -n "PFEvent\|account_role.*etrading" -n src/jarvis_finance/imports/postfinance_documents.py | head -80


Output:
Chunk ID: a0a0d2
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 101
Output:
47:class PFEvent:
115:    events: tuple[PFEvent, ...]
304:def _event_from_document(doc: PFDocument) -> PFEvent | None:
465:    return PFEvent(
490:def _interest_events(doc: PFDocument) -> tuple[PFEvent, ...]:
493:    events: list[PFEvent] = []
530:            PFEvent(
812:        len(items) != 2 or {d.account_role for d in items} != {"efinance", "etrading_cash"}
823:    event_list: list[PFEvent] = []


sed -n '304,470p' src/jarvis_finance/imports/postfinance_documents.py


Output:
Chunk ID: 430ed9
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1745
Output:
def _event_from_document(doc: PFDocument) -> PFEvent | None:
    text = doc.text
    reference = doc.semantic_reference
    occurred = doc.document_date or ""
    settlement = None
    match = re.search(r"Ausführungsdatum\s*:?\s*(\d{1,2}[./]\d{1,2}[./]\d{4})", text, flags=re.I)
    if match:
        occurred = _date(match.group(1)) or occurred
    match = re.search(r"Valutadatum\s*:?\s*(\d{1,2}[./]\d{1,2}[./]\d{4})", text, flags=re.I)
    if match:
        settlement = _date(match.group(1))
    if not occurred:
        raise ValueError("PostFinance economic document lacks an event date")
    fee = Decimal("0")
    tax = Decimal("0")
    fx = None
    instrument = None
    isin = None
    quantity = price = gross = net = None
    direction = None
    transfer_group = None
    reasons: list[str] = []
    currency = "CHF"
    event_type = ""

    if doc.document_type == "trade_confirmation":
        event_type = (
            "buy"
            if re.search(r"Börsentransaktion\s*:\s*Kauf", text, flags=re.I)
            else "sell"
            if re.search(r"Börsentransaktion\s*:\s*Verkauf", text, flags=re.I)
            else ""
        )
        if not event_type:
            raise ValueError("Unsupported PostFinance trade direction")
        im = re.search(r"([^\n]+?)\s+ISIN\s*:\s*([A-Z]{2}[A-Z0-9]{9}[0-9])", text)
        if not im:
            raise ValueError("PostFinance trade lacks instrument identity")
        instrument, isin = im.group(1).strip(), im.group(2).upper()
        lines = text.splitlines()
        header = next(
            (
                i
                for i, line in enumerate(lines)
                if "Anzahl" in line and "Preis" in line and "Betrag" in line
            ),
            None,
        )
        values: list[str] = []
        if header is not None:
            for line in lines[header + 1 : header + 6]:
                cols = [item.strip() for item in re.split(r"\s{2,}", line.strip()) if item.strip()]
                if len(cols) >= 4 and sum(bool(re.search(r"\d", item)) for item in cols) >= 3:
                    values = cols
                    break
        if not values:
            raise ValueError("PostFinance trade amount row cannot be parsed")
        quantity = _number(values[0], required=True)
        price = _number(values[1], required=True)
        currency = next((item for item in values if item in CURRENCIES), "")
        gross = _number(values[-1], required=True)
        if not currency:
            raise ValueError("PostFinance trade currency is missing")
        fee_item = _currency_amount(text, r"Kommission")
        stamp_item = _currency_amount(text, r"Abgabe\s*\([^\n]*Stempelsteuer[^\n]*\)")
        fee = fee_item[1] if fee_item else Decimal("0")
        tax = stamp_item[1] if stamp_item else Decimal("0")
        net_item = _currency_amount(text, r"Betrag\s+belastet[^\n]*")
        assert gross is not None
        net = (
            net_item[1]
            if net_item
            else (gross + fee + tax if event_type == "buy" else gross - fee - tax)
        )
        direction = "out" if event_type == "buy" else "in"
    elif doc.document_type == "corporate_action":
        if re.search(r"\bDividende\b", text, flags=re.I):
            event_type = "dividend"
            im = re.search(r"([^\n]+?)\s+ISIN\s*:\s*([A-Z]{2}[A-Z0-9]{9}[0-9])", text)
            if not im:
                im = re.search(r"ISIN\s*:\s*([A-Z]{2}[A-Z0-9]{9}[0-9])", text)
                isin = im.group(1).upper() if im else None
            else:
                instrument, isin = im.group(1).strip(), im.group(2).upper()
            gross_item = _currency_amount(text, r"(?:Dividende|Betrag)")
            total_item = _currency_amount(text, r"Total")
            if not gross_item or not total_item:
                raise ValueError("PostFinance dividend totals cannot be parsed")
            currency, gross = gross_item
            net = total_item[1]
            taxes = re.findall(
                r"(?:Quellensteuer|Steuerrückbehalt)[^\n]*?\b"
                + currency
                + r"\s+([+\-−]?[0-9][0-9'’., ]*)",
                text,
                flags=re.I,
            )
            tax = sum(
                (_number(item, required=True) or Decimal("0") for item in taxes), Decimal("0")
            )
            direction = "in"
        elif re.search(r"\bSplit\b", text, flags=re.I):
            event_type = "split"
            im = re.search(r"ISIN\s*:\s*([A-Z]{2}[A-Z0-9]{9}[0-9])", text)
            isin = im.group(1).upper() if im else None
            currency = "CHF"
            reasons.append("non_cash_corporate_action")
        else:
            raise ValueError("Unsupported PostFinance corporate action")
    elif doc.document_type == "custody_fee":
        event_type = "fee"
        item = _currency_amount(text, r"Betrag\s+belastet") or _currency_amount(
            text, r"Bruttobetrag"
        )
        if not item:
            raise ValueError("PostFinance custody fee amount cannot be parsed")
        currency, fee = item
        gross = fee
        net = fee
        direction = "out"
    elif doc.document_type == "transfer_confirmation":
        credit = bool(re.search(r"Zahlungsverkehr\s*-\s*Gutschrift", text, flags=re.I))
        debit = bool(re.search(r"Zahlungsverkehr\s*-\s*Belastung", text, flags=re.I))
        if credit == debit:
            raise ValueError("PostFinance transfer direction is ambiguous")
        event_type = "internal_transfer"
        direction = "in" if credit else "out"
        item = _currency_amount(
            text, r"(?:Gutgeschriebener|Belasteter)\s+Betrag"
        ) or _currency_amount(text, r"Total")
        if not item:
            raise ValueError("PostFinance transfer amount cannot be parsed")
        currency, gross = item
        net = gross
        fx_match = re.search(r"Wechselkurs\s+([0-9][0-9'’., ]*)", text, flags=re.I)
        fx = _number(fx_match.group(1)) if fx_match else None
        transfer_group = _hash("transfer", reference)[:32]
        reasons.append("internal_transfer_not_income_or_expense")
    elif doc.document_type == "interest_statement":
        event_type = "interest"
        reasons.append("multi_currency_interest_statement")
        # Detailed currency components remain in the document evidence; one aggregate event
        # is intentionally not invented when the source has several independent currencies.
        currency = "CHF"
        direction = "in"
        reasons.append("component_events_required")
        return None
    else:
        return None
    fingerprint = _hash(
        reference,
        doc.semantic_identity,
        event_type,
        direction,
        isin or "",
        occurred,
        currency,
        quantity or "",
        gross or "",
        net or "",
    )
    return PFEvent(
        fingerprint,
        doc.document_hash,
        reference,
        event_type,
        doc.account_role,


grep -n "def _classify\|account_role" src/jarvis_finance/imports/postfinance_documents.py | head -60


Output:
Chunk ID: b8fc35
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 70
Output:
40:    account_role: str
52:    account_role: str
233:def _classify(name: str, text: str) -> str:
470:        doc.account_role,
782:        if d.account_role != "unresolved_transfer":
812:        len(items) != 2 or {d.account_role for d in items} != {"efinance", "etrading_cash"}


sed -n '220,260p' src/jarvis_finance/imports/postfinance_documents.py && sed -n '760,820p' src/jarvis_finance/imports/postfinance_documents.py


Output:
Chunk ID: 5ce999
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1020
Output:
        reader = PdfReader(io.BytesIO(raw), strict=False)
        if reader.is_encrypted:
            raise ValueError("Encrypted PostFinance PDFs are not supported")
        pages = [page.extract_text(extraction_mode="layout") or "" for page in reader.pages]
    except ValueError:
        raise
    except Exception as exc:
        raise ValueError("PostFinance PDF cannot be parsed") from exc
    if not pages or any(not page.strip() for page in pages):
        raise ValueError("PostFinance PDF has a page without selectable text")
    return "\n".join(pages), len(pages)

def _classify(name: str, text: str) -> str:
    key = normalize_label(PurePosixPath(name).name)
    tests = (
        ("account_statement", "kontoauszug"),
        ("trade_confirmation", "borsenabrechnung"),
        ("corporate_action", "corporate action abrechnung"),
        ("transfer_confirmation", "transferabrechnung"),
        ("custody_fee", "depotgebuhr"),
        ("interest_statement", "zinsabrechnung"),
        ("portfolio_performance", "portfolio wertentwicklung"),
    )
    for kind, marker in tests:
        if marker in key:
            return kind
    low = normalize_label(text[:2500])
    if "transaktionsbeleg" in low and "borsentransaktion" in low:
        return "trade_confirmation"
    if "transaktionsbeleg" in low and "dividende" in low:
        return "corporate_action"
    raise ValueError("Unsupported document in PostFinance bundle")

def _field(text: str, label: str) -> str | None:
    match = re.search(label + r"\s*:?\s*([^\n]+)", text, flags=re.I)
    return match.group(1).strip() if match else None

def _account_reference(text: str) -> str | None:
                        sha256(raw).hexdigest(),
                        sha256(path.name.encode()).hexdigest(),
                        kind,
                        reference,
                        identity,
                        _document_date(info.filename, text),
                        account_hash,
                        role,
                        page_count,
                        raw,
                        text,
                    )
                )
    except ValueError:
        raise
    except BadZipFile as exc:
        raise ValueError("PostFinance source is not a valid ZIP") from exc
    # Every paired transfer contains one debit and one credit. In this verified export the
    # E-Trading statements independently evidence the credited leg; the debit leg is
    # E-Finance. This semantic rule is validated again for every pair below.
    resolved_docs: list[PFDocument] = []
    for d in documents:
        if d.account_role != "unresolved_transfer":
            resolved_docs.append(d)
            continue
        if "Zahlungsverkehr - Gutschrift" in d.text:
            role = "etrading_cash"
        elif "Zahlungsverkehr - Belastung" in d.text:
            role = "efinance"
        else:
            raise ValueError("PostFinance transfer role is ambiguous")
        resolved_docs.append(
            PFDocument(
                d.document_hash,
                d.filename_hash,
                d.document_type,
                d.semantic_reference,
                d.semantic_identity,
                d.document_date,
                d.account_reference_hash,
                role,
                d.page_count,
                d.raw,
                d.text,
            )
        )
    documents = resolved_docs
    groups: dict[str, list[PFDocument]] = {}
    for doc in documents:
        if doc.document_type == "transfer_confirmation":
            groups.setdefault(doc.semantic_reference, []).append(doc)
    if any(
        len(items) != 2 or {d.account_role for d in items} != {"efinance", "etrading_cash"}
        for items in groups.values()
    ):
        raise ValueError("PostFinance internal transfer pairing is incomplete or ambiguous")
    identities: dict[str, str] = {}
    for doc in documents:
        prior = identities.setdefault(doc.semantic_identity, doc.document_hash)
        if prior != doc.document_hash:
            raise ValueError(


grep -n "role =" -B10 -A10 src/jarvis_finance/imports/postfinance_documents.py | head -80


Output:
Chunk ID: 22517e
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 556
Output:
726-                ):
727-                    raise ValueError("PostFinance ZIP contains an unsafe entry")
728-                if info.is_dir() or path.suffix.lower() != ".pdf" or info.file_size > MAX_PDF_BYTES:
729-                    raise ValueError("PostFinance ZIP contains an unsupported entry")
730-                raw = archive.read(info)
731-                text, page_count = _pdf_text(raw)
732-                kind = _classify(info.filename, text)
733-                reference = _reference(info.filename, text)
734-                account = _account_reference(text)
735-                account_hash = _hash("account", account) if account else None
736:                role = (
737-                    "etrading_cash"
738-                    if kind
739-                    in {
740-                        "trade_confirmation",
741-                        "corporate_action",
742-                        "custody_fee",
743-                        "account_statement",
744-                        "interest_statement",
745-                    }
746-                    else "unresolved_transfer"
--
776-        raise ValueError("PostFinance source is not a valid ZIP") from exc
777-    # Every paired transfer contains one debit and one credit. In this verified export the
778-    # E-Trading statements independently evidence the credited leg; the debit leg is
779-    # E-Finance. This semantic rule is validated again for every pair below.
780-    resolved_docs: list[PFDocument] = []
781-    for d in documents:
782-        if d.account_role != "unresolved_transfer":
783-            resolved_docs.append(d)
784-            continue
785-        if "Zahlungsverkehr - Gutschrift" in d.text:
786:            role = "etrading_cash"
787-        elif "Zahlungsverkehr - Belastung" in d.text:
788:            role = "efinance"
789-        else:
790-            raise ValueError("PostFinance transfer role is ambiguous")
791-        resolved_docs.append(
792-            PFDocument(
793-                d.document_hash,
794-                d.filename_hash,
795-                d.document_type,
796-                d.semantic_reference,
797-                d.semantic_identity,
798-                d.document_date,


grep -n "account_map" -n src/jarvis_finance/services/portfolio_data.py | head -60 && sed -n '1450,1665p' src/jarvis_finance/services/portfolio_data.py


Output:
Chunk ID: a7e8ab
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 4039
Output:
217:    account_map = {row["account_id"]: row for row in accounts}
218:    placeholders = ",".join("?" for _ in account_map)
223:        (*account_map, period_from, period_to, cutoff),
278:            fingerprint, account_id, _safe_ref("account", account_id), _safe_label(account_map[account_id]["account_name"]),
291:    account_map = {row["account_id"]: row for row in accounts}
292:    placeholders = ",".join("?" for _ in account_map)
297:        (*account_map, period_from, period_to, cutoff),
340:            fingerprint, account_id, _safe_ref("account", account_id), _safe_label(account_map[account_id]["account_name"]),
353:    account_map = {row["account_id"]: row for row in accounts}
354:    placeholders = ",".join("?" for _ in account_map)
360:        (*account_map, period_from, period_to, cutoff),
389:            fingerprint, account_id, _safe_ref("account", account_id), _safe_label(account_map[account_id]["account_name"]),
1467:    account_map = {row["account_id"]: row for row in accounts}
1468:    if not account_map:
1471:    placeholders = ",".join("?" for _ in account_map)
1476:            ORDER BY trade_date, created_at, transaction_id""", (*account_map, as_of, cutoff)
1503:            ) SELECT * FROM ranked WHERE rn=1""", (*account_map, as_of, cutoff)
1509:    account_position_values: dict[str, Decimal] = {key: Decimal("0") for key in account_map}
1510:    account_position_dates: dict[str, set[str]] = {key: set() for key in account_map}
1592:            "account_ref": _safe_ref("account", account), "account_label": _safe_label(account_map[account]["account_name"]),
1613:    for account, meta in account_map.items():
1684:        f"SELECT COUNT(DISTINCT account_id) AS count FROM portfolio_valuation_snapshots WHERE account_id IN ({placeholders}) AND substr(valuation_at,1,10)<? AND datetime(captured_at)<=datetime(?)", (*account_map, as_of, cutoff)
1687:        f"SELECT COUNT(DISTINCT account_id) AS count FROM portfolio_valuation_snapshots WHERE account_id IN ({placeholders}) AND substr(valuation_at,1,10)=? AND datetime(captured_at)<=datetime(?)", (*account_map, as_of, cutoff)
1692:        "accounts": coverage(account_covered, len(account_map)),
1695:        "opening_valuations": coverage(opening_covered, len(account_map)),
1696:        "closing_valuations": coverage(closing_covered, len(account_map)),
1700:        "reconciliation": coverage(recon_covered + account_covered, instrument_total + len(account_map)),
    return _stored_decimal(row["rate"], "FX", positive=True), str(row["rate_date"]), str(row["provider"])

def build_portfolio_reconciliation(
    conn: Connection, *, as_of: str, data_cutoff: str | None = None, account_id: str | None = None,
    base_currency: str = "CHF", absolute_tolerance: str = "0.01", relative_tolerance: str = "0.001",
    limit: int = 200, offset: int = 0,
) -> dict[str, Any]:
    _date(as_of, "Stichtag")
    cutoff = _instant(data_cutoff or _now().isoformat(), "Data-Cutoff").isoformat()
    if base_currency.upper() != "CHF":
        raise ValueError("Reconciliation v1 unterstützt als Basiswährung ausschliesslich CHF")
    absolute = _decimal(absolute_tolerance, "Absolute Toleranz", non_negative=True)
    relative = _decimal(relative_tolerance, "Relative Toleranz", non_negative=True)
    if limit < 1 or limit > 500 or offset < 0:
        raise ValueError("Pagination ist ungültig")
    accounts = _validate_scope(conn, "account" if account_id else "portfolio", account_id)
    account_map = {row["account_id"]: row for row in accounts}
    if not account_map:
        empty_coverage = {key: {"covered": 0, "total": 0, "ratio": None} for key in ("accounts", "instruments", "activities", "opening_valuations", "closing_valuations", "prices", "fx", "cost_basis", "reconciliation")}
        return {"as_of": as_of, "data_cutoff": cutoff, "base_currency": "CHF", "status": "unavailable", "reason_codes": ["missing_account_valuation"], "tolerance": {"version": TOLERANCE_VERSION, "absolute": str(absolute), "relative": str(relative)}, "coverage": empty_coverage, "differences": [], "account_totals": [], "input_fingerprint": _hash([as_of, cutoff, []]), "sources": [], "limit": limit, "offset": offset, "total": 0}
    placeholders = ",".join("?" for _ in account_map)
    transaction_rows = conn.execute(
        f"""SELECT * FROM transactions WHERE account_id IN ({placeholders}) AND trade_date<=?
            AND datetime(created_at)<=datetime(?)
            AND COALESCE(is_voided,0)=0 AND COALESCE(is_confirmed,1)=1
            ORDER BY trade_date, created_at, transaction_id""", (*account_map, as_of, cutoff)
    ).fetchall()
    quantities: dict[tuple[str, str], Decimal] = {}
    activity_supported = 0
    cost_basis_keys: set[tuple[str, str]] = set()
    for row in transaction_rows:
        kind = str(row["activity_kind"] or row["transaction_type"] or "").lower()
        if kind in SUPPORTED_ACTIVITIES:
            activity_supported += 1
        if not row["instrument_id"] or row["quantity"] in (None, ""):
            continue
        key = (str(row["account_id"]), str(row["instrument_id"]))
        try:
            quantity = _stored_decimal(row["quantity"], "Menge", non_negative=True)
        except ValueError:
            continue
        if kind in {"buy", "initial_position_snapshot"}:
            quantities[key] = quantities.get(key, Decimal("0")) + quantity
            if row["gross_amount_original"] not in (None, ""):
                cost_basis_keys.add(key)
        elif kind in {"partial_sell", "full_sell", "sell"}:
            quantities[key] = quantities.get(key, Decimal("0")) - quantity
    reported_rows = conn.execute(
        f"""WITH ranked AS (
              SELECT p.*, ROW_NUMBER() OVER(PARTITION BY account_id,instrument_id ORDER BY snapshot_date DESC,created_at DESC,position_snapshot_id DESC) rn
              FROM positions_snapshot p WHERE account_id IN ({placeholders}) AND snapshot_date<=?
                AND datetime(created_at)<=datetime(?)
            ) SELECT * FROM ranked WHERE rn=1""", (*account_map, as_of, cutoff)
    ).fetchall()
    reported = {(str(row["account_id"]), str(row["instrument_id"])): row for row in reported_rows}
    keys = sorted(set(quantities) | set(reported))
    differences: list[dict[str, Any]] = []
    price_covered = fx_covered = recon_covered = 0
    account_position_values: dict[str, Decimal] = {key: Decimal("0") for key in account_map}
    account_position_dates: dict[str, set[str]] = {key: set() for key in account_map}
    for account, instrument in keys:
        ledger_qty = quantities.get((account, instrument))
        source = reported.get((account, instrument))
        source_qty = _stored_decimal(source["quantity"], "Gemeldete Menge") if source and source["quantity"] not in (None, "") else None
        reasons: list[str] = []
        if source and str(source["snapshot_date"]) != as_of:
            reasons.append("stale_snapshot")
        if source and str(source["data_quality_status"] or "").lower() not in {"ok", "fresh", "complete"}:
            reasons.append("source_error")
        quantity_status = "unavailable"
        quantity_diff: Decimal | None = None
        if ledger_qty is None or source_qty is None:
            reasons.append("missing_position_snapshot")
        else:
            quantity_diff = ledger_qty - source_qty
            if source and str(source["snapshot_date"]) != as_of:
                quantity_status = "not_comparable"
            else:
                quantity_status = _status(quantity_diff, source_qty, absolute, relative)
                if quantity_status == "mismatch":
                    reasons.append("quantity_mismatch")
        derived_value: Decimal | None = None
        reported_value = _stored_decimal(source["market_value_chf"], "Gemeldeter Positionswert") if source and source["market_value_chf"] not in (None, "") else None
        price_row = conn.execute(
            """SELECT close,currency,price_date,provider FROM market_prices WHERE instrument_id=? AND price_date<=?
               AND datetime(created_at)<=datetime(?)
               AND quality_status IN ('ok','fresh','complete') ORDER BY price_date DESC,created_at DESC,market_price_id DESC LIMIT 1""", (instrument, as_of, cutoff)
        ).fetchone()
        price_date = str(price_row["price_date"]) if price_row else None
        price_source = str(price_row["provider"]) if price_row else None
        fx_date = fx_source = None
        if source and price_row and str(source["snapshot_date"]) != price_date:
            reasons.append("cutoff_mismatch")
        if ledger_qty is None:
            reasons.append("missing_position_snapshot")
        elif not price_row:
            reasons.append("missing_price")
        else:
            if price_date != as_of:
                reasons.append("stale_price")
            else:
                price_covered += 1
            price = _stored_decimal(price_row["close"], "Marktpreis", non_negative=True)
            fx, fx_date, fx_source = _latest_fx(conn, str(price_row["currency"]).upper(), as_of, cutoff)
            if fx is None:
                reasons.append("missing_fx")
            else:
                if fx_date != as_of:
                    reasons.append("stale_fx")
                else:
                    fx_covered += 1
                derived_value = ledger_qty * price * fx
        value_status = "unavailable"
        value_diff: Decimal | None = None
        relative_diff: Decimal | None = None
        if derived_value is not None and reported_value is not None:
            if source and (
                "cutoff_mismatch" in reasons
                or "stale_snapshot" in reasons
                or "stale_price" in reasons
                or "stale_fx" in reasons
                or (fx_date and fx_date != price_date)
            ):
                value_status = "not_comparable"
                if "cutoff_mismatch" not in reasons:
                    reasons.append("cutoff_mismatch")
            else:
                value_diff = derived_value - reported_value
                relative_diff = value_diff / reported_value if reported_value != 0 else None
                value_status = _status(value_diff, reported_value, absolute, relative)
                if value_status == "mismatch":
                    reasons.append("value_mismatch")
                recon_covered += 1
        elif reported_value is None:
            reasons.append("missing_position_snapshot")
        if source and reported_value is not None:
            account_position_values[account] += reported_value
            account_position_dates[account].add(str(source["snapshot_date"]))
        instrument_row = conn.execute("SELECT name FROM instruments WHERE instrument_id=?", (instrument,)).fetchone()
        differences.append({
            "difference_ref": _safe_ref("difference", f"{account}:{instrument}:{as_of}"),
            "account_ref": _safe_ref("account", account), "account_label": _safe_label(account_map[account]["account_name"]),
            "instrument_ref": _safe_ref("instrument", instrument), "instrument_label": str(instrument_row["name"]) if instrument_row else "Instrument (unbekannt)",
            "quantity": {"ledger": _decimal_text(ledger_qty), "reported": _decimal_text(source_qty), "difference": _decimal_text(quantity_diff), "status": quantity_status},
            "valuation": {"derived": _decimal_text(derived_value), "reported": _decimal_text(reported_value), "difference": _decimal_text(value_diff), "relative_difference": _decimal_text(relative_diff), "status": value_status,
                          "position_as_of": str(source["snapshot_date"]) if source else None, "price_as_of": price_date, "fx_as_of": fx_date, "price_source": price_source, "fx_source": fx_source},
            "reason_codes": sorted(set(reasons)),
        })
    pf_roles = postfinance_account_roles(conn)
    official_pf_cash = latest_official_postfinance_cash(
        conn,
        as_of=as_of,
        data_cutoff=cutoff,
    )
    official_pf_cash_total = sum(
        (component.amount_chf for component in official_pf_cash), Decimal("0")
    )
    official_pf_cash_date = max(
        (component.snapshot_date for component in official_pf_cash), default=None
    )
    account_totals: list[dict[str, Any]] = []
    account_covered = 0
    for account, meta in account_map.items():
        cash = conn.execute(
            """SELECT amount_chf,balance_date,source FROM cash_account_snapshots WHERE account_id=? AND balance_date<=?
               AND datetime(created_at)<=datetime(?)
               ORDER BY balance_date DESC,created_at DESC,snapshot_id DESC LIMIT 1""", (account, as_of, cutoff)
        ).fetchone()
        cash_value = _stored_decimal(cash["amount_chf"], "Cashbestand") if cash else None
        cash_date = str(cash["balance_date"]) if cash else None
        if account == pf_roles.get("etrading_depot") and official_pf_cash:
            # The provider account total spans the distinct depot and trading-cash
            # roles. This is a reconciliation-only join, not an additive holding.
            cash_value = official_pf_cash_total
            cash_date = official_pf_cash_date
        legacy = conn.execute(
            """SELECT total_value_chf,valuation_date,source_type FROM account_value_snapshots
               WHERE account_id=? AND valuation_date<=? AND datetime(created_at)<=datetime(?)
                 AND COALESCE(is_active,1)=1
               ORDER BY valuation_date DESC,
                        CASE source_type WHEN 'truewealth_official_import' THEN 3 WHEN 'truewealth_manual_provisional' THEN 2 ELSE 1 END DESC,
                        COALESCE(valuation_at,created_at) DESC,created_at DESC,snapshot_id DESC LIMIT 1""", (account, as_of, cutoff)
        ).fetchone()
        reported_total = _stored_decimal(legacy["total_value_chf"], "Kontototal") if legacy else None
        total_date = str(legacy["valuation_date"]) if legacy else None
        has_positions = any(key[0] == account for key in reported)
        derived_total = account_position_values[account] + (cash_value or Decimal("0")) if has_positions or cash else None
        reasons: list[str] = []
        total_status = "unavailable"
        diff: Decimal | None = None
        total_value_only = meta["account_type"] in {"robo_advisor", "total_value", "pension", "truewealth"} or (legacy is not None and not any(key[0] == account for key in keys))
        canonical_date: str | None = None
        if total_value_only:
            canonical = conn.execute(
                """SELECT value_original,valuation_at FROM portfolio_valuation_snapshots
                   WHERE scope_kind='account' AND scope_id=? AND substr(valuation_at,1,10)<=?
                     AND datetime(captured_at)<=datetime(?)
                   ORDER BY valuation_at DESC,snapshot_version DESC,captured_at DESC,snapshot_id DESC LIMIT 1""", (account, as_of, cutoff)
            ).fetchone()
            derived_total = _stored_decimal(canonical["value_original"], "Kanonischer Totalwert") if canonical else None
            canonical_date = str(canonical["valuation_at"])[:10] if canonical else None
            if canonical and total_date != canonical_date:
                reasons.append("cutoff_mismatch")
        dates = set(account_position_dates[account]) | ({cash_date} if cash_date else set()) | ({total_date} if total_date else set()) | ({canonical_date} if canonical_date else set())
        if derived_total is None:
            reasons.append("missing_position_snapshot" if not total_value_only else "missing_account_valuation")
        if reported_total is None:
            reasons.append("missing_account_valuation")
        if derived_total is not None and reported_total is not None:
            stale_dates = {item for item in dates if item and item != as_of}
            if len({item for item in dates if item}) > 1 or stale_dates or "cutoff_mismatch" in reasons:
                total_status = "not_comparable"
                if stale_dates:
                    reasons.append("stale_snapshot")
                if "cutoff_mismatch" not in reasons:


grep -n "positions_snapshot" -n src/jarvis_finance/services/postfinance_service.py | head -30


Output:
Chunk ID: e7e388
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 168
Output:
164:        "positions_snapshot",
187:           FROM positions_snapshot p JOIN instruments i ON i.instrument_id=p.instrument_id
327:        "SELECT COUNT(*) FROM positions_snapshot WHERE account_id=? AND snapshot_date=?",
773:                """INSERT INTO positions_snapshot(position_snapshot_id,snapshot_date,account_id,platform_id,instrument_id,quantity,average_cost_original,cost_basis_original,market_price_original,market_value_original,market_fx_rate_to_chf,market_value_chf,portfolio_weight_pct,category,data_quality_status,source_type,source_reference,created_at) SELECT ?,?,?,a.platform_id,?,?,?,?,?,?,?,?,?,?,?,?,?,? FROM accounts a WHERE a.account_id=?""",


sed -n '748,790p' src/jarvis_finance/services/postfinance_service.py


Output:
Chunk ID: ebd15f
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 565
Output:
                    snapshot_id,
                    position.row_reference,
                    instrument_id,
                    position.asset_class,
                    _text(position.quantity),
                    _text(position.provider_average_cost_original),
                    _text(position.provider_cost_total_original),
                    _text(position.market_price_original),
                    position.price_currency,
                    _money(position.market_value_chf),
                    _text(position.weight_pct),
                    _text(calculated),
                    status,
                    json.dumps(
                        {
                            "provider": "official_account_overview",
                            "calculated": "documented_trade_fifo",
                            "estimated": False,
                        },
                        sort_keys=True,
                    ),
                    now,
                ),
            )
            conn.execute(
                """INSERT INTO positions_snapshot(position_snapshot_id,snapshot_date,account_id,platform_id,instrument_id,quantity,average_cost_original,cost_basis_original,market_price_original,market_value_original,market_fx_rate_to_chf,market_value_chf,portfolio_weight_pct,category,data_quality_status,source_type,source_reference,created_at) SELECT ?,?,?,a.platform_id,?,?,?,?,?,?,?,?,?,?,?,?,?,? FROM accounts a WHERE a.account_id=?""",
                (
                    _id("position"),
                    snapshot.valuation_at[:10],
                    accounts["etrading_depot"],
                    instrument_id,
                    _text(position.quantity),
                    _text(position.provider_average_cost_original),
                    _text(position.provider_cost_total_original),
                    _text(position.market_price_original),
                    _text(position.quantity * position.market_price_original),
                    None,
                    _money(position.market_value_chf),
                    _text(position.weight_pct),
                    position.asset_class,
                    "verified",
                    "postfinance_official_import",
                    snapshot_id,


Output:
Chunk ID: a4375d
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 19
Output:
2026-08-01T00:30:00+02:00 2026-07-31
2026-08-01T09:14:30+02:00 2026-08-01


[P2] Use local snapshot dates for as_of filters

When `snapshot_at` has a positive offset shortly after midnight, e.g. `2026-08-01T00:30:00+02:00`, SQLite `date()` converts it to UTC and returns `2026-07-31`. A historical `as_of=2026-07-31` can therefore select the Aug 1 PostFinance snapshot for cash, leaking future official balances into prior-day reports; the identical predicate in the positions helper has the same issue. Compare the stored local date portion or normalize explicitly instead.

The patch can select future PostFinance snapshots for historical as-of dates around timezone-offset midnight boundaries, which breaks cash and position projections for those reports.