Output:
Chunk ID: f0ca85
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 7033
Output:
diff --git a/src/jarvis_finance/services/equity_service.py b/src/jarvis_finance/services/equity_service.py
index 614e76b..acb3fca 100644
--- a/src/jarvis_finance/services/equity_service.py
+++ b/src/jarvis_finance/services/equity_service.py
@@ -108,24 +108,57 @@ def list_equity_positions(conn: Connection) -> list[EquityPosition]:
         and row.get("instrument_status", "").lower() != "inactive"
     ]
     analysis, values, missing = _latest_analysis(conn)
-    use_cache_fallback = analysis is None
-    expected_keys = {(str(row.get("account_id") or ""), str(row.get("instrument_id") or "")) for row in source_rows}
-    complete = bool(analysis and analysis.get("quality_status") == "complete" and set(values) == expected_keys)
-    total = sum((Decimal(str(item.get("value_chf") or "0")) for item in values.values()), Decimal("0"))
+    effective_values: dict[tuple[str, str], Decimal | None] = {}
+    for row in source_rows:
+        key = (str(row.get("account_id") or ""), str(row.get("instrument_id") or ""))
+        candidate = values.get(key)
+        row_is_audited = row.get("valuation_source") in {
+            "postfinance_official_import",
+            "audited_market_run",
+        }
+        candidate_is_newer = bool(
+            analysis
+            and candidate
+            and str(analysis.get("as_of") or "") > str(row.get("valuation_as_of") or row.get("price_date") or "")
+            and candidate.get("quality_status") == "fresh"
+        )
+        raw = (
+            candidate.get("value_chf")
+            if candidate_is_newer and candidate
+            else row.get("market_value_chf") if analysis is None or row_is_audited else None
+        )
+        effective_values[key] = Decimal(str(raw)) if raw not in {None, ""} else None
+    complete = bool(source_rows) and all(value is not None for value in effective_values.values())
+    total = sum((value or Decimal("0") for value in effective_values.values()), Decimal("0"))
     positions: list[EquityPosition] = []
     for row in source_rows:
         account_id = str(row.get("account_id") or "")
         instrument_id = str(row.get("instrument_id") or "")
         snapshot_value = values.get((account_id, instrument_id))
-        value = snapshot_value if snapshot_value and snapshot_value.get("quality_status") == "fresh" else None
+        value = (
+            snapshot_value
+            if analysis
+            and snapshot_value
+            and snapshot_value.get("quality_status") == "fresh"
+            and str(analysis.get("as_of") or "") > str(row.get("valuation_as_of") or row.get("price_date") or "")
+            else None
+        )
+        row_fallback_allowed = analysis is None or row.get("valuation_source") in {
+            "postfinance_official_import",
+            "audited_market_run",
+        }
         mapping = _mapping_details(conn, instrument_id)
-        raw_market_value = value.get("value_chf") if value else (row.get("market_value_chf") if use_cache_fallback else None)
-        if use_cache_fallback and Decimal(str(row.get("quantity") or "0")) <= 0:
+        raw_market_value = (
+            value.get("value_chf")
+            if value
+            else row.get("market_value_chf") if row_fallback_allowed else None
+        )
+        if Decimal(str(row.get("quantity") or "0")) <= 0:
             raw_market_value = None
         market_value = Decimal(str(raw_market_value)) if raw_market_value not in {None, ""} else None
         share = (market_value / total * Decimal("100")) if complete and market_value is not None and total > 0 else None
         code = "valued" if market_value is not None else ("stale_price" if snapshot_value else missing.get(instrument_id, "price_missing"))
-        status = "Bewertet" if value else ("Kurs veraltet" if snapshot_value else (_position_status(row) if use_cache_fallback else "Kurs fehlt"))
+        status = "Bewertet" if market_value is not None else ("Kurs veraltet" if snapshot_value else _position_status(row))
         provenance = value.get("price_input_provenance") if value else None
         provenance_provider = provenance.get("provider") if isinstance(provenance, dict) else None
         positions.append(EquityPosition(
@@ -138,11 +171,23 @@ def list_equity_positions(conn: Connection) -> list[EquityPosition]:
             asset_class=_asset_class_label(row.get("asset_class")),
             quantity=optional_decimal_text(row.get("quantity")) or "0",
             currency=str(value.get("currency") if value else mapping.get("currency") or row.get("price_currency") or row.get("currency") or ""),
-            price=optional_decimal_text(value.get("close")) if value else (optional_decimal_text(row.get("market_price_original") or row.get("price")) if use_cache_fallback else None),
+            price=(
+                optional_decimal_text(value.get("close"))
+                if value
+                else optional_decimal_text(row.get("market_price_original") or row.get("price"))
+                if row_fallback_allowed
+                else None
+            ),
             market_value_chf=optional_decimal_text(market_value, 2) if market_value is not None else None,
             portfolio_share_pct=optional_decimal_text(share, 2) if share is not None else None,
-            price_date=str(value.get("as_of")) if value else (str(analysis.get("as_of")) if analysis else None),
-            provider=str((value.get("provider") if value else None) or provenance_provider or mapping.get("provider") or ""),
+            price_date=(
+                str(analysis.get("as_of"))
+                if value and analysis
+                else str(row.get("valuation_as_of") or row.get("price_date") or "") or None
+                if row_fallback_allowed
+                else str(analysis.get("as_of")) if analysis else None
+            ),
+            provider=str((value.get("provider") if value else None) or provenance_provider or row.get("price_provider") or mapping.get("provider") or ""),
             provider_symbol=str(mapping.get("provider_symbol") or ""),
             exchange=str(mapping.get("provider_market") or ""),
             price_currency=str(value.get("currency") if value else mapping.get("currency") or row.get("price_currency") or row.get("currency") or ""),
@@ -161,6 +206,14 @@ def get_equity_summary(conn: Connection) -> EquityPortfolioSummary:
     complete = bool(positions) and len(valued) == len(positions)
     analysis, _, _ = _latest_analysis(conn)
     status = "complete" if complete else ("partial" if positions else "unavailable")
+    effective_as_of = max((item.price_date or "" for item in valued), default="") or None
+    analysis_as_of = str(analysis.get("as_of") or "") if analysis else ""
+    response_as_of = effective_as_of or analysis_as_of or None
+    last_successful = (
+        str(analysis.get("completed_at"))
+        if analysis and (not effective_as_of or analysis_as_of >= str(effective_as_of)[:10])
+        else effective_as_of
+    )
     return EquityPortfolioSummary(
         total_positions=len(positions),
         valued_positions=len(valued),
@@ -169,8 +222,8 @@ def get_equity_summary(conn: Connection) -> EquityPortfolioSummary:
         equity_value_chf=optional_decimal_text(partial, 2) if complete else None,
         valued_partial_chf=optional_decimal_text(partial, 2) or "0.00",
         value_label="Aktien-/ETF-Gesamtwert" if complete else "Bewerteter Teilwert",
-        as_of=str(analysis.get("as_of")) if analysis else None,
-        last_successful_run=str(analysis.get("completed_at")) if analysis else None,
+        as_of=response_as_of,
+        last_successful_run=last_successful,
         status=status,
         status_label=f"{len(valued)}/{len(positions)} bewertet" if positions else "Keine Aktien-/ETF-Positionen",
         unvalued_tickers=[item.ticker for item in positions if item.market_value_chf is None],
diff --git a/src/jarvis_finance/services/portfolio_analytics.py b/src/jarvis_finance/services/portfolio_analytics.py
index f6acbd6..d13272c 100644
--- a/src/jarvis_finance/services/portfolio_analytics.py
+++ b/src/jarvis_finance/services/portfolio_analytics.py
@@ -25,6 +25,10 @@ from jarvis_finance.market_data.prices import (
     exchange_matches,
     store_market_price,
 )
+from jarvis_finance.services.portfolio_aggregation import (
+    latest_official_postfinance_positions,
+    postfinance_account_roles,
+)
 from jarvis_finance.services.portfolio_performance import build_portfolio_performance

 ZERO = Decimal("0")
@@ -224,6 +228,41 @@ def confirmed_canonical_positions(conn: Connection, *, as_of: str | None = None)
         item = grouped.setdefault(key, dict(row) | {"quantity_total": ZERO})
         quantity = _decimal(row["quantity"])
         item["quantity_total"] += -abs(quantity) if str(row["transaction_type"]).lower() in SELL_TYPES else quantity
+    official = {
+        key: override
+        for key, override in latest_official_postfinance_positions(conn).items()
+        if not as_of or override.snapshot_date <= as_of
+    }
+    trading_cash_id = postfinance_account_roles(conn).get("etrading_cash")
+    official_instruments = {key[1] for key in official}
+    for key in list(grouped):
+        if key[0] == trading_cash_id and key[1] in official_instruments:
+            grouped.pop(key)
+    for key, override in official.items():
+        if as_of and override.snapshot_date > as_of:
+            continue
+        item = grouped.get(key)
+        if item is None:
+            instrument = conn.execute(
+                """SELECT isin,name,lower(asset_class) asset_class,
+                          upper(COALESCE(trading_currency,currency,'')) instrument_currency
+                   FROM instruments WHERE instrument_id=? AND is_active=1""",
+                (override.instrument_id,),
+            ).fetchone()
+            if not instrument:
+                continue
+            item = {
+                "account_id": override.account_id,
+                "instrument_id": override.instrument_id,
+                "isin": instrument["isin"],
+                "name": instrument["name"],
+                "asset_class": instrument["asset_class"],
+                "instrument_currency": instrument["instrument_currency"],
+            }
+            grouped[key] = item
+        # The latest official snapshot is the authoritative holding quantity. Historical
+        # buys already represented by the old baseline snapshot must not be added twice.
+        item["quantity_total"] = override.quantity
     return [
         ConfirmedPosition(
             account_id=item["account_id"],
diff --git a/src/jarvis_finance/services/portfolio_data.py b/src/jarvis_finance/services/portfolio_data.py
index 7a5762e..ebfa6cc 100644
--- a/src/jarvis_finance/services/portfolio_data.py
+++ b/src/jarvis_finance/services/portfolio_data.py
@@ -6,27 +6,33 @@ reconciliation are pure database projections. Confirm is the only write boundary

 from __future__ import annotations

-from dataclasses import dataclass
-from datetime import UTC, date, datetime, timedelta
-from decimal import Decimal, InvalidOperation
 import base64
 import binascii
 import hashlib
 import json
 import re
+import uuid
+from dataclasses import dataclass
+from datetime import UTC, date, datetime, timedelta
+from decimal import Decimal, InvalidOperation
 from sqlite3 import Connection, IntegrityError
 from typing import Any
-import uuid

 from jarvis_finance.audit.log import record_audit_event
+from jarvis_finance.imports.common import stable_id
 from jarvis_finance.imports.postfinance_etrading import (
     PostFinanceDocument,
-    decimal_text as postfinance_decimal_text,
     normalize_label,
     parse_postfinance_portfolio,
 )
-from jarvis_finance.imports.common import stable_id
+from jarvis_finance.imports.postfinance_etrading import (
+    decimal_text as postfinance_decimal_text,
+)
 from jarvis_finance.market_data.mappings import confirm_instrument_price_mapping
+from jarvis_finance.services.portfolio_aggregation import (
+    latest_official_postfinance_cash,
+    postfinance_account_roles,
+)

 INGESTION_VERSION = "portfolio_ingestion_v1"
 RECONCILIATION_VERSION = "portfolio_reconciliation_v1"
@@ -1434,7 +1440,8 @@ def _latest_fx(conn: Connection, currency: str, as_of: str, cutoff: str) -> tupl
         return Decimal("1"), as_of, "identity"
     row = conn.execute(
         """SELECT rate, rate_date, provider FROM fx_rates
-           WHERE base_currency=? AND quote_currency='CHF' AND rate_date<=? AND created_at<=?
+           WHERE base_currency=? AND quote_currency='CHF' AND rate_date<=?
+             AND datetime(created_at)<=datetime(?)
              AND rate IS NOT NULL AND quality_status IN ('ok','fresh','complete')
            ORDER BY rate_date DESC, created_at DESC, fx_rate_id DESC LIMIT 1""", (currency, as_of, cutoff)
     ).fetchone()
@@ -1463,7 +1470,8 @@ def build_portfolio_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 created_at<=?
+        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()
@@ -1490,7 +1498,8 @@ def build_portfolio_reconciliation(
     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 created_at<=?
+              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}
@@ -1523,7 +1532,8 @@ def build_portfolio_reconciliation(
         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 created_at<=?
+            """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
@@ -1586,18 +1596,33 @@ def build_portfolio_reconciliation(
                           "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)
+    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 created_at<=?
+            """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 created_at<=? AND COALESCE(is_active,1)=1
+               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)
@@ -1614,7 +1639,8 @@ def build_portfolio_reconciliation(
         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 captured_at<=?
+                   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
@@ -1651,10 +1677,10 @@ def build_portfolio_reconciliation(
         return {"covered": covered, "total": total, "ratio": _decimal_text(Decimal(covered) / Decimal(total)) if total else None}
     instrument_total = len(keys)
     opening_rows = conn.execute(
-        f"SELECT COUNT(DISTINCT account_id) AS count FROM portfolio_valuation_snapshots WHERE account_id IN ({placeholders}) AND substr(valuation_at,1,10)<? AND captured_at<=?", (*account_map, as_of, cutoff)
+        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)
     ).fetchone()
     closing_rows = conn.execute(
-        f"SELECT COUNT(DISTINCT account_id) AS count FROM portfolio_valuation_snapshots WHERE account_id IN ({placeholders}) AND substr(valuation_at,1,10)=? AND captured_at<=?", (*account_map, as_of, cutoff)
+        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)
     ).fetchone()
     opening_covered = int(opening_rows["count"] or 0)
     closing_covered = int(closing_rows["count"] or 0)
"""Central role-aware portfolio aggregation for verified PostFinance projections.

Official account totals are reconciliation controls, never additive holdings. Source FX
rates and line values can be rounded independently, so component CHF values are allocated
deterministically to the signed-off source subtotal without inventing an extra position.
"""

from __future__ import annotations

import json
from dataclasses import dataclass
from decimal import ROUND_HALF_UP, Decimal
from sqlite3 import Connection

MONEY = Decimal("0.01")

@dataclass(frozen=True)
class OfficialCashComponent:
    account_id: str
    currency: str
    amount_original: Decimal
    source_fx_rate_to_chf: Decimal
    amount_chf: Decimal
    snapshot_date: str
    valuation_at: str
    reconciliation_method: str = "official_total_pro_rata_rounded_fx_v1"

@dataclass(frozen=True)
class OfficialPositionOverride:
    account_id: str
    instrument_id: str
    asset_class: str
    quantity: Decimal
    market_price_original: Decimal
    price_currency: str
    market_value_chf: Decimal
    snapshot_date: str
    valuation_at: str
    reconciliation_method: str = "official_asset_class_residual_v1"

@dataclass(frozen=True)
class AuditedMarketValue:
    account_id: str
    instrument_id: str
    value_chf: Decimal
    close: Decimal | None
    currency: str
    as_of: str
    completed_at: str
    provider: str

def _decimal(value: object) -> Decimal:
    return Decimal(str(value))

def _money(value: Decimal) -> Decimal:
    return value.quantize(MONEY, rounding=ROUND_HALF_UP)

def postfinance_account_roles(conn: Connection) -> dict[str, str]:
    """Return canonical PostFinance role mappings without name-based inference."""

    return {
        str(row["role"]): str(row["account_id"])
        for row in conn.execute(
            "SELECT role,account_id FROM postfinance_account_roles ORDER BY role"
        ).fetchall()
    }

def _allocate_pro_rata(values: list[Decimal], target: Decimal) -> list[Decimal]:
    """Reconcile rounded source components to an authoritative signed-off subtotal."""

    if not values:
        return []
    raw_total = sum(values, Decimal("0"))
    if raw_total <= 0 or target < 0:
        raise ValueError("Official portfolio components cannot be reconciled")
    allocated = [_money(value * target / raw_total) for value in values]
    residual = _money(target - sum(allocated, Decimal("0")))
    if residual:
        largest = max(range(len(values)), key=values.__getitem__)
        allocated[largest] = _money(allocated[largest] + residual)
    if sum(allocated, Decimal("0")) != _money(target):
        raise ValueError("Official portfolio component allocation does not reconcile")
    return allocated

def latest_official_postfinance_cash(conn: Connection) -> list[OfficialCashComponent]:
    roles = postfinance_account_roles(conn)
    account_id = roles.get("etrading_cash")
    if not account_id:
        return []
    snapshot = conn.execute(
        """SELECT snapshot_id,snapshot_at,cash_chf
           FROM postfinance_snapshots
           ORDER BY snapshot_at DESC,created_at DESC LIMIT 1"""
    ).fetchone()
    if not snapshot:
        return []
    rows = conn.execute(
        """SELECT currency,amount_original,fx_rate_to_chf
           FROM postfinance_snapshot_cash WHERE snapshot_id=? ORDER BY currency""",
        (snapshot["snapshot_id"],),
    ).fetchall()
    raw_values = [
        _decimal(row["amount_original"]) * _decimal(row["fx_rate_to_chf"])
        for row in rows
    ]
    target = _money(_decimal(snapshot["cash_chf"]))
    allocated = _allocate_pro_rata(raw_values, target)
    valuation_at = str(snapshot["snapshot_at"])
    return [
        OfficialCashComponent(
            account_id=account_id,
            currency=str(row["currency"]),
            amount_original=_decimal(row["amount_original"]),
            source_fx_rate_to_chf=_decimal(row["fx_rate_to_chf"]),
            amount_chf=value,
            snapshot_date=valuation_at[:10],
            valuation_at=valuation_at,
        )
        for row, value in zip(rows, allocated, strict=True)
    ]

def latest_official_postfinance_positions(
    conn: Connection,
) -> dict[tuple[str, str], OfficialPositionOverride]:
    roles = postfinance_account_roles(conn)
    account_id = roles.get("etrading_depot")
    if not account_id:
        return {}
    snapshot = conn.execute(
        """SELECT snapshot_id,snapshot_at,stocks_chf,etfs_chf
           FROM postfinance_snapshots
           ORDER BY snapshot_at DESC,created_at DESC LIMIT 1"""
    ).fetchone()
    if not snapshot:
        return {}
    rows = conn.execute(
        """SELECT instrument_id,asset_class,quantity,price_original,price_currency,value_chf
           FROM postfinance_snapshot_positions
           WHERE snapshot_id=? ORDER BY asset_class,instrument_id""",
        (snapshot["snapshot_id"],),
    ).fetchall()
    targets = {
        "stock": _money(_decimal(snapshot["stocks_chf"])),
        "etf": _money(_decimal(snapshot["etfs_chf"])),
    }
    allocated_by_instrument: dict[str, Decimal] = {}
    for asset_class, target in targets.items():
        members = [row for row in rows if str(row["asset_class"]).lower() == asset_class]
        allocated = _allocate_pro_rata(
            [_decimal(row["value_chf"]) for row in members], target
        )
        allocated_by_instrument.update(
            {
                str(row["instrument_id"]): value
                for row, value in zip(members, allocated, strict=True)
            }
        )
    valuation_at = str(snapshot["snapshot_at"])
    return {
        (account_id, str(row["instrument_id"])): OfficialPositionOverride(
            account_id=account_id,
            instrument_id=str(row["instrument_id"]),
            asset_class=str(row["asset_class"]).lower(),
            quantity=_decimal(row["quantity"]),
            market_price_original=_decimal(row["price_original"]),
            price_currency=str(row["price_currency"]),
            market_value_chf=allocated_by_instrument[str(row["instrument_id"])],
            snapshot_date=valuation_at[:10],
            valuation_at=valuation_at,
        )
        for row in rows
    }

def latest_audited_market_values(
    conn: Connection,
) -> dict[tuple[str, str], AuditedMarketValue]:
    """Return the latest immutable market-analysis values without writing on GET."""

    row = conn.execute(
        """SELECT pas.as_of,pas.summary_json,mdr.completed_at
           FROM portfolio_analysis_snapshots pas
           JOIN market_data_runs mdr ON mdr.run_id=pas.run_id
           ORDER BY pas.as_of DESC,pas.created_at DESC LIMIT 1"""
    ).fetchone()
    if not row:
        return {}
    try:
        payload = json.loads(row["summary_json"] or "{}")
    except json.JSONDecodeError:
        return {}
    values: dict[tuple[str, str], AuditedMarketValue] = {}
    for item in payload.get("positions", []):
        if not isinstance(item, dict) or item.get("quality_status") != "fresh":
            continue
        account_id = str(item.get("account_id") or "")
        instrument_id = str(item.get("instrument_id") or "")
        if not account_id or not instrument_id or item.get("value_chf") in {None, ""}:
            continue
        provenance = item.get("price_input_provenance")
        provider = (
            str(item.get("provider") or "")
            or (str(provenance.get("provider") or "") if isinstance(provenance, dict) else "")
        )
        values[(account_id, instrument_id)] = AuditedMarketValue(
            account_id=account_id,
            instrument_id=instrument_id,
            value_chf=_decimal(item["value_chf"]),
            close=_decimal(item["close"]) if item.get("close") not in {None, ""} else None,
            currency=str(item.get("currency") or ""),
            as_of=str(row["as_of"]),
            completed_at=str(row["completed_at"]),
            provider=provider,
        )
    return values


grep -R "postfinance_account_roles\|postfinance_snapshots\|postfinance_snapshot_positions\|portfolio_analysis_snapshots" -n src tests | head -80


Output:
Chunk ID: 18d1c2
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 2368
Output:
grep: src/jarvis_finance/storage/__pycache__/migrations.cpython-311.pyc: binary file matches
grep: src/jarvis_finance/storage/__pycache__/postfinance_schema.cpython-311.pyc: binary file matches
grep: src/jarvis_finance/dashboard/__pycache__/data.cpython-312.pyc: binary file matches
grep: src/jarvis_finance/dashboard/__pycache__/data.cpython-311.pyc: binary file matches
grep: src/jarvis_finance/services/__pycache__/postfinance_service.cpython-311.pyc: binary file matches
grep: src/jarvis_finance/services/__pycache__/portfolio_data.cpython-311.pyc: binary file matches
grep: src/jarvis_finance/services/__pycache__/portfolio_aggregation.cpython-311.pyc: binary file matches
grep: src/jarvis_finance/services/__pycache__/equity_service.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_aggregation.cpython-312.pyc: binary file matches
grep: src/jarvis_finance/services/__pycache__/equity_service.cpython-312.pyc: binary file matches
grep: src/jarvis_finance/services/__pycache__/portfolio_analytics.cpython-311.pyc: binary file matches
grep: src/jarvis_finance/services/__pycache__/postfinance_service.cpython-312.pyc: binary file matches
grep: tests/__pycache__/test_postfinance_sprint13.cpython-311-pytest-9.1.1.pyc: binary file matches
src/jarvis_finance/storage/migrations.py:1891:        CREATE TABLE IF NOT EXISTS portfolio_analysis_snapshots (
src/jarvis_finance/storage/migrations.py:1907:        CREATE INDEX IF NOT EXISTS idx_portfolio_analysis_snapshots_time
src/jarvis_finance/storage/migrations.py:1908:            ON portfolio_analysis_snapshots(as_of DESC, created_at DESC);
src/jarvis_finance/storage/migrations.py:1919:        CREATE TRIGGER IF NOT EXISTS portfolio_analysis_snapshots_immutable
src/jarvis_finance/storage/migrations.py:1920:        BEFORE UPDATE ON portfolio_analysis_snapshots
src/jarvis_finance/storage/migrations.py:1922:        CREATE TRIGGER IF NOT EXISTS portfolio_analysis_snapshots_no_delete
src/jarvis_finance/storage/migrations.py:1923:        BEFORE DELETE ON portfolio_analysis_snapshots
src/jarvis_finance/storage/postfinance_schema.py:15:        CREATE TABLE IF NOT EXISTS postfinance_account_roles (
src/jarvis_finance/storage/postfinance_schema.py:56:        CREATE TABLE IF NOT EXISTS postfinance_snapshots (
src/jarvis_finance/storage/postfinance_schema.py:75:        CREATE TABLE IF NOT EXISTS postfinance_snapshot_positions (
src/jarvis_finance/storage/postfinance_schema.py:77:            snapshot_id TEXT NOT NULL REFERENCES postfinance_snapshots(snapshot_id),
src/jarvis_finance/storage/postfinance_schema.py:97:            snapshot_id TEXT NOT NULL REFERENCES postfinance_snapshots(snapshot_id),
src/jarvis_finance/storage/postfinance_schema.py:172:        CREATE TRIGGER IF NOT EXISTS postfinance_snapshots_immutable BEFORE UPDATE ON postfinance_snapshots
src/jarvis_finance/storage/postfinance_schema.py:174:        CREATE TRIGGER IF NOT EXISTS postfinance_snapshots_no_delete BEFORE DELETE ON postfinance_snapshots
src/jarvis_finance/storage/postfinance_schema.py:176:        CREATE TRIGGER IF NOT EXISTS postfinance_snapshot_positions_immutable BEFORE UPDATE ON postfinance_snapshot_positions
src/jarvis_finance/storage/postfinance_schema.py:178:        CREATE TRIGGER IF NOT EXISTS postfinance_snapshot_positions_no_delete BEFORE DELETE ON postfinance_snapshot_positions
src/jarvis_finance/dashboard/data.py:15:    postfinance_account_roles,
src/jarvis_finance/dashboard/data.py:562:    roles = postfinance_account_roles(conn)
src/jarvis_finance/services/portfolio_aggregation.py:64:def postfinance_account_roles(conn: Connection) -> dict[str, str]:
src/jarvis_finance/services/portfolio_aggregation.py:70:            "SELECT role,account_id FROM postfinance_account_roles ORDER BY role"
src/jarvis_finance/services/portfolio_aggregation.py:94:    roles = postfinance_account_roles(conn)
src/jarvis_finance/services/portfolio_aggregation.py:100:           FROM postfinance_snapshots
src/jarvis_finance/services/portfolio_aggregation.py:134:    roles = postfinance_account_roles(conn)
src/jarvis_finance/services/portfolio_aggregation.py:140:           FROM postfinance_snapshots
src/jarvis_finance/services/portfolio_aggregation.py:147:           FROM postfinance_snapshot_positions
src/jarvis_finance/services/portfolio_aggregation.py:191:           FROM portfolio_analysis_snapshots pas
src/jarvis_finance/services/equity_service.py:32:             FROM portfolio_analysis_snapshots pas
src/jarvis_finance/services/portfolio_data.py:34:    postfinance_account_roles,
src/jarvis_finance/services/portfolio_data.py:1599:    pf_roles = postfinance_account_roles(conn)
src/jarvis_finance/services/portfolio_analytics.py:30:    postfinance_account_roles,
src/jarvis_finance/services/portfolio_analytics.py:236:    trading_cash_id = postfinance_account_roles(conn).get("etrading_cash")
src/jarvis_finance/services/portfolio_analytics.py:728:            """INSERT OR REPLACE INTO portfolio_analysis_snapshots(
src/jarvis_finance/services/portfolio_analytics.py:926:    latest = conn.execute("SELECT * FROM portfolio_analysis_snapshots ORDER BY as_of DESC,created_at DESC LIMIT 1").fetchone()
src/jarvis_finance/services/postfinance_service.py:541:                "INSERT OR IGNORE INTO postfinance_account_roles(role,account_id,source_reference_hash,created_at) VALUES(?,?,NULLgrep: tests/__pycache__/test_postfinance_sprint13.cpython-311.pyc: binary file matches
grep: tests/__pycache__/test_postfinance_sprint13.cpython-311-pytest-9.0.2.pyc: binary file matches
grep: tests/unit/__pycache__/test_portfolio_market_analytics_v1.cpython-311.pyc: binary file matches
grep: tests/unit/__pycache__/test_portfolio_market_analytics_v1.cpython-311-pytest-9.0.2.pyc: binary file matches
grep: tests/unit/__pycache__/test_portfolio_market_analytics_v1.cpython-311-pytest-9.1.1.pyc: binary file matches
,?)",
src/jarvis_finance/services/postfinance_service.py:545:                "SELECT account_id FROM postfinance_account_roles WHERE role=?", (role,)
src/jarvis_finance/services/postfinance_service.py:602:            """INSERT INTO postfinance_snapshots(snapshot_id,batch_id,account_id,snapshot_at,total_chf,securities_chf,cash_chf,stocks_chf,etfs_chf,component_total_chf,difference_chf,tolerance_chf,position_count,cash_count,open_orders,reconciliation_status,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,? ,?,?,?,?,?)""",
src/jarvis_finance/services/postfinance_service.py:745:                """INSERT INTO postfinance_snapshot_positions(snapshot_position_id,snapshot_id,source_row_reference,instrument_id,asset_class,quantity,provider_average_cost_original,provider_cost_total_original,price_original,price_currency,value_chf,weight_pct,computed_cost_basis_original,computed_cost_basis_status,provenance_json,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
src/jarvis_finance/services/postfinance_service.py:873:           FROM postfinance_snapshots ORDER BY snapshot_at DESC,created_at DESC LIMIT 1"""
src/jarvis_finance/services/postfinance_service.py:908:               FROM postfinance_snapshot_positions p
src/jarvis_finance/services/postfinance_service.py:937:               FROM postfinance_import_batches b JOIN postfinance_snapshots s ON s.batch_id=b.batch_id
tests/unit/test_portfolio_market_analytics_v1.py:177:    payload = json.loads(conn.execute("SELECT summary_json FROM portfolio_analysis_snapshots").fetchone()[0])
tests/unit/test_portfolio_market_analytics_v1.py:181:    assert conn.execute("SELECT total_value_chf FROM portfolio_analysis_snapshots").fetchone()[0] == "1635.30"
tests/unit/test_portfolio_market_analytics_v1.py:270:    payload = json.loads(conn.execute("SELECT summary_json FROM portfolio_analysis_snapshots").fetchone()[0])
tests/unit/test_portfolio_market_analytics_v1.py:356:    values = json.loads(conn.execute("SELECT summary_json FROM portfolio_analysis_snapshots").fetchone()[0])["positions"]
tests/test_postfinance_sprint13.py:272:    assert conn.execute("SELECT COUNT(*) FROM postfinance_snapshots").fetchone()[0] == 1
tests/test_postfinance_sprint13.py:273:    assert conn.execute("SELECT COUNT(*) FROM postfinance_snapshot_positions").fetchone()[0] == 22
tests/test_postfinance_sprint13.py:274:    assert conn.execute("SELECT COUNT(*) FROM postfinance_account_roles").fetchone()[0] == 3
tests/test_postfinance_sprint13.py:275:    roles = dict(conn.execute("SELECT role,account_id FROM postfinance_account_roles"))
tests/test_postfinance_sprint13.py:320:        "SELECT provider_cost_total_original,computed_cost_basis_original,computed_cost_basis_status FROM postfinance_snapshot_positions WHERE computed_cost_basis_original IS NOT NULL"
tests/test_postfinance_sprint13.py:366:        "SELECT computed_cost_basis_original,computed_cost_basis_status FROM postfinance_snapshot_positions WHERE computed_cost_basis_original IS NOT NULL"
tests/test_postfinance_sprint13.py:442:            "UPDATE postfinance_snapshots SET total_chf='0' WHERE snapshot_id=?",
tests/test_postfinance_sprint13.py:447:            "DELETE FROM postfinance_snapshots WHERE snapshot_id=?", (result["snapshot_id"],)
tests/test_postfinance_sprint13.py:451:        "postfinance_snapshot_positions": "created_at=created_at",
tests/test_postfinance_sprint13.py:585:        """INSERT INTO portfolio_analysis_snapshots(analysis_snapshot_id,run_id,as_of,


pytest -q tests/test_postfinance_sprint13.py tests/unit/test_portfolio_market_analytics_v1.py


Output:
Chunk ID: 993abe
Wall time: 13.5398 seconds
Process exited with code 0
Original token count: 25
Output:
.......                                                                  [100%]
7 passed in 13.22s


sed -n '1,260p' src/jarvis_finance/services/equity_service.py && sed -n '520,780p' src/jarvis_finance/services/postfinance_service.py


Output:
Chunk ID: 837956
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 6303
Output:
from __future__ import annotations

import json
from collections.abc import Mapping
from decimal import Decimal
from sqlite3 import Connection

from fastapi import HTTPException

from jarvis_finance.api.schemas.positions import EquityPortfolioSummary, EquityPosition
from jarvis_finance.dashboard import data as dashboard_data
from jarvis_finance.services.api_helpers import optional_decimal_text

def _asset_class_label(value: object) -> str:
    return "ETF" if str(value or "").lower() == "etf" else "Aktie"

def _account_label(row: dict[str, str]) -> str:
    platform = str(row.get("platform") or "").strip()
    account = str(row.get("account_name") or "").strip()
    if "postfinance" in platform.lower():
        if not account or "manual" in account.lower():
            return "PostFinance E-Trading"
        return account if "postfinance" in account.lower() or "e-trading" in account.lower() else f"PostFinance · {account}"
    return account or platform

def _latest_analysis(conn: Connection) -> tuple[dict[str, object] | None, dict[tuple[str, str], dict[str, object]], dict[str, str]]:
    row = conn.execute(
        """SELECT pas.*,mdr.completed_at,mdr.missing_instruments_json
             FROM portfolio_analysis_snapshots pas
             JOIN market_data_runs mdr ON mdr.run_id=pas.run_id
            ORDER BY pas.as_of DESC,pas.created_at DESC LIMIT 1"""
    ).fetchone()
    if not row:
        return None, {}, {}
    try:
        payload = json.loads(row["summary_json"] or "{}")
    except json.JSONDecodeError:
        payload = {}
    values = {
        (str(item.get("account_id") or ""), str(item.get("instrument_id") or "")): item
        for item in payload.get("positions", [])
        if isinstance(item, dict)
    }
    try:
        missing_rows = json.loads(row["missing_instruments_json"] or "[]")
    except json.JSONDecodeError:
        missing_rows = []
    missing = {
        str(item.get("instrument_id") or ""): str(item.get("reason_code") or "price_missing")
        for item in missing_rows
        if isinstance(item, dict)
    }
    return dict(row), values, missing

def _status_reason(code: str) -> str:
    labels = {
        "fmp_endpoint_restricted": "Der primäre Provider beschränkt diesen historischen Kurs im aktuellen Tarif.",
        "plan_restricted": "Der Provider beschränkt diesen historischen Kurs im aktuellen Tarif.",
        "fmp_rate_limited": "Das Provider-Limit wurde erreicht. Der Lauf kann kontrolliert fortgesetzt werden.",
        "rate_limited": "Das Provider-Limit wurde erreicht. Der Lauf kann kontrolliert fortgesetzt werden.",
        "currency_mismatch": "Die Kurswährung stimmt nicht mit der bestätigten Instrumentwährung überein.",
        "exchange_mismatch": "Der Handelsplatz des Provider-Ergebnisses ist nicht bestätigt.",
        "future_price_rejected": "Der gelieferte Kurs liegt nach dem zulässigen Börsenstichtag.",
        "stale_price": "Der letzte bestätigte Kurs ist für den Börsenstichtag zu alt und wird nicht bewertet.",
        "mapping_required": "Die Provider-Zuordnung ist noch nicht eindeutig bestätigt.",
        "fx_rate_missing": "Für die CHF-Bewertung fehlt ein bestätigter FX-Kurs.",
    }
    return labels.get(code, "Für den Börsenstichtag konnte noch kein bestätigter Kurs geladen werden.")

def _position_status(row: Mapping[str, object]) -> str:
    """Compatibility status when no audited market-analysis snapshot exists yet."""

    if "quantity" in row and Decimal(str(row.get("quantity") or "0")) <= 0:
        return "Einstand unvollständig"
    market_value = row.get("market_value_chf")
    warnings = str(row.get("quality_warnings") or "")
    if market_value not in {None, ""}:
        if "missing_cost_basis" in warnings or "missing_fx" in warnings:
            return "Einstand unvollständig"
        return "Bewertet"
    if str(row.get("price_status") or "") != "ok":
        return "Kurs fehlt"
    if str(row.get("fx_status") or "") == "missing_fx":
        return "FX fehlt für Bewertung"
    return "Bewertung unvollständig"

def _mapping_details(conn: Connection, instrument_id: str) -> dict[str, str | None]:
    row = conn.execute(
        """SELECT provider,provider_symbol,provider_market,upper(COALESCE(trading_currency,currency,'')) currency
             FROM instrument_price_mappings
            WHERE instrument_id=? AND mapping_status='mapped'
            ORDER BY updated_at DESC,mapping_id LIMIT 1""",
        (instrument_id,),
    ).fetchone()
    return dict(row) if row else {"provider": None, "provider_symbol": None, "provider_market": None, "currency": None}

def list_equity_positions(conn: Connection) -> list[EquityPosition]:
    source_rows = [
        row for row in dashboard_data.get_positions(conn)
        if row.get("asset_class", "").lower() in {"stock", "equity", "etf"}
        and row.get("instrument_status", "").lower() != "inactive"
    ]
    analysis, values, missing = _latest_analysis(conn)
    effective_values: dict[tuple[str, str], Decimal | None] = {}
    for row in source_rows:
        key = (str(row.get("account_id") or ""), str(row.get("instrument_id") or ""))
        candidate = values.get(key)
        row_is_audited = row.get("valuation_source") in {
            "postfinance_official_import",
            "audited_market_run",
        }
        candidate_is_newer = bool(
            analysis
            and candidate
            and str(analysis.get("as_of") or "") > str(row.get("valuation_as_of") or row.get("price_date") or "")
            and candidate.get("quality_status") == "fresh"
        )
        raw = (
            candidate.get("value_chf")
            if candidate_is_newer and candidate
            else row.get("market_value_chf") if analysis is None or row_is_audited else None
        )
        effective_values[key] = Decimal(str(raw)) if raw not in {None, ""} else None
    complete = bool(source_rows) and all(value is not None for value in effective_values.values())
    total = sum((value or Decimal("0") for value in effective_values.values()), Decimal("0"))
    positions: list[EquityPosition] = []
    for row in source_rows:
        account_id = str(row.get("account_id") or "")
        instrument_id = str(row.get("instrument_id") or "")
        snapshot_value = values.get((account_id, instrument_id))
        value = (
            snapshot_value
            if analysis
            and snapshot_value
            and snapshot_value.get("quality_status") == "fresh"
            and str(analysis.get("as_of") or "") > str(row.get("valuation_as_of") or row.get("price_date") or "")
            else None
        )
        row_fallback_allowed = analysis is None or row.get("valuation_source") in {
            "postfinance_official_import",
            "audited_market_run",
        }
        mapping = _mapping_details(conn, instrument_id)
        raw_market_value = (
            value.get("value_chf")
            if value
            else row.get("market_value_chf") if row_fallback_allowed else None
        )
        if Decimal(str(row.get("quantity") or "0")) <= 0:
            raw_market_value = None
        market_value = Decimal(str(raw_market_value)) if raw_market_value not in {None, ""} else None
        share = (market_value / total * Decimal("100")) if complete and market_value is not None and total > 0 else None
        code = "valued" if market_value is not None else ("stale_price" if snapshot_value else missing.get(instrument_id, "price_missing"))
        status = "Bewertet" if market_value is not None else ("Kurs veraltet" if snapshot_value else _position_status(row))
        provenance = value.get("price_input_provenance") if value else None
        provenance_provider = provenance.get("provider") if isinstance(provenance, dict) else None
        positions.append(EquityPosition(
            position_id=f"{account_id}:{instrument_id}",
            instrument_id=instrument_id,
            name=str(row.get("name") or ""),
            ticker=str(row.get("ticker") or ""),
            isin=str(row.get("isin") or ""),
            account=_account_label(row),
            asset_class=_asset_class_label(row.get("asset_class")),
            quantity=optional_decimal_text(row.get("quantity")) or "0",
            currency=str(value.get("currency") if value else mapping.get("currency") or row.get("price_currency") or row.get("currency") or ""),
            price=(
                optional_decimal_text(value.get("close"))
                if value
                else optional_decimal_text(row.get("market_price_original") or row.get("price"))
                if row_fallback_allowed
                else None
            ),
            market_value_chf=optional_decimal_text(market_value, 2) if market_value is not None else None,
            portfolio_share_pct=optional_decimal_text(share, 2) if share is not None else None,
            price_date=(
                str(analysis.get("as_of"))
                if value and analysis
                else str(row.get("valuation_as_of") or row.get("price_date") or "") or None
                if row_fallback_allowed
                else str(analysis.get("as_of")) if analysis else None
            ),
            provider=str((value.get("provider") if value else None) or provenance_provider or row.get("price_provider") or mapping.get("provider") or ""),
            provider_symbol=str(mapping.get("provider_symbol") or ""),
            exchange=str(mapping.get("provider_market") or ""),
            price_currency=str(value.get("currency") if value else mapping.get("currency") or row.get("price_currency") or row.get("currency") or ""),
            data_status="fresh" if market_value is not None else "missing",
            status_code=code,
            status_reason=None if market_value is not None else _status_reason(code),
            status=status,
        ))
    return sorted(positions, key=lambda item: Decimal(item.market_value_chf or "-1"), reverse=True)

def get_equity_summary(conn: Connection) -> EquityPortfolioSummary:
    positions = list_equity_positions(conn)
    valued = [item for item in positions if item.market_value_chf is not None]
    partial = sum((Decimal(item.market_value_chf or "0") for item in valued), Decimal("0"))
    complete = bool(positions) and len(valued) == len(positions)
    analysis, _, _ = _latest_analysis(conn)
    status = "complete" if complete else ("partial" if positions else "unavailable")
    effective_as_of = max((item.price_date or "" for item in valued), default="") or None
    analysis_as_of = str(analysis.get("as_of") or "") if analysis else ""
    response_as_of = effective_as_of or analysis_as_of or None
    last_successful = (
        str(analysis.get("completed_at"))
        if analysis and (not effective_as_of or analysis_as_of >= str(effective_as_of)[:10])
        else effective_as_of
    )
    return EquityPortfolioSummary(
        total_positions=len(positions),
        valued_positions=len(valued),
        unvalued_positions=len(positions) - len(valued),
        coverage_complete=complete,
        equity_value_chf=optional_decimal_text(partial, 2) if complete else None,
        valued_partial_chf=optional_decimal_text(partial, 2) or "0.00",
        value_label="Aktien-/ETF-Gesamtwert" if complete else "Bewerteter Teilwert",
        as_of=response_as_of,
        last_successful_run=last_successful,
        status=status,
        status_label=f"{len(valued)}/{len(positions)} bewertet" if positions else "Keine Aktien-/ETF-Positionen",
        unvalued_tickers=[item.ticker for item in positions if item.market_value_chf is None],
    )

def get_equity_position(conn: Connection, position_id: str) -> EquityPosition:
    position = next((p for p in list_equity_positions(conn) if p.position_id == position_id), None)
    if position is None:
        raise HTTPException(status_code=404, detail="Equity position not found")
    return position
        event.event_fingerprint: _event_instrument(event, bundle, position_map, conn)
        for event in bundle.events
    }
    archive_refs: list[tuple[str, bool]] = []
    zip_ref, created = _archive(zip_raw, bundle.zip_hash, "zip")
    archive_refs.append((zip_ref, created))
    overview_ref, created = _archive(overview_raw, bundle.overview_hash, "pdf")
    archive_refs.append((overview_ref, created))
    for document in bundle.documents:
        reference, created = _archive(document.raw, document.document_hash, "pdf")
        archive_refs.append((reference, created))
    now = _now()
    batch_id = _id("pfbatch")
    snapshot_id = _id("pfsnapshot")
    try:
        conn.execute("BEGIN IMMEDIATE")
        if _revision(conn, accounts) != preview["db_revision"]:
            raise ValueError("PostFinance database revision changed before persistence")
        _ensure_efinance_account(conn, accounts, now)
        for role, account_id in accounts.items():
            conn.execute(
                "INSERT OR IGNORE INTO postfinance_account_roles(role,account_id,source_reference_hash,created_at) VALUES(?,?,NULL,?)",
                (role, account_id, now),
            )
            mapped = conn.execute(
                "SELECT account_id FROM postfinance_account_roles WHERE role=?", (role,)
            ).fetchone()
            if not mapped or mapped["account_id"] != account_id:
                raise ValueError(
                    "PostFinance account-role mapping conflicts with confirmed mapping"
                )
        audit_id = _audit(conn, batch_id, bundle, now)
        conn.execute(
            """INSERT INTO postfinance_import_batches(batch_id,bundle_sha256,zip_sha256,overview_sha256,parser_id,parser_version,db_revision,archive_reference,snapshot_at,status,audit_id,confirmed_at,confirmed_by) VALUES(?,?,?,?,?,?,?,?,?,'confirmed',?,?,'user')""",
            (
                batch_id,
                bundle.bundle_hash,
                bundle.zip_hash,
                bundle.overview_hash,
                PARSER_ID,
                PARSER_VERSION,
                preview["db_revision"],
                zip_ref,
                bundle.snapshot.valuation_at,
                audit_id,
                now,
            ),
        )
        for document in bundle.documents:
            reference = f"postfinance/archive/{document.document_hash}.pdf"
            ref_hash = _hash(document.semantic_reference)
            existing = conn.execute(
                "SELECT document_hash FROM postfinance_documents WHERE semantic_identity=?",
                (document.semantic_identity,),
            ).fetchone()
            if existing and existing["document_hash"] != document.document_hash:
                raise ValueError("PostFinance semantic document conflict")
            conn.execute(
                """INSERT OR IGNORE INTO postfinance_documents(document_hash,filename_hash,semantic_identity,semantic_reference_hash,document_type,document_date,account_reference_hash,account_role,page_count,archive_reference,parser_version,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)""",
                (
                    document.document_hash,
                    document.filename_hash,
                    document.semantic_identity,
                    ref_hash,
                    document.document_type,
                    document.document_date,
                    document.account_reference_hash,
                    document.account_role,
                    document.page_count,
                    reference,
                    PARSER_VERSION,
                    now,
                ),
            )
            conn.execute(
                "INSERT INTO postfinance_batch_documents(batch_id,document_hash) VALUES(?,?)",
                (batch_id, document.document_hash),
            )
        snapshot = bundle.snapshot
        component_total = snapshot.securities_total_chf + snapshot.cash_total_chf
        difference = snapshot.total_value_chf - component_total
        conn.execute(
            """INSERT INTO postfinance_snapshots(snapshot_id,batch_id,account_id,snapshot_at,total_chf,securities_chf,cash_chf,stocks_chf,etfs_chf,component_total_chf,difference_chf,tolerance_chf,position_count,cash_count,open_orders,reconciliation_status,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,? ,?,?,?,?,?)""",
            (
                snapshot_id,
                batch_id,
                accounts["etrading_depot"],
                snapshot.valuation_at,
                _money(snapshot.total_value_chf),
                _money(snapshot.securities_total_chf),
                _money(snapshot.cash_total_chf),
                _money(snapshot.stock_total_chf),
                _money(snapshot.etf_total_chf),
                _money(component_total),
                _money(difference),
                _money(TOLERANCE),
                len(snapshot.positions),
                len(snapshot.cash),
                snapshot.open_orders,
                "matched" if difference == 0 else "within_tolerance",
                now,
            ),
        )
        for event in bundle.events:
            instrument_id = event_instruments[event.event_fingerprint]
            event_id = f"pfevent_{event.event_fingerprint[:24]}"
            account_id = accounts[event.account_role]
            conn.execute(
                """INSERT INTO postfinance_ledger_events(event_id,event_fingerprint,document_hash,semantic_reference_hash,event_type,account_role,account_id,occurred_on,settlement_on,direction,instrument_id,quantity,price_original,gross_original,fee_original,tax_original,net_original,currency,fx_rate_to_chf,internal_transfer_group,quality_status,reason_codes_json,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
                (
                    event_id,
                    event.event_fingerprint,
                    event.document_hash,
                    _hash(event.semantic_reference),
                    event.event_type,
                    event.account_role,
                    account_id,
                    event.occurred_on,
                    event.settlement_on,
                    event.direction or "neutral",
                    instrument_id,
                    _text(event.quantity),
                    _text(event.price_original),
                    _text(event.gross_original),
                    _text(event.fee_original) or "0",
                    _text(event.tax_original) or "0",
                    _text(event.net_original),
                    event.currency,
                    _text(event.fx_rate_to_chf),
                    event.internal_transfer_group,
                    event.quality_status,
                    json.dumps(event.reason_codes),
                    now,
                ),
            )
            for kind, amount in (
                ("gross", event.gross_original),
                ("fee", event.fee_original),
                ("tax", event.tax_original),
                ("net", event.net_original),
            ):
                if amount is not None and event.currency:
                    conn.execute(
                        "INSERT INTO postfinance_event_components(component_id,event_id,component_type,amount_original,currency,provenance_document_hash,created_at) VALUES(?,?,?,?,?,?,?)",
                        (
                            _id("pfcomponent"),
                            event_id,
                            kind,
                            _text(amount),
                            event.currency,
                            event.document_hash,
                            now,
                        ),
                    )
            _canonical_transaction(conn, event_id, event, account_id, instrument_id, now)
        lots: dict[str, list[dict[str, Any]]] = defaultdict(list)
        for event in sorted(
            bundle.events,
            key=lambda item: (
                item.occurred_on,
                0 if item.event_type == "buy" else 1,
                item.event_fingerprint,
            ),
        ):
            instrument_id = event_instruments[event.event_fingerprint]
            if (
                event.event_type == "buy"
                and instrument_id
                and event.quantity
                and event.price_original
                and event.currency
            ):
                lots[instrument_id].append({"event": event, "remaining": event.quantity})
            elif event.event_type == "sell" and instrument_id and event.quantity:
                to_consume = event.quantity
                for lot in lots[instrument_id]:
                    if to_consume <= 0:
                        break
                    consumed = min(lot["remaining"], to_consume)
                    lot["remaining"] -= consumed
                    to_consume -= consumed
        for instrument_id, instrument_lots in lots.items():
            for lot in instrument_lots:
                event = lot["event"]
                conn.execute(
                    """INSERT INTO postfinance_cost_basis_lots(lot_id,event_id,instrument_id,acquired_on,quantity_acquired,quantity_remaining,unit_cost_original,fees_original,currency,method,provenance_document_hash,created_at) VALUES(?,?,?,?,?,?,?,?,?,'documented_trade_fifo',?,?)""",
                    (
                        _id("pflot"),
                        f"pfevent_{event.event_fingerprint[:24]}",
                        instrument_id,
                        event.occurred_on,
                        _text(event.quantity),
                        _text(lot["remaining"]),
                        _text(event.price_original),
                        _text(event.fee_original) or "0",
                        event.currency,
                        event.document_hash,
                        now,
                    ),
                )
        for position in snapshot.positions:
            instrument_id = position_map[position.row_reference]
            matched_lots = lots.get(instrument_id, [])
            quantity = sum((lot["remaining"] for lot in matched_lots), Decimal("0"))
            complete = (
                bool(matched_lots)
                and quantity == position.quantity
                and len({lot["event"].currency for lot in matched_lots}) == 1
            )
            calculated = (
                sum(
                    (
                        lot["remaining"] * (lot["event"].price_original or 0)
                        + (lot["event"].fee_original + lot["event"].tax_original)
                        * (lot["remaining"] / lot["event"].quantity)
                        for lot in matched_lots
                        if lot["event"].quantity
                    ),
                    Decimal("0"),
                )
                if complete
                else None
            )
            status = "complete" if complete else "partial" if matched_lots else "unavailable"
            conn.execute(
                """INSERT INTO postfinance_snapshot_positions(snapshot_position_id,snapshot_id,source_row_reference,instrument_id,asset_class,quantity,provider_average_cost_original,provider_cost_total_original,price_original,price_currency,value_chf,weight_pct,computed_cost_basis_original,computed_cost_basis_status,provenance_json,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
                (
                    _id("pfposition"),
                    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),


sed -n '780,860p' src/jarvis_finance/services/postfinance_service.py && sed -n '1,130p' src/jarvis_finance/storage/postfinance_schema.py


Output:
Chunk ID: 162b26
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 2587
Output:
                    _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,
                    now,
                    accounts["etrading_depot"],
                ),
            )
        for cash in snapshot.cash:
            conn.execute(
                "INSERT INTO postfinance_snapshot_cash(snapshot_cash_id,snapshot_id,currency,amount_original,fx_rate_to_chf,value_chf,created_at) VALUES(?,?,?,?,?,?,?)",
                (
                    _id("pfcash"),
                    snapshot_id,
                    cash.currency,
                    _text(cash.amount_original),
                    _text(cash.fx_rate_to_chf),
                    _money(cash.source_value_chf),
                    now,
                ),
            )
            conn.execute(
                "INSERT INTO cash_balances(cash_balance_id,account_id,balance_date,currency,amount_original,fx_rate_to_chf,amount_chf,source_type,quality_status,notes,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?)",
                (
                    _id("cash"),
                    accounts["etrading_cash"],
                    snapshot.valuation_at[:10],
                    cash.currency,
                    _text(cash.amount_original),
                    _text(cash.fx_rate_to_chf),
                    _money(cash.source_value_chf),
                    "postfinance_official_import",
                    "verified",
                    "Official PostFinance account overview",
                    now,
                ),
            )
        conn.execute(
            "INSERT INTO account_value_snapshots(snapshot_id,account_id,valuation_date,total_value_chf,currency,source_type,quality_status,notes,created_at,valuation_at,source_reference,is_active) VALUES(?,?,?,?,'CHF','postfinance_official_import','ok','Official PostFinance account overview',?,?,?,1)",
            (
                _id("acctval"),
                accounts["etrading_depot"],
                snapshot.valuation_at[:10],
                _money(snapshot.total_value_chf),
                now,
                snapshot.valuation_at,
                snapshot_id,
            ),
        )
        conn.commit()
    except IntegrityError as exc:
        conn.rollback()
        duplicate = conn.execute(
            "SELECT batch_id,audit_id FROM postfinance_import_batches WHERE bundle_sha256=?",
            (bundle.bundle_hash,),
        ).fetchone()
        if duplicate:
            return {
                "status": "confirmed",
                "batch_id": duplicate["batch_id"],
                "audit_id": duplicate["audit_id"],
                "idempotent": True,
            }
        for ref, created in archive_refs:
            if created:
                _remove_archive(ref)
        raise ValueError("PostFinance import conflicted with immutable data") from exc
    except Exception:
        conn.rollback()
        for ref, created in archive_refs:
            if created:
                _remove_archive(ref)
        raise
    return {
from __future__ import annotations

from sqlite3 import Connection

def create_postfinance_ledger_import_v1(conn: Connection) -> None:
    """Create immutable PostFinance source, ledger, snapshot and cost-basis tables."""
    position_columns = {row[1] for row in conn.execute("PRAGMA table_info(positions_snapshot)")}
    if "source_type" not in position_columns:
        conn.execute("ALTER TABLE positions_snapshot ADD COLUMN source_type TEXT")
    if "source_reference" not in position_columns:
        conn.execute("ALTER TABLE positions_snapshot ADD COLUMN source_reference TEXT")
    conn.executescript(
        """
        CREATE TABLE IF NOT EXISTS postfinance_account_roles (
            role TEXT PRIMARY KEY CHECK(role IN ('efinance','etrading_depot','etrading_cash')),
            account_id TEXT NOT NULL REFERENCES accounts(account_id),
            source_reference_hash TEXT,
            created_at TEXT NOT NULL,
            UNIQUE(account_id, role)
        );
        CREATE TABLE IF NOT EXISTS postfinance_import_batches (
            batch_id TEXT PRIMARY KEY,
            bundle_sha256 TEXT NOT NULL UNIQUE CHECK(length(bundle_sha256)=64),
            zip_sha256 TEXT NOT NULL CHECK(length(zip_sha256)=64),
            overview_sha256 TEXT NOT NULL CHECK(length(overview_sha256)=64),
            parser_id TEXT NOT NULL,
            parser_version TEXT NOT NULL,
            db_revision TEXT NOT NULL,
            archive_reference TEXT NOT NULL,
            snapshot_at TEXT NOT NULL,
            status TEXT NOT NULL CHECK(status='confirmed'),
            audit_id TEXT NOT NULL,
            confirmed_at TEXT NOT NULL,
            confirmed_by TEXT NOT NULL DEFAULT 'user'
        );
        CREATE TABLE IF NOT EXISTS postfinance_documents (
            document_hash TEXT PRIMARY KEY CHECK(length(document_hash)=64),
            filename_hash TEXT NOT NULL CHECK(length(filename_hash)=64),
            semantic_identity TEXT NOT NULL UNIQUE CHECK(length(semantic_identity)=64),
            semantic_reference_hash TEXT NOT NULL CHECK(length(semantic_reference_hash)=64),
            document_type TEXT NOT NULL,
            document_date TEXT,
            account_reference_hash TEXT,
            account_role TEXT NOT NULL CHECK(account_role IN ('efinance','etrading_depot','etrading_cash')),
            page_count INTEGER NOT NULL CHECK(page_count > 0),
            archive_reference TEXT NOT NULL,
            parser_version TEXT NOT NULL,
            created_at TEXT NOT NULL
        );
        CREATE TABLE IF NOT EXISTS postfinance_batch_documents (
            batch_id TEXT NOT NULL REFERENCES postfinance_import_batches(batch_id),
            document_hash TEXT NOT NULL REFERENCES postfinance_documents(document_hash),
            PRIMARY KEY(batch_id, document_hash)
        );
        CREATE TABLE IF NOT EXISTS postfinance_snapshots (
            snapshot_id TEXT PRIMARY KEY,
            batch_id TEXT NOT NULL UNIQUE REFERENCES postfinance_import_batches(batch_id),
            account_id TEXT NOT NULL REFERENCES accounts(account_id),
            snapshot_at TEXT NOT NULL,
            total_chf TEXT NOT NULL,
            securities_chf TEXT NOT NULL,
            cash_chf TEXT NOT NULL,
            stocks_chf TEXT NOT NULL,
            etfs_chf TEXT NOT NULL,
            component_total_chf TEXT NOT NULL,
            difference_chf TEXT NOT NULL,
            tolerance_chf TEXT NOT NULL,
            position_count INTEGER NOT NULL,
            cash_count INTEGER NOT NULL,
            open_orders INTEGER NOT NULL,
            reconciliation_status TEXT NOT NULL CHECK(reconciliation_status IN ('matched','within_tolerance')),
            created_at TEXT NOT NULL
        );
        CREATE TABLE IF NOT EXISTS postfinance_snapshot_positions (
            snapshot_position_id TEXT PRIMARY KEY,
            snapshot_id TEXT NOT NULL REFERENCES postfinance_snapshots(snapshot_id),
            source_row_reference TEXT NOT NULL,
            instrument_id TEXT NOT NULL REFERENCES instruments(instrument_id),
            asset_class TEXT NOT NULL CHECK(asset_class IN ('stock','etf')),
            quantity TEXT NOT NULL,
            provider_average_cost_original TEXT,
            provider_cost_total_original TEXT,
            price_original TEXT NOT NULL,
            price_currency TEXT NOT NULL,
            value_chf TEXT NOT NULL,
            weight_pct TEXT NOT NULL,
            computed_cost_basis_original TEXT,
            computed_cost_basis_status TEXT NOT NULL CHECK(computed_cost_basis_status IN ('complete','partial','unavailable')),
            provenance_json TEXT NOT NULL CHECK(json_valid(provenance_json)),
            created_at TEXT NOT NULL,
            UNIQUE(snapshot_id, source_row_reference),
            UNIQUE(snapshot_id, instrument_id)
        );
        CREATE TABLE IF NOT EXISTS postfinance_snapshot_cash (
            snapshot_cash_id TEXT PRIMARY KEY,
            snapshot_id TEXT NOT NULL REFERENCES postfinance_snapshots(snapshot_id),
            currency TEXT NOT NULL,
            amount_original TEXT NOT NULL,
            fx_rate_to_chf TEXT NOT NULL,
            value_chf TEXT NOT NULL,
            created_at TEXT NOT NULL,
            UNIQUE(snapshot_id, currency)
        );
        CREATE TABLE IF NOT EXISTS postfinance_ledger_events (
            event_id TEXT PRIMARY KEY,
            event_fingerprint TEXT NOT NULL UNIQUE CHECK(length(event_fingerprint)=64),
            document_hash TEXT NOT NULL REFERENCES postfinance_documents(document_hash),
            semantic_reference_hash TEXT NOT NULL CHECK(length(semantic_reference_hash)=64),
            event_type TEXT NOT NULL CHECK(event_type IN ('buy','sell','dividend','interest','fee','internal_transfer','fx','split','corporate_action')),
            account_role TEXT NOT NULL CHECK(account_role IN ('efinance','etrading_depot','etrading_cash')),
            account_id TEXT NOT NULL REFERENCES accounts(account_id),
            occurred_on TEXT NOT NULL,
            settlement_on TEXT,
            direction TEXT NOT NULL CHECK(direction IN ('in','out','neutral')),
            instrument_id TEXT REFERENCES instruments(instrument_id),
            quantity TEXT,
            price_original TEXT,
            gross_original TEXT,
            fee_original TEXT NOT NULL DEFAULT '0',
            tax_original TEXT NOT NULL DEFAULT '0',
            net_original TEXT,
            currency TEXT,
            fx_rate_to_chf TEXT,
            internal_transfer_group TEXT,
            quality_status TEXT NOT NULL CHECK(quality_status IN ('complete','partial')),
            reason_codes_json TEXT NOT NULL CHECK(json_valid(reason_codes_json)),
            created_at TEXT NOT NULL
        );
        CREATE INDEX IF NOT EXISTS idx_postfinance_events_time ON postfinance_ledger_events(occurred_on, event_type);


grep -R "get_cash_overview\|cash_total_chf" -n src/jarvis_finance/dashboard src/jarvis_finance/services | head -60 && sed -n '1,80p' src/jarvis_finance/services/cash_service.py && grep -n "get_cash_summary" -A50 src/jarvis_finance/services/cash_service.py


Output:
Chunk ID: 1cf2d7
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 2665
Output:
grep: src/jarvis_finance/dashboard/__pycache__/data.cpython-312.pyc: binary file matches
grep: src/jarvis_finance/dashboard/__pycache__/data.cpython-311.pyc: binary file matches
grep: src/jarvis_finance/dashboard/views/__pycache__/page_00_command_center.cpython-311.pyc: binary file matches
grep: src/jarvis_finance/dashboard/views/__pycache__/page_03_equities_etfs.cpython-311.pyc: binary file matches
grep: src/jarvis_finance/services/__pycache__/postfinance_service.cpython-311.pyc: binary file matches
grep: src/jarvis_finance/services/__pycache__/truewealth_service.cpython-311.pyc: binary file matches
grep: src/jarvis_finance/services/__pycache__/postfinance_service.cpython-312.pyc: binary file matches
src/jarvis_finance/dashboard/views/page_03_equities_etfs.py:32:    for row in data.get_cash_overview(conn):
src/jarvis_finance/dashboard/views/page_00_command_center.py:63:        ("Cash CHF", data.chf_text(summary["cash_total_chf"]), None),
src/jarvis_finance/dashboard/data.py:539:def get_cash_overview(conn: Connection) -> list[dict[str, str]]:
src/jarvis_finance/dashboard/data.py:686:        {"label": "Cash", "value_chf": summary["cash_total_chf"]},
src/jarvis_finance/dashboard/data.py:692:    for row in get_cash_overview(conn):
src/jarvis_finance/dashboard/data.py:1154:    cash_rows = get_cash_overview(conn)
src/jarvis_finance/dashboard/data.py:1176:        "cash_total_chf": dec_text(cash_total, "0.01"),
src/jarvis_finance/services/postfinance_service.py:269:    component_total = snapshot.securities_total_chf + snapshot.cash_total_chf
src/jarvis_finance/services/postfinance_service.py:295:        "cash_chf": _money(snapshot.cash_total_chf),
src/jarvis_finance/services/postfinance_service.py:599:        component_total = snapshot.securities_total_chf + snapshot.cash_total_chf
src/jarvis_finance/services/postfinance_service.py:610:                _money(snapshot.cash_total_chf),
src/jarvis_finance/services/truewealth_service.py:131:        "cash_total_chf": _decimal_text(statement.cash_total_chf),
src/jarvis_finance/services/truewealth_service.py:334:                 cash_total_chf,components_total_chf,reconciliation_difference_chf,reconciliation_tolerance_chf,
src/jarvis_finance/services/truewealth_service.py:345:                _decimal_text(statement.cash_total_chf),
src/jarvis_finance/services/truewealth_service.py:613:                  s.cash_total_chf,s.components_total_chf,s.reconciliation_difference_chf,s.reconciliation_status,
from __future__ import annotations

import json
from datetime import UTC, datetime
from decimal import Decimal, InvalidOperation
from sqlite3 import Connection
from uuid import uuid4

from fastapi import HTTPException

from jarvis_finance.api.schemas.overview import CashSummary
from jarvis_finance.api.schemas.positions import (
    ActionItem,
    CashPosition,
    CashPositionDetail,
    CashSnapshot,
    CashSnapshotConfirmRequest,
    CashSnapshotPreviewRequest,
    ConfirmResponse,
    PreviewResponse,
)
from jarvis_finance.ledger.cash import calculate_cash_balances
from jarvis_finance.services.api_helpers import decimal_text
from jarvis_finance.services.portfolio_aggregation import latest_official_postfinance_cash

CANONICAL_CASH_ACCOUNTS = [
    {"platform": "Raiffeisen", "account_name": "Raiffeisen", "balance_mode": "csv_calculated"},
    {"platform": "PostFinance Cash", "account_name": "PostFinance Cash-Konto", "balance_mode": "manual"},
    {"platform": "AKB", "account_name": "AKB Zinsbereitstellungskonto", "balance_mode": "manual"},
    {"platform": "AKB", "account_name": "AKB gemeinsames Konto", "balance_mode": "manual"},
    {"platform": "AKB", "account_name": "AKB Aargauer-Sparkonto", "balance_mode": "manual"},
    {"platform": "AKB", "account_name": "AKB Haushaltskonto", "balance_mode": "csv_calculated"},
]

def _now() -> str:
    return datetime.now(UTC).replace(microsecond=0).isoformat().replace("+00:00", "Z")

def _uuid(prefix: str) -> str:
    return f"{prefix}_{uuid4().hex[:16]}"

def _decimal(value: str, field: str) -> Decimal:
    try:
        parsed = Decimal(str(value))
    except (InvalidOperation, ValueError) as exc:
        raise HTTPException(status_code=422, detail=f"Invalid decimal for {field}") from exc
    if parsed < 0:
        raise HTTPException(status_code=422, detail=f"{field} must be zero or positive")
    return parsed

def _audit(conn: Connection, *, action: str, entity_type: str, entity_id: str, payload: dict, note: str, created_by: str = "user") -> str:
    audit_id = _uuid("audit")
    ts = _now()
    conn.execute(
        """
        INSERT INTO audit_log(audit_id, timestamp, source, action, entity_type, entity_id, old_values_json, new_values_json, user_text_note, confirmed, confirmation_timestamp, created_by, quality_status, created_at)
        VALUES (?, ?, 'vue_dashboard', ?, ?, ?, NULL, ?, ?, 1, ?, ?, 'ok', ?)
        """,
        (audit_id, ts, action, entity_type, entity_id, json.dumps(payload, sort_keys=True), note, ts, created_by, ts),
    )
    return audit_id

def _ensure_platform(conn: Connection, name: str) -> str:
    row = conn.execute("SELECT platform_id FROM platforms WHERE lower(name)=lower(?)", (name,)).fetchone()
    if row:
        return row["platform_id"]
    platform_id = _uuid("plat")
    conn.execute(
        "INSERT INTO platforms(platform_id, name, platform_type, default_currency, created_at) VALUES (?, ?, 'bank', 'CHF', ?)",
        (platform_id, name, _now()),
    )
    return platform_id

def ensure_canonical_cash_accounts(conn: Connection, *, created_by: str = "user") -> dict:
    created: list[str] = []
251:def get_cash_summary(conn: Connection) -> CashSummary:
252-    # Read paths must never seed or refresh accounts; setup is an explicit Confirm action.
253-    positions = list_cash_positions(conn)
254-    total = sum(Decimal(p.used_value_chf) for p in positions)
255-    last_dates = [p.last_manual_reconciliation for p in positions if p.last_manual_reconciliation]
256-    return CashSummary(
257-        base_currency="CHF",
258-        cash_chf=decimal_text(total, 2),
259-        manual_accounts_count=sum(1 for p in positions if p.balance_mode == "manual"),
260-        csv_calculated_accounts_count=sum(1 for p in positions if p.balance_mode == "csv_calculated"),
261-        open_reconciliation_count=sum(1 for p in positions if p.status == "Abgleich offen"),
262-        last_reconciliation_at=max(last_dates) if last_dates else None,
263-        positions=positions,
264-    )
265-
266-
267-def preview_cash_snapshot(conn: Connection, request: CashSnapshotPreviewRequest) -> PreviewResponse:
268-    row = conn.execute("SELECT account_id, account_type FROM accounts WHERE account_id=?", (request.account_id,)).fetchone()
269-    if not row or row["account_type"] != "cash":
270-        raise HTTPException(status_code=404, detail="Cash account not found")
271-    if request.snapshot_type not in {"manual_balance", "csv_anchor_balance", "calculated_balance", "reconciliation"}:
272-        raise HTTPException(status_code=422, detail="Invalid snapshot_type")
273-    amount = _decimal(request.amount_chf, "amount_chf")
274-    return PreviewResponse(preview_id=_uuid("preview"), asset_class="cash", summary=f"{request.snapshot_type} CHF per {request.balance_date}", amount_chf=decimal_text(amount, 2), fx_status="not_needed", warnings=[])
275-
276-
277-def confirm_cash_snapshot(conn: Connection, request: CashSnapshotConfirmRequest) -> ConfirmResponse:
278-    if not request.confirm:
279-        raise HTTPException(status_code=400, detail="Explicit confirm required")
280-    preview_cash_snapshot(conn, request)
281-    amount = _decimal(request.amount_chf, "amount_chf")
282-    snapshot_id = _uuid("cashsnap")
283-    now = _now()
284-    source = "manual" if request.snapshot_type in {"manual_balance", "reconciliation"} else "csv_import" if request.snapshot_type == "csv_anchor_balance" else "system_calculated"
285-    audit_id = _audit(conn, action=f"cash_{request.snapshot_type}_confirm", entity_type="cash_account", entity_id=request.account_id, payload=request.model_dump(), note=request.note)
286-    conn.execute(
287-        """
288-        INSERT INTO cash_account_snapshots(snapshot_id, account_id, snapshot_type, balance_date, amount_original, currency, amount_chf, source, note, created_at, created_by, audit_id)
289-        VALUES (?, ?, ?, ?, ?, 'CHF', ?, ?, ?, ?, 'user', ?)
290-        """,
291-        (snapshot_id, request.account_id, request.snapshot_type, request.balance_date, decimal_text(amount, 2), decimal_text(amount, 2), source, request.note, now, audit_id),
292-    )
293-    conn.commit()
294-    return ConfirmResponse(status="confirmed", entity_id=snapshot_id, audit_id=audit_id, message="Kontostand gespeichert · Audit geschrieben")
295-
296-
297-def get_cash_position_detail(conn: Connection, position_id: str) -> CashPositionDetail:
298-    account_id, _currency = position_id.split(":", 1)
299:    position = next((p for p in get_cash_summary(conn).positions if p.account_id == account_id), None)
300-    if not position:
301-        raise HTTPException(status_code=404, detail="Cash position not found")
302-    rows = conn.execute(
303-        """
304-        SELECT snapshot_id, snapshot_type, balance_date, amount_chf, source, COALESCE(note,'') AS note, audit_id
305-        FROM cash_account_snapshots
306-        WHERE account_id=?
307-        ORDER BY balance_date DESC, created_at DESC
308-        LIMIT 30
309-        """,
310-        (account_id,),
311-    ).fetchall()
312-    history: list[CashSnapshot] = []
313-    previous: Decimal | None = None
314-    for row in reversed(rows):
315-        current = Decimal(str(row["amount_chf"]))
316-        diff = None if previous is None else decimal_text(current - previous, 2)
317-        previous = current
318-        history.append(CashSnapshot(snapshot_id=row["snapshot_id"], snapshot_type=row["snapshot_type"], balance_date=row["balance_date"], amount_chf=decimal_text(current, 2), source=row["source"], note=row["note"], difference_to_previous_chf=diff, audit_hint="Audit vorhanden" if row["audit_id"] else None))
319-    history = list(reversed(history))
320-    return CashPositionDetail(**position.model_dump(), last_change=position.last_manual_reconciliation, history=history, available_actions=[ActionItem(label="Einzahlung", enabled=True), ActionItem(label="Auszahlung", enabled=True), ActionItem(label="Kontostand aktualisieren", enabled=True), ActionItem(label="CSV-Anfangsstand setzen", enabled=True), ActionItem(label="Abgleich bestätigen", enabled=True), ActionItem(label="Verlauf anzeigen", enabled=True)])


nl -ba src/jarvis_finance/services/cash_service.py | sed -n '184,225p'; nl -ba src/jarvis_finance/services/portfolio_data.py | sed -n '1590,1610p'; nl -ba src/jarvis_finance/services/portfolio_aggregation.py | sed -n '88,110p'


Output:
Chunk ID: e4dd1a
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1326
Output:
   184	        """
   185	    ).fetchall()
   186	    positions: list[CashPosition] = []
   187	    official_components = latest_official_postfinance_cash(conn)
   188	    official_by_account: dict[str, list] = {}
   189	    for component in official_components:
   190	        official_by_account.setdefault(component.account_id, []).append(component)
   191	    for row in rows:
   192	        account_id = row["account_id"]
   193	        if account_id in official_by_account:
   194	            for component in official_by_account[account_id]:
   195	                positions.append(
   196	                    CashPosition(
   197	                        id=f"{account_id}:{component.currency}",
   198	                        account_id=account_id,
   199	                        platform=row["platform"],
   200	                        account_label=row["account_name"],
   201	                        account_type=row["account_type"],
   202	                        currency=component.currency,
   203	                        balance_mode="official_components",
   204	                        amount=decimal_text(component.amount_original, 2),
   205	                        amount_chf=decimal_text(component.amount_chf, 2),
   206	                        calculated_balance_chf=decimal_text(component.amount_chf, 2),
   207	                        manual_balance_chf=None,
   208	                        csv_anchor_balance_chf=None,
   209	                        used_value_chf=decimal_text(component.amount_chf, 2),
   210	                        difference_chf="0.00",
   211	                        last_manual_reconciliation=component.snapshot_date,
   212	                        last_imported_booking=_last_imported_booking(conn, account_id),
   213	                        status="Offiziell abgeglichen",
   214	                    )
   215	                )
   216	            continue
   217	        manual = _latest_snapshot(conn, account_id, "manual_balance")
   218	        anchor = _latest_snapshot(conn, account_id, "csv_anchor_balance")
   219	        calculated = _calculated_balance(conn, account_id)
   220	        manual_amount = Decimal(str(manual["amount_chf"])) if manual else None
   221	        mode = row["balance_mode"] or "manual"
   222	        used = manual_amount if mode == "manual" and manual_amount is not None else calculated
   223	        difference = (manual_amount - calculated) if manual_amount is not None else Decimal("0")
   224	        status = "Aktuell"
   225	        if manual_amount is not None and difference != 0:
  1590	        differences.append({
  1591	            "difference_ref": _safe_ref("difference", f"{account}:{instrument}:{as_of}"),
  1592	            "account_ref": _safe_ref("account", account), "account_label": _safe_label(account_map[account]["account_name"]),
  1593	            "instrument_ref": _safe_ref("instrument", instrument), "instrument_label": str(instrument_row["name"]) if instrument_row else "Instrument (unbekannt)",
  1594	            "quantity": {"ledger": _decimal_text(ledger_qty), "reported": _decimal_text(source_qty), "difference": _decimal_text(quantity_diff), "status": quantity_status},
  1595	            "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,
  1596	                          "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},
  1597	            "reason_codes": sorted(set(reasons)),
  1598	        })
  1599	    pf_roles = postfinance_account_roles(conn)
  1600	    official_pf_cash = latest_official_postfinance_cash(conn)
  1601	    official_pf_cash_total = sum(
  1602	        (component.amount_chf for component in official_pf_cash), Decimal("0")
  1603	    )
  1604	    official_pf_cash_date = max(
  1605	        (component.snapshot_date for component in official_pf_cash), default=None
  1606	    )
  1607	    account_totals: list[dict[str, Any]] = []
  1608	    account_covered = 0
  1609	    for account, meta in account_map.items():
  1610	        cash = conn.execute(
    88	    if sum(allocated, Decimal("0")) != _money(target):
    89	        raise ValueError("Official portfolio component allocation does not reconcile")
    90	    return allocated
    91
    92
    93	def latest_official_postfinance_cash(conn: Connection) -> list[OfficialCashComponent]:
    94	    roles = postfinance_account_roles(conn)
    95	    account_id = roles.get("etrading_cash")
    96	    if not account_id:
    97	        return []
    98	    snapshot = conn.execute(
    99	        """SELECT snapshot_id,snapshot_at,cash_chf
   100	           FROM postfinance_snapshots
   101	           ORDER BY snapshot_at DESC,created_at DESC LIMIT 1"""
   102	    ).fetchone()
   103	    if not snapshot:
   104	        return []
   105	    rows = conn.execute(
   106	        """SELECT currency,amount_original,fx_rate_to_chf
   107	           FROM postfinance_snapshot_cash WHERE snapshot_id=? ORDER BY currency""",
   108	        (snapshot["snapshot_id"],),
   109	    ).fetchall()
   110	    raw_values = [


nl -ba src/jarvis_finance/services/portfolio_analytics.py | sed -n '228,245p'; nl -ba src/jarvis_finance/services/portfolio_aggregation.py | sed -n '134,145p'


Output:
Chunk ID: 214b34
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 368
Output:
   228	        item = grouped.setdefault(key, dict(row) | {"quantity_total": ZERO})
   229	        quantity = _decimal(row["quantity"])
   230	        item["quantity_total"] += -abs(quantity) if str(row["transaction_type"]).lower() in SELL_TYPES else quantity
   231	    official = {
   232	        key: override
   233	        for key, override in latest_official_postfinance_positions(conn).items()
   234	        if not as_of or override.snapshot_date <= as_of
   235	    }
   236	    trading_cash_id = postfinance_account_roles(conn).get("etrading_cash")
   237	    official_instruments = {key[1] for key in official}
   238	    for key in list(grouped):
   239	        if key[0] == trading_cash_id and key[1] in official_instruments:
   240	            grouped.pop(key)
   241	    for key, override in official.items():
   242	        if as_of and override.snapshot_date > as_of:
   243	            continue
   244	        item = grouped.get(key)
   245	        if item is None:
   134	    roles = postfinance_account_roles(conn)
   135	    account_id = roles.get("etrading_depot")
   136	    if not account_id:
   137	        return {}
   138	    snapshot = conn.execute(
   139	        """SELECT snapshot_id,snapshot_at,stocks_chf,etfs_chf
   140	           FROM postfinance_snapshots
   141	           ORDER BY snapshot_at DESC,created_at DESC LIMIT 1"""
   142	    ).fetchone()
   143	    if not snapshot:
   144	        return {}
   145	    rows = conn.execute(


sed -n '1120,1190p' src/jarvis_finance/dashboard/data.py; sed -n '660,700p' src/jarvis_finance/dashboard/data.py


Output:
Chunk ID: 7e097d
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1585
Output:

def get_top_user_hints(conn: Connection, limit: int = 3) -> list[str]:
    hints: list[str] = []
    summary = get_command_center_summary(conn)
    if equity_etf_user_label(summary) == "noch nicht bewertet":
        hints.append("Aktien/ETF: Bewertung unvollständig")
    crypto = get_crypto_summary_cards(conn)
    if d(crypto.get("coins_without_price")) > 0:
        hints.append("Crypto-Preise fehlen für einzelne Coins")
    elif d(crypto.get("stale_price_count")) > 0:
        hints.append("Crypto-Preise teilweise veraltet")
    else:
        hints.append("Crypto-Preise aktuell")
    for alert in get_alert_cards(conn, limit=limit):
        text = alert["title"]
        if not any(marker in text for marker in ("asset_id", "wallet_id", "instrument-", "rule_id", "entity_id")):
            hints.append(text)
        if len(hints) >= limit:
            break
    return hints[:limit]

def compact_chart_rows(rows: list[dict[str, str]], *, limit: int = 10) -> list[dict[str, str]]:
    ordered = sorted(rows, key=lambda row: d(row.get("value_chf")), reverse=True)
    if len(ordered) <= limit:
        return ordered
    head = ordered[:limit]
    other = sum((d(row.get("value_chf")) for row in ordered[limit:]), Decimal("0"))
    if other > 0:
        head.append({"label": "Andere", "value_chf": dec_text(other, "0.01")})
    return head

def get_command_center_summary(conn: Connection) -> dict[str, str]:
    cash_rows = get_cash_overview(conn)
    crypto_summary = get_crypto_summary_cards(conn)
    positions = [r for r in get_positions(conn) if r.get("asset_class", "").lower() in {"stock", "equity", "etf"} and r.get("instrument_status", "").lower() != "inactive"]
    cash_total = sum(d(r["amount_chf"]) for r in cash_rows if r["amount_chf"])
    crypto_total = d(crypto_summary["total_value_chf"])
    stock_rows = [r for r in positions if r.get("asset_class", "").lower() in {"stock", "equity"}]
    etf_rows = [r for r in positions if r.get("asset_class", "").lower() == "etf"]
    stock_total = sum(d(r["market_value_chf"]) for r in stock_rows if r.get("market_value_chf"))
    etf_total = sum(d(r["market_value_chf"]) for r in etf_rows if r.get("market_value_chf"))
    equity_total = stock_total + etf_total
    missing_price_count = sum(1 for r in positions if not r.get("market_price_original"))
    missing_fx_count = sum(1 for r in positions if r.get("fx_status") == "missing_fx")
    incomplete_cost_count = sum(1 for r in positions if "cost_basis_uncertain" in (r.get("quality_warnings") or "") or not r.get("cost_basis_chf"))
    unvalued_count = missing_price_count + missing_fx_count
    latest_equity_price = max((r.get("price_date") or "" for r in positions), default="")
    alert_counts = conn.execute("SELECT priority, COUNT(*) AS c FROM alerts WHERE status='active' GROUP BY priority").fetchall()
    critical = sum(int(r["c"] or 0) for r in alert_counts if r["priority"] == "kritisch")
    quality = get_data_quality(conn)
    dq = "kritisch" if any(i["severity"] == "kritisch" and i["count"] != "0" for i in quality) else ("warnung" if any(i["severity"] == "warnung" and i["count"] != "0" for i in quality) else "ok")
    total = cash_total + crypto_total + equity_total
    return {
        "base_currency": "CHF",
        "cash_total_chf": dec_text(cash_total, "0.01"),
        "crypto_total_chf": dec_text(crypto_total, "0.01"),
        "stock_total_chf": dec_text(stock_total, "0.01"),
        "etf_total_chf": dec_text(etf_total, "0.01"),
        "equity_etf_total_chf": dec_text(equity_total, "0.01"),
        "equity_position_count": str(len(positions)),
        "unvalued_position_count": str(unvalued_count),
        "missing_price_count": str(missing_price_count),
        "missing_fx_count": str(missing_fx_count),
        "incomplete_cost_count": str(incomplete_cost_count),
        "total_portfolio_chf": dec_text(total, "0.01"),
        "total_value_chf": dec_text(total, "0.01"),
        "critical_alert_count": str(critical),
        "active_alerts": ", ".join(f"{r['priority']}: {r['c']}" for r in alert_counts) or "0",
        "data_quality_status": dq,
    rows: list[dict[str, str]] = []
    for row in get_positions(conn):
        if row.get("asset_class", "").lower() not in {"stock", "equity", "etf"}:
            continue
        rows.append({
            "name": row.get("name", ""),
            "ticker": row.get("ticker", ""),
            "isin": row.get("isin", ""),
            "depot": row.get("account_name", ""),
            "assettyp": "ETF" if row.get("asset_class", "").lower() == "etf" else "Aktie",
            "menge": row.get("quantity", ""),
            "kurs": row.get("market_price_original") or "Preis noch nicht verfügbar",
            "kurswaehrung": row.get("price_currency", ""),
            "marktwert_chf": chf_text(row.get("market_value_chf")) if row.get("market_value_chf") else "nicht bewertet",
            "einstand_chf": chf_text(row.get("cost_basis_chf")) if row.get("cost_basis_chf") else "unvollständig",
            "p_l_chf": chf_text(row.get("total_return_chf")) if row.get("total_return_chf") else "nicht berechenbar",
            "status": translate_status(row.get("price_status") or row.get("data_quality_status")),
        })
    return rows

def get_portfolio_asset_class_chart(conn: Connection) -> list[dict[str, str]]:
    summary = get_command_center_summary(conn)
    return [
        {"label": "Crypto", "value_chf": summary["crypto_total_chf"]},
        {"label": "Aktien/ETF", "value_chf": summary["equity_etf_total_chf"]},
        {"label": "Cash", "value_chf": summary["cash_total_chf"]},
    ]

def get_platform_value_chart(conn: Connection) -> list[dict[str, str]]:
    totals: dict[str, Decimal] = {}
    for row in get_cash_overview(conn):
        totals[row["platform"]] = totals.get(row["platform"], Decimal("0")) + d(row.get("amount_chf"))
    for row in get_positions(conn):
        totals[row["platform"]] = totals.get(row["platform"], Decimal("0")) + d(row.get("market_value_chf"))
    crypto_total = d(get_crypto_summary_cards(conn)["total_value_chf"])
    if crypto_total:
        totals["Crypto Wallets"] = totals.get("Crypto Wallets", Decimal("0")) + crypto_total
    return [{"label": k, "value_chf": dec_text(v, "0.01")} for k, v in sorted(totals.items(), key=lambda item: item[1], reverse=True)]


sed -n '700,760p' src/jarvis_finance/services/portfolio_analytics.py; sed -n '1888,1906p' src/jarvis_finance/storage/migrations.py