Output:
Chunk ID: e93515
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 2683
Output:
"""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
    quantity: Decimal | None = None

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,
    *,
    as_of: str | None = None,
    data_cutoff: str | None = None,
) -> list[OfficialCashComponent]:
    roles = postfinance_account_roles(conn)
    account_id = roles.get("etrading_cash")
    if not account_id:
        return []
    conditions: list[str] = []
    params: list[str] = []
    if as_of:
        conditions.append("date(snapshot_at)<=date(?)")
        params.append(as_of)
    if data_cutoff:
        conditions.append("datetime(created_at)<=datetime(?)")
        params.append(data_cutoff)
    where = f"WHERE {' AND '.join(conditions)}" if conditions else ""
    snapshot = conn.execute(
        f"""SELECT snapshot_id,snapshot_at,cash_chf
            FROM postfinance_snapshots {where}
            ORDER BY snapshot_at DESC,datetime(created_at) DESC LIMIT 1""",
        params,
    ).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,
    *,
    as_of: str | None = None,
    data_cutoff: str | None = None,
) -> dict[tuple[str, str], OfficialPositionOverride]:
    roles = postfinance_account_roles(conn)
    account_id = roles.get("etrading_depot")
    if not account_id:
        return {}
    conditions: list[str] = []
    params: list[str] = []
    if as_of:
        conditions.append("date(snapshot_at)<=date(?)")
        params.append(as_of)
    if data_cutoff:
        conditions.append("datetime(created_at)<=datetime(?)")
        params.append(data_cutoff)
    where = f"WHERE {' AND '.join(conditions)}" if conditions else ""
    snapshot = conn.execute(
        f"""SELECT snapshot_id,snapshot_at,stocks_chf,etfs_chf
            FROM postfinance_snapshots {where}
            ORDER BY snapshot_at DESC,datetime(created_at) DESC LIMIT 1""",
        params,
    ).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 post_snapshot_postfinance_quantity_deltas(
    conn: Connection,
    positions: dict[tuple[str, str], OfficialPositionOverride],
    *,
    as_of: str | None = None,
) -> dict[str, Decimal]:
    """Replay confirmed buys/sells after the official snapshot baseline."""

    if not positions:
        return {}
    roles = postfinance_account_roles(conn)
    account_ids = [
        account_id
        for role in ("etrading_depot", "etrading_cash")
        if (account_id := roles.get(role))
    ]
    if not account_ids:
        return {}
    snapshot_date = min(item.snapshot_date for item in positions.values())
    effective_as_of = as_of or "9999-12-31"
    placeholders = ",".join("?" for _ in account_ids)
    rows = conn.execute(
        f"""SELECT instrument_id,lower(transaction_type) transaction_type,quantity
            FROM transactions
            WHERE account_id IN ({placeholders})
              AND instrument_id IS NOT NULL
              AND COALESCE(is_confirmed,0)=1
              AND COALESCE(is_voided,0)=0
              AND lower(transaction_type) IN ('buy','sell','partial_sell','full_sell')
              AND trade_date>? AND trade_date<=?""",
        (*account_ids, snapshot_date, effective_as_of),
    ).fetchall()
    deltas: dict[str, Decimal] = {}
    for row in rows:
        instrument_id = str(row["instrument_id"])
        quantity = abs(_decimal(row["quantity"]))
        if str(row["transaction_type"]) in {"sell", "partial_sell", "full_sell"}:
            quantity = -quantity
        deltas[instrument_id] = deltas.get(instrument_id, Decimal("0")) + quantity
    return deltas

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,
            quantity=(
                _decimal(item["quantity"])
                if item.get("quantity") not in {None, ""}
                else None
            ),
        )
    return values


grep -R "postfinance_account_roles" -n . | head -50


Output:
Chunk ID: 97ae6a
Wall time: 0.7955 seconds
Process exited with code 0
Original token count: 846
Output:
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__/cash_service.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__/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__/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
grep: ./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
./src/jarvis_finance/storage/postfinance_schema.py:15:        CREATE TABLE IF NOT EXISTS postfinance_account_roles (
./src/jarvis_finance/dashboard/data.py:16:    postfinance_account_roles,
./src/jarvis_finance/dashboard/data.py:564:    roles = postfinance_account_roles(conn)
./src/jarvis_finance/services/portfolio_aggregation.py:65:def postfinance_account_roles(conn: Connection) -> dict[str, str]:
./src/jarvis_finance/services/portfolio_aggregation.py:71:            "SELECT role,account_id FROM postfinance_account_roles ORDER BY role"
./src/jarvis_finance/services/portfolio_aggregation.py:100:    roles = postfinance_account_roles(conn)
./src/jarvis_finance/services/portfolio_aggregation.py:153:    roles = postfinance_account_roles(conn)
./src/jarvis_finance/services/portfolio_aggregation.py:223:    roles = postfinance_account_roles(conn)
./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:31:    postfinance_account_roles,
./src/jarvis_finance/services/portfolio_analytics.py:238:    trading_cash_id = postfinance_account_roles(conn).get("etrading_cash")
./src/jarvis_finance/services/postfinance_service.py:541:                "INSERT OR IGNORE INTO postfinance_account_roles(role,account_id,source_reference_hash,created_at) VALUES(?,?,NULL,?)",
./src/jarvis_finance/services/postfinance_service.py:545:                "SELECT account_id FROM postfinance_account_roles WHERE role=?", (role,)
./src/jarvis_finance/services/cash_service.py:218:                  SELECT account_id FROM postfinance_account_roles
./tests/test_postfinance_sprint13.py:283:    assert conn.execute("SELECT COUNT(*) FROM postfinance_account_roles").fetchone()[0] == 3
./tests/test_postfinance_sprint13.py:284:    roles = dict(conn.execute("SELECT role,account_id FROM postfinance_account_roles"))


Output:
Chunk ID: e35dbf
Wall time: 2.0825 seconds
Process exited with code 0
Original token count: 25
Output:
........                                                                 [100%]
8 passed in 1.89s


sed -n '1,280p' src/jarvis_finance/services/equity_service.py && sed -n '520,700p' src/jarvis_finance/dashboard/data.py


Output:
Chunk ID: 12f485
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 5349
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
        FROM crypto_transactions ct
        LEFT JOIN crypto_assets ca ON ca.asset_id=ct.asset_id
        LEFT JOIN crypto_wallets fw ON fw.wallet_id=ct.from_wallet_id
        LEFT JOIN crypto_wallets tw ON tw.wallet_id=ct.to_wallet_id
        ORDER BY ct.transaction_datetime DESC, ct.created_at DESC LIMIT ?
        """, (limit,)
    ).fetchall())

def get_platform_overview(conn: Connection) -> list[dict[str, str]]:
    return rowdicts(conn.execute(
        """
        SELECT p.name AS platform, p.platform_type, a.account_name, a.account_type, a.currency,
               a.performance_included, a.is_active
        FROM accounts a JOIN platforms p ON p.platform_id=a.platform_id
        ORDER BY p.name, a.account_name
        """
    ).fetchall())

def get_cash_overview(conn: Connection) -> list[dict[str, str]]:
    # One shared cash read model for the API, Command Center, allocation and reports.
    # The local import avoids coupling the write-oriented cash service into module init.
    from jarvis_finance.services.cash_service import list_cash_positions

    return [
        {
            "account_id": position.account_id,
            "platform": position.platform,
            "account_name": position.account_label,
            "currency": position.currency,
            "amount_original": position.amount,
            "amount_chf": position.used_value_chf,
            "quality_status": "ok" if position.status != "Abgleich offen" else "partial",
        }
        for position in list_cash_positions(conn)
    ]

def get_positions(conn: Connection) -> list[dict[str, str]]:
    result = calculate_positions(conn, persist_alerts=False)
    official = latest_official_postfinance_positions(conn)
    post_snapshot_deltas = post_snapshot_postfinance_quantity_deltas(conn, official)
    audited = latest_audited_market_values(conn)
    roles = postfinance_account_roles(conn)
    trading_cash_id = roles.get("etrading_cash")
    official_instruments = {key[1] for key in official}
    for key in list(result.positions):
        if key[0] == trading_cash_id and key[1] in official_instruments:
            # Instrument-bearing settlement events live on trading cash, while the
            # authoritative holding belongs to the distinct depot role.
            result.positions.pop(key)
    for key, override in official.items():
        result.positions.setdefault(
            key, Position(account_id=override.account_id, instrument_id=override.instrument_id)
        )
    rows: list[dict[str, str]] = []
    for (account_id, instrument_id), pos in sorted(result.positions.items()):
        row = conn.execute(
            """
            SELECT a.account_name, p.name AS platform, i.name, i.ticker, i.isin, i.asset_class, i.currency,
                   i.hedge_status, i.instrument_status, i.valuation_policy, i.corporate_action_status,
                   mp.close AS latest_price, mp.currency AS latest_price_currency, mp.price_date AS latest_price_date,
                   mp.provider AS latest_price_provider
            FROM accounts a JOIN platforms p ON p.platform_id=a.platform_id
            JOIN instruments i ON i.instrument_id=?
            LEFT JOIN market_prices mp ON mp.market_price_id = (
                SELECT market_price_id FROM market_prices
                WHERE instrument_id=i.instrument_id AND quality_status IN ('fresh','ok')
                ORDER BY price_date DESC, created_at DESC LIMIT 1
            )
            WHERE a.account_id=?
            """,
            (instrument_id, account_id),
        ).fetchone()
        override = official.get((account_id, instrument_id))
        effective_quantity = (
            override.quantity + post_snapshot_deltas.get(instrument_id, Decimal("0"))
            if override
            else pos.quantity
        )
        if override and effective_quantity <= 0:
            continue
        market_value = audited.get((account_id, instrument_id))
        use_market_run = bool(
            market_value
            and (not override or market_value.as_of > override.snapshot_date)
        )
        if override and not use_market_run:
            quantity = effective_quantity
            market_price = override.market_price_original
            market_value_chf = (
                override.market_value_chf * effective_quantity / override.quantity
                if override.quantity
                else Decimal("0")
            )
            price_currency = override.price_currency
            price_date = override.valuation_at
            price_provider = "PostFinance official snapshot"
            valuation_source = "postfinance_official_import"
        elif use_market_run and market_value:
            quantity = effective_quantity
            market_price = market_value.close
            market_value_chf = (
                market_value.value_chf * effective_quantity / market_value.quantity
                if market_value.quantity
                else market_value.value_chf
            )
            price_currency = market_value.currency
            price_date = market_value.as_of
            price_provider = market_value.provider
            valuation_source = "audited_market_run"
        else:
            quantity = pos.quantity
            market_price = pos.market_price_original
            market_value_chf = pos.market_value_chf
            price_currency = row["latest_price_currency"] if row else ""
            price_date = date_text(row["latest_price_date"] if row else "")
            price_provider = row["latest_price_provider"] if row else ""
            valuation_source = "canonical_ledger_market"
        rows.append({
            "account_id": account_id,
            "instrument_id": instrument_id,
            "platform": row["platform"] if row else "",
            "account_name": row["account_name"] if row else account_id,
            "asset_class": row["asset_class"] if row else "",
            "name": row["name"] if row else instrument_id,
            "ticker": row["ticker"] if row else "",
            "isin": row["isin"] if row else "",
            "quantity": dec_text(quantity),
            "cost_basis_chf": dec_text(pos.cost_basis_chf, "0.01"),
            "market_price_original": dec_text(market_price, "0.01") if market_price is not None else "",
            "price_currency": price_currency,
            "price_date": price_date,
            "price_provider": price_provider,
            "market_value_chf": dec_text(market_value_chf, "0.01") if market_value_chf is not None else "",
            "price_status": "ok" if market_price is not None else "missing_market_price",
            "valuation_source": valuation_source,
            "valuation_as_of": price_date,
            "provider_symbol_status": "mapped" if conn.execute("SELECT 1 FROM instrument_price_mappings WHERE instrument_id=? AND mapping_status IN ('mapped','confirmed','user_confirmed') AND provider_symbol IS NOT NULL LIMIT 1", (instrument_id,)).fetchone() else "missing_provider_symbol",
            "hedge_status": row["hedge_status"] if row else pos.hedge_status,
            "instrument_status": row["instrument_status"] if row else pos.instrument_status,
            "valuation_policy": row["valuation_policy"] if row else pos.valuation_policy,
            "fx_status": "ok" if "missing_fx" not in pos.quality_warnings else "missing_fx",
            "market_price_status": "ok" if market_price is not None else "missing_market_price",
            "valuation_timestamp_status": pos.valuation_timestamp_status,
            "corporate_action_status": row["corporate_action_status"] if row else pos.corporate_action_status,
            "valuation_status": pos.valuation_status,
            "total_return_chf": dec_text(pos.total_return_chf, "0.01") if pos.total_return_chf is not None else "",
            "total_return_status": "complete" if pos.total_return_chf is not None else "incomplete",
            "quality_warnings": ",".join(pos.quality_warnings),
            "data_quality_status": pos.data_quality_status,
        })
    return rows

def get_portfolio_positions_user(conn: Connection) -> list[dict[str, str]]:
    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 [


sed -n '500,570p' src/jarvis_finance/services/postfinance_service.py && sed -n '1,80p' src/jarvis_finance/storage/postfinance_schema.py


Output:
Chunk ID: cbbce1
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1870
Output:
        row = conn.execute(
            "SELECT batch_id,audit_id FROM postfinance_import_batches WHERE bundle_sha256=?",
            (preview["bundle_sha256"],),
        ).fetchone()
        return {
            "status": "confirmed",
            "batch_id": row["batch_id"],
            "audit_id": row["audit_id"],
            "idempotent": True,
        }
    accounts = _resolve_accounts(conn, request)
    zip_raw, overview_raw = _source_bytes(request)
    bundle = parse_postfinance_bundle(zip_raw, overview_raw)  # mandatory byte reparse on confirm
    if (
        bundle.bundle_hash != preview["bundle_sha256"]
        or _revision(conn, accounts) != preview["db_revision"]
    ):
        raise ValueError("PostFinance source bytes or database revision changed after preview")
    position_map = _match_instruments(conn, bundle, accounts["etrading_depot"])
    event_instruments = {
        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)
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')),


sed -n '1,460p' src/jarvis_finance/services/cash_service.py && sed -n '1,120p' src/jarvis_finance/ledger/cash.py


Output:
Chunk ID: 6ab9b9
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 5986
Output:
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 _signed_cash_delta, 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] = []
    existing_names = [item["account_name"] for item in CANONICAL_CASH_ACCOUNTS]
    duplicates = [
        row["account_name"]
        for row in conn.execute(
            f"""
            SELECT account_name, COUNT(*) AS n
            FROM accounts
            WHERE account_type='cash' AND account_name IN ({','.join('?' for _ in existing_names)})
            GROUP BY account_name HAVING COUNT(*) > 1
            """,
            existing_names,
        ).fetchall()
    ]
    for item in CANONICAL_CASH_ACCOUNTS:
        rows = conn.execute("SELECT account_id FROM accounts WHERE account_type='cash' AND account_name=?", (item["account_name"],)).fetchall()
        if len(rows) > 1:
            continue
        platform_id = _ensure_platform(conn, item["platform"])
        if rows:
            account_id = rows[0]["account_id"]
            conn.execute(
                "UPDATE accounts SET platform_id=?, currency='CHF', balance_mode=?, portfolio_bucket='cash', updated_at=? WHERE account_id=?",
                (platform_id, item["balance_mode"], _now(), account_id),
            )
        else:
            account_id = _uuid("acct")
            conn.execute(
                """
                INSERT INTO accounts(account_id, platform_id, account_name, account_type, currency, balance_mode, portfolio_bucket, is_active, created_at)
                VALUES (?, ?, ?, 'cash', 'CHF', ?, 'cash', 1, ?)
                """,
                (account_id, platform_id, item["account_name"], item["balance_mode"], _now()),
            )
            created.append(item["account_name"])
            _audit(conn, action="cash_account_seed_confirm", entity_type="cash_account", entity_id=account_id, payload=item, note="Canonical cash account ensured", created_by=created_by)
    conn.commit()
    return {"created": len(created), "created_accounts": created, "duplicates": duplicates}

def _latest_snapshot(conn: Connection, account_id: str, snapshot_type: str):
    return conn.execute(
        """
        SELECT * FROM cash_account_snapshots
        WHERE account_id=? AND snapshot_type=?
        ORDER BY balance_date DESC, created_at DESC
        LIMIT 1
        """,
        (account_id, snapshot_type),
    ).fetchone()

def _cash_movements_after_anchor(conn: Connection, account_id: str, anchor_date: str | None) -> Decimal:
    if not anchor_date:
        return Decimal("0")
    row = conn.execute(
        """
        SELECT COALESCE(SUM(CAST(net_amount_chf AS TEXT)), '0') AS movement
        FROM transactions
        WHERE account_id=? AND COALESCE(is_voided,0)=0 AND COALESCE(is_confirmed,1)=1
          AND source_type LIKE '%csv%'
          AND trade_date > ?
        """,
        (account_id, anchor_date),
    ).fetchone()
    return Decimal(str(row["movement"] or "0"))

def _calculated_balance(conn: Connection, account_id: str) -> Decimal:
    anchor = _latest_snapshot(conn, account_id, "csv_anchor_balance")
    if anchor:
        return Decimal(str(anchor["amount_chf"])) + _cash_movements_after_anchor(conn, account_id, anchor["balance_date"])
    rows = conn.execute(
        "SELECT amount_chf FROM cash_balances WHERE account_id=?",
        (account_id,),
    ).fetchall()
    if rows:
        return sum((Decimal(str(row["amount_chf"])) for row in rows), Decimal("0"))
    # Legacy/demo datasets can have a transaction ledger without materialized
    # cash_balances. Keep the shared read model compatible without overriding an
    # explicit snapshot projection.
    ledger = calculate_cash_balances(conn, persist_alerts=False)
    return sum(
        (
            balance.amount_chf
            for (ledger_account_id, _currency), balance in ledger.balances.items()
            if ledger_account_id == account_id and balance.amount_chf is not None
        ),
        Decimal("0"),
    )

def _last_imported_booking(conn: Connection, account_id: str) -> str | None:
    row = conn.execute("SELECT MAX(trade_date) AS last_date FROM transactions WHERE account_id=? AND source_type LIKE '%csv%'", (account_id,)).fetchone()
    return row["last_date"] if row else None

def _post_snapshot_cash_deltas(
    conn: Connection,
    account_id: str,
    snapshot_date: str,
) -> dict[str, tuple[Decimal, Decimal | None]]:
    rows = conn.execute(
        """SELECT * FROM transactions
           WHERE account_id=? AND trade_date>?
             AND COALESCE(is_confirmed,0)=1 AND COALESCE(is_voided,0)=0
           ORDER BY trade_date,created_at,transaction_id""",
        (account_id, snapshot_date),
    ).fetchall()
    deltas: dict[str, tuple[Decimal, Decimal | None]] = {}
    for row in rows:
        original, amount_chf = _signed_cash_delta(row)
        if original == 0 and amount_chf in {None, Decimal("0")}:
            continue
        currency = str(row["currency_original"])
        previous_original, previous_chf = deltas.get(
            currency,
            (Decimal("0"), Decimal("0")),
        )
        deltas[currency] = (
            previous_original + original,
            None
            if previous_chf is None or amount_chf is None
            else previous_chf + amount_chf,
        )
    return deltas

def list_cash_positions(conn: Connection) -> list[CashPosition]:
    ledger = calculate_cash_balances(conn, persist_alerts=False)
    rows = conn.execute(
        """
        SELECT a.account_id, a.account_name, a.account_type, a.currency, COALESCE(a.balance_mode,'manual') AS balance_mode, p.name AS platform
        FROM accounts a JOIN platforms p ON p.platform_id=a.platform_id
        WHERE a.is_active=1
          AND (
              a.account_type='cash'
              OR a.account_id IN (
                  SELECT account_id FROM postfinance_account_roles
                  WHERE role='etrading_cash'
              )
          )
        ORDER BY p.name, a.account_name
        """
    ).fetchall()
    positions: list[CashPosition] = []
    official_components = latest_official_postfinance_cash(conn)
    official_by_account: dict[str, list] = {}
    for component in official_components:
        official_by_account.setdefault(component.account_id, []).append(component)
    for row in rows:
        account_id = row["account_id"]
        if account_id in official_by_account:
            components = {
                component.currency: component
                for component in official_by_account[account_id]
            }
            snapshot_date = next(iter(components.values())).snapshot_date
            deltas = _post_snapshot_cash_deltas(conn, account_id, snapshot_date)
            for currency in sorted(set(components) | set(deltas)):
                component = components.get(currency)
                delta_original, delta_chf = deltas.get(
                    currency,
                    (Decimal("0"), Decimal("0")),
                )
                amount_original = (
                    component.amount_original if component else Decimal("0")
                ) + delta_original
                baseline_chf = component.amount_chf if component else Decimal("0")
                amount_chf = (
                    baseline_chf + delta_chf if delta_chf is not None else baseline_chf
                )
                status = (
                    "Abgleich offen"
                    if delta_chf is None
                    else "Offiziell abgeglichen + Bewegungen"
                    if delta_original or delta_chf
                    else "Offiziell abgeglichen"
                )
                positions.append(
                    CashPosition(
                        id=f"{account_id}:{currency}",
                        account_id=account_id,
                        platform=row["platform"],
                        account_label=row["account_name"],
                        account_type=row["account_type"],
                        currency=currency,
                        balance_mode="official_components",
                        amount=decimal_text(amount_original, 2),
                        amount_chf=decimal_text(amount_chf, 2),
                        calculated_balance_chf=decimal_text(amount_chf, 2),
                        manual_balance_chf=None,
                        csv_anchor_balance_chf=None,
                        used_value_chf=decimal_text(amount_chf, 2),
                        difference_chf="0.00",
                        last_manual_reconciliation=snapshot_date,
                        last_imported_booking=_last_imported_booking(conn, account_id),
                        status=status,
                    )
                )
            continue
        manual = _latest_snapshot(conn, account_id, "manual_balance")
        anchor = _latest_snapshot(conn, account_id, "csv_anchor_balance")
        mode = row["balance_mode"] or "manual"
        default_currency = str(row["currency"])
        ledger_currencies = {
            currency
            for ledger_account_id, currency in ledger.balances
            if ledger_account_id == account_id
        }
        currencies = set(ledger_currencies)
        if not currencies or manual or anchor:
            currencies.add(default_currency)
        for currency in sorted(currencies):
            ledger_balance = ledger.balances.get((account_id, currency))
            if ledger_balance is not None:
                calculated = ledger_balance.amount_chf or Decimal("0")
                amount_original = ledger_balance.amount_original
            else:
                calculated = _calculated_balance(conn, account_id)
                amount_original = (
                    calculated if currency.upper() == "CHF" else Decimal("0")
                )
            manual_amount = (
                Decimal(str(manual["amount_chf"]))
                if manual and currency == default_currency
                else None
            )
            used = (
                manual_amount
                if mode == "manual" and manual_amount is not None
                else calculated
            )
            difference = (
                manual_amount - calculated
                if manual_amount is not None
                else Decimal("0")
            )
            status = "Abgleich offen" if manual_amount is not None and difference != 0 else "Aktuell"
            positions.append(
                CashPosition(
                    id=f"{account_id}:{currency}",
                    account_id=account_id,
                    platform=row["platform"],
                    account_label=row["account_name"],
                    account_type=row["account_type"],
                    currency=currency,
                    balance_mode=mode,
                    amount=decimal_text(amount_original, 2),
                    amount_chf=decimal_text(used, 2),
                    calculated_balance_chf=decimal_text(calculated, 2),
                    manual_balance_chf=(
                        decimal_text(manual_amount, 2)
                        if manual_amount is not None
                        else None
                    ),
                    csv_anchor_balance_chf=(
                        decimal_text(anchor["amount_chf"], 2)
                        if anchor and currency == default_currency
                        else None
                    ),
                    used_value_chf=decimal_text(used, 2),
                    difference_chf=decimal_text(difference, 2),
                    last_manual_reconciliation=(
                        manual["balance_date"]
                        if manual and currency == default_currency
                        else None
                    ),
                    last_imported_booking=_last_imported_booking(conn, account_id),
                    status=status,
                )
            )
    return positions

def get_cash_summary(conn: Connection) -> CashSummary:
    # Read paths must never seed or refresh accounts; setup is an explicit Confirm action.
    positions = list_cash_positions(conn)
    total = sum(Decimal(p.used_value_chf) for p in positions)
    last_dates = [p.last_manual_reconciliation for p in positions if p.last_manual_reconciliation]
    return CashSummary(
        base_currency="CHF",
        cash_chf=decimal_text(total, 2),
        manual_accounts_count=sum(1 for p in positions if p.balance_mode == "manual"),
        csv_calculated_accounts_count=sum(1 for p in positions if p.balance_mode == "csv_calculated"),
        open_reconciliation_count=sum(1 for p in positions if p.status == "Abgleich offen"),
        last_reconciliation_at=max(last_dates) if last_dates else None,
        positions=positions,
    )

def preview_cash_snapshot(conn: Connection, request: CashSnapshotPreviewRequest) -> PreviewResponse:
    row = conn.execute("SELECT account_id, account_type FROM accounts WHERE account_id=?", (request.account_id,)).fetchone()
    if not row or row["account_type"] != "cash":
        raise HTTPException(status_code=404, detail="Cash account not found")
    if request.snapshot_type not in {"manual_balance", "csv_anchor_balance", "calculated_balance", "reconciliation"}:
        raise HTTPException(status_code=422, detail="Invalid snapshot_type")
    amount = _decimal(request.amount_chf, "amount_chf")
    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=[])

def confirm_cash_snapshot(conn: Connection, request: CashSnapshotConfirmRequest) -> ConfirmResponse:
    if not request.confirm:
        raise HTTPException(status_code=400, detail="Explicit confirm required")
    preview_cash_snapshot(conn, request)
    amount = _decimal(request.amount_chf, "amount_chf")
    snapshot_id = _uuid("cashsnap")
    now = _now()
    source = "manual" if request.snapshot_type in {"manual_balance", "reconciliation"} else "csv_import" if request.snapshot_type == "csv_anchor_balance" else "system_calculated"
    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)
    conn.execute(
        """
        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)
        VALUES (?, ?, ?, ?, ?, 'CHF', ?, ?, ?, ?, 'user', ?)
        """,
        (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),
    )
    conn.commit()
    return ConfirmResponse(status="confirmed", entity_id=snapshot_id, audit_id=audit_id, message="Kontostand gespeichert · Audit geschrieben")

def get_cash_position_detail(conn: Connection, position_id: str) -> CashPositionDetail:
    position = next((p for p in get_cash_summary(conn).positions if p.id == position_id), None)
    if not position:
        raise HTTPException(status_code=404, detail="Cash position not found")
    account_id = position.account_id
    rows = conn.execute(
        """
        SELECT snapshot_id, snapshot_type, balance_date, amount_chf, source, COALESCE(note,'') AS note, audit_id
        FROM cash_account_snapshots
        WHERE account_id=?
        ORDER BY balance_date DESC, created_at DESC
        LIMIT 30
        """,
        (account_id,),
    ).fetchall()
    history: list[CashSnapshot] = []
    previous: Decimal | None = None
    for row in reversed(rows):
        current = Decimal(str(row["amount_chf"]))
        diff = None if previous is None else decimal_text(current - previous, 2)
        previous = current
        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))
    history = list(reversed(history))
    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)])
from __future__ import annotations

from dataclasses import dataclass, field
from decimal import Decimal
from sqlite3 import Connection

from jarvis_finance.audit.log import record_audit_event
from jarvis_finance.imports.common import stable_id, utc_now
from jarvis_finance.quality.alerts import create_alert

@dataclass
class CashBalance:
    account_id: str
    currency: str
    amount_original: Decimal = Decimal("0")
    amount_chf: Decimal | None = Decimal("0")
    quality_status: str = "ok"

@dataclass
class CashCalculationResult:
    balances: dict[tuple[str, str], CashBalance] = field(default_factory=dict)
    negative_cash_warnings: list[str] = field(default_factory=list)

def _d(value: object, default: str = "0") -> Decimal:
    if value is None or str(value).strip() == "":
        return Decimal(default)
    return Decimal(str(value))

def _signed_cash_delta(row) -> tuple[Decimal, Decimal | None]:
    t = row["transaction_type"]
    gross = _d(row["gross_amount_original"])
    net = _d(row["net_amount_original"]) if row["net_amount_original"] is not None else None
    fee = _d(row["fee_original"])
    tax = _d(row["tax_original"])
    fx = _d(row["fx_rate_to_chf"], "1") if row["fx_rate_to_chf"] is not None else None

    if t == "initial_cash_snapshot":
        original = net if net is not None else gross
    elif t == "cash_deposit":
        original = net if net is not None else gross
    elif t == "cash_withdrawal":
        original = -(net if net is not None else gross)
    elif t == "buy":
        original = -(net if net is not None else gross + fee + tax)
    elif t in {"partial_sell", "full_sell"}:
        original = net if net is not None else gross - fee - tax
    elif t in {"dividend", "etf_distribution"}:
        original = net if net is not None else gross - tax - fee
    elif t in {"fee", "tax"}:
        original = -(net if net is not None else gross or fee or tax)
    elif t == "fx_conversion":
        original = net if net is not None else gross
    elif t == "manual_cash_correction":
        original = net if net is not None else gross
    else:
        original = Decimal("0")
    chf = original * fx if fx is not None else None
    return original, chf

def calculate_cash_balances(
    conn: Connection, *, as_of_date: str | None = None, persist_alerts: bool = True
) -> CashCalculationResult:
    result = CashCalculationResult()
    params: tuple[object, ...] = ()
    where = "WHERE COALESCE(is_voided, 0) = 0"
    if as_of_date:
        where += " AND trade_date <= ?"
        params = (as_of_date,)
    rows = conn.execute(
        f"""
        SELECT * FROM transactions
        {where}
        ORDER BY account_id, currency_original, trade_date, created_at, transaction_id
        """,
        params,
    ).fetchall()
    for row in rows:
        account_id = row["account_id"]
        currency = row["currency_original"]
        key = (account_id, currency)
        balance = result.balances.setdefault(key, CashBalance(account_id, currency))
        original, chf = _signed_cash_delta(row)
        balance.amount_original += original
        if chf is None:
            balance.amount_chf = None
            balance.quality_status = "incomplete"
        elif balance.amount_chf is not None:
            balance.amount_chf += chf
    for key, balance in result.balances.items():
        if balance.amount_original < 0:
            msg = f"Negative cash balance for account={balance.account_id} currency={balance.currency}: {balance.amount_original}"
            result.negative_cash_warnings.append(msg)
            if persist_alerts:
                create_alert(
                    conn,
                    priority="warnung",
                    category="cash",
                    entity_type="account",
                    entity_id=balance.account_id,
                    rule_id="negative_cash",
                    message=msg,
                    evidence={"currency": balance.currency, "amount_original": str(balance.amount_original)},
                )
    if persist_alerts:
        conn.commit()
    return result

def apply_manual_cash_correction(
    conn: Connection,
    *,
    account_id: str,
    currency: str,
    amount: Decimal,
    note: str,


grep -R "calculate_cash_balances" -n tests src | head -50 && grep -R "is_confirmed.*0" -n tests | head -20


Output:
Chunk ID: aea7d3
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 2418
Output:
grep: tests/unit/__pycache__/test_dashboard_mvp_regressions.cpython-311-pytest-9.0.2.pyc: binary file matches
grep: tests/unit/__pycache__/test_dashboard_mvp_regressions.cpython-311-pytest-9.1.1.pyc: binary file matches
grep: tests/unit/__pycache__/test_dashboard_mvp_regressions.cpython-311.pyc: binary file matches
grep: tests/unit/__pycache__/test_ledger_cash_wac_positions.cpython-311.pyc: binary file matches
grep: tests/unit/__pycache__/test_ledger_cash_wac_positions.cpython-311-pytest-9.1.1.pyc: binary file matches
grep: tests/unit/__pycache__/test_ledger_cash_wac_positions.cpython-311-pytest-9.0.2.pyc: binary file matches
grep: src/jarvis_finance/ledger/__pycache__/cash.cpython-311.pyc: binary file matches
grep: src/jarvis_finance/services/__pycache__/cash_service.cpython-311.pyc: binary file matches
tests/unit/test_dashboard_mvp_regressions.py:9:from jarvis_finance.ledger.cash import calculate_cash_balances
tests/unit/test_dashboard_mvp_regressions.py:126:    balances = calculate_cash_balances(conn)
tests/unit/test_ledger_cash_wac_positions.py:5:from jarvis_finance.ledger.cash import apply_manual_cash_correction, calculate_cash_balances
tests/unit/test_ledger_cash_wac_positions.py:52:    result = calculate_cash_balances(conn)
tests/unit/test_ledger_cash_wac_positions.py:65:    result = calculate_cash_balances(conn)
src/jarvis_finance/ledger/cash.py:65:def calculate_cash_balances(
src/jarvis_finance/services/cash_service.py:22:from jarvis_finance.ledger.cash import _signed_cash_delta, calculate_cash_balances
src/jarvis_finance/services/cash_service.py:161:    ledger = calculate_cash_balances(conn, persist_alerts=False)
src/jarvis_finance/services/cash_service.py:209:    ledger = calculate_cash_balances(conn, persist_alerts=False)
grep: tests/unit/__pycache__/test_sprint9_fx_fmp_history.cpython-311-pytest-9.1.1.pyc: binary file matches
grep: tests/unit/__pycache__/test_true_wealth_mapping_candidates_c3d.cpython-311.pyc: binary file matches
grep: tests/unit/__pycache__/test_vue_dashboard_v12_acceptance.cpython-311-pytest-9.1.1.pyc: binary file matches
grep: tests/unit/__pycache__/test_dashboard_functional_actions_v2.cpython-311-pytest-9.0.2.pyc: binary file matches
grep: tests/unit/__pycache__/test_sprint11_portfolio_completeness.cpython-311-pytest-9.0.2.pyc: binary file matches
grep: tests/unit/__pycache__/test_cash_truewealth_management.cpython-311-pytest-9.0.2.pyc: binary file matches
grep: tests/unit/__pycache__/test_vue_dashboard_v12_acceptance.cpython-311-pytest-9.0.2.pyc: binary file matches
grep: tests/unit/__pycache__/test_vue_dashboard_v12_acceptance.cpython-311.pyc: binary file matches
grep: tests/unit/__pycache__/test_portfolio_advisor_phase8.cpython-311-pytest-9.0.2.pyc: binary file matches
grep: tests/unit/__pycache__/test_sprint9_fx_fmp_history.cpython-311-pytest-9.0.2.pyc: binary file matches
grep: tests/unit/__pycache__/test_dashboard_functional_actions_v2.cpython-311-pytest-9.1.1.pyc: binary file matches
grep: tests/unit/__pycache__/test_dashboard_functional_actions_v2.cpython-311.pyc: binary file matches
grep: tests/unit/__pycache__/test_true_wealth_mapping_candidates_c3.cpython-311.pyc: binary file matches
grep: tests/unit/__pycache__/test_cash_truewealth_management.cpython-311-pytest-9.1.1.pyc: binary file matches
grep: tests/unit/__pycache__/test_sprint11_portfolio_completeness.cpython-311.pyc: binary file matches
grep: tests/unit/__pycache__/test_cash_truewealth_management.cpython-311.pyc: binary file matches
grep: tests/unit/__pycache__/test_sprint9_fx_fmp_history.cpython-311.pyc: binary file matches
grep: tests/unit/__pycache__/test_portfolio_advisor_phase8.cpython-311.pyc: binary file matches
grep: tests/unit/__pycache__/test_portfolio_advisor_phase8.cpython-311-pytest-9.1.1.pyc: binary file matches
grep: tests/unit/__pycache__/test_sprint11_portfolio_completeness.cpython-311-pytest-9.1.1.pyc: binary file matches
grep: tests/unit/__pycache__/test_true_wealth_mapping_candidates_c3d.cpython-311-pytest-9.1.1.pyc: binary file matches
grep: tests/unit/__pycache__/test_true_wealth_mapping_candidates_c3.cpython-311-pytest-9.0.2.pyc: binary file matches
grep: tests/unit/__pycache__/test_true_wealth_mapping_candidates_c3.cpython-311-pytest-9.1.1.pyc: binary file matches
grep: tests/unit/__pycache__/test_true_wealth_mapping_candidates_c3d.cpython-311-pytest-9.0.2.pyc: binary file matches
tests/unit/test_sprint11_portfolio_completeness.py:55:            conn.execute("INSERT INTO transactions(transaction_id,transaction_type,account_id,instrument_id,trade_date,quantity,currency_original,source_type,source_id,row_hash,is_confirmed,quality_status,created_at) VALUES(?, 'initial_position_snapshot','etrading',?,'2026-07-01','1','CHF','confirmed',?,?,1,'ok',?)", (f"tx-{index:02d}", instrument_id, f"source-{index:02d}", f"hash-{index:02d}", NOW))
tests/unit/test_portfolio_advisor_phase8.py:30:    conn.execute("INSERT INTO transactions(transaction_id, transaction_type, account_id, instrument_id, trade_date, gross_amount_chf, net_amount_chf, currency_original, source_type, is_confirmed, quality_status, created_at) VALUES ('tx_pf', 'initial_snapshot', 'acct_pf', 'inst_vti', '2026-06-30', '30000.00', '30000.00', 'USD', 'fixture', 1, 'ok', ?)", (now,))
tests/unit/test_vue_dashboard_v12_acceptance.py:135:    conn.execute("INSERT INTO transactions(transaction_id, transaction_type, account_id, instrument_id, trade_date, quantity, gross_amount_original, net_amount_original, currency_original, source_type, is_confirmed, quality_status, notes, created_at) VALUES ('review_tx', 'initial_position_snapshot', 'review_acc', 'usd1', '2026-05-16', '1', '1', '1', 'USD', 'broker_import_reviewed_snapshot', 1, 'ok', 'review artifact', ?)", (now,))
tests/unit/test_vue_dashboard_v12_acceptance.py:136:    conn.execute("INSERT INTO transactions(transaction_id, transaction_type, account_id, instrument_id, trade_date, quantity, gross_amount_original, net_amount_original, currency_original, source_type, is_confirmed, quality_status, notes, created_at) VALUES ('manual_tx', 'initial_position_snapshot', 'broker1', 'chf1', '2026-05-16', '1', '1', '1', 'CHF', 'vue_manual_position', 1, 'ok', 'real manual', ?)", (now,))
tests/unit/test_vue_dashboard_v12_acceptance.py:168:    conn.execute("INSERT INTO transactions(transaction_id, transaction_type, account_id, instrument_id, trade_date, quantity, gross_amount_original, net_amount_original, currency_original, fx_rate_to_chf, fx_status, source_type, is_confirmed, quality_status, notes, created_at) VALUES ('legacy_snapshot', 'initial_snapshot', 'broker1', 'usd1', '2026-05-16', '1', '10', '10', 'USD', '0.91', 'ok', 'manual_initial_snapshot', 1, 'ok', 'legacy manual snapshot', '2026-05-16T12:00:00Z')")
tests/unit/test_vue_dashboard_v12_acceptance.py:176:    conn.execute("INSERT INTO transactions(transaction_id, transaction_type, account_id, instrument_id, trade_date, quantity, gross_amount_original, net_amount_original, currency_original, fx_rate_to_chf, fx_status, source_type, is_confirmed, quality_status, notes, created_at) VALUES ('manual_adjustment_snapshot', 'manual_adjustment', 'broker1', 'usd1', '2026-05-16', '1', '10', '10', 'USD', '0.91', 'ok', 'manual_dashboard', 1, 'ok', 'manual adjustment snapshot', '2026-05-16T12:00:00Z')")
tests/unit/test_vue_dashboard_v12_acceptance.py:185:    conn.execute("INSERT INTO transactions(transaction_id, transaction_type, account_id, instrument_id, trade_date, quantity, gross_amount_original, net_amount_original, currency_original, source_type, is_confirmed, quality_status, notes, created_at) VALUES ('hist_tx', 'buy', 'broker1', 'chf1', '2026-05-17', '1', '10', '10', 'CHF', 'vue_manual_position', 1, 'ok', 'history', '2026-05-17T12:00:00Z')")
tests/unit/test_sprint9_fx_fmp_history.py:130:           WHERE t.is_confirmed=1 AND t.is_voided=0 AND i.trading_currency='EUR'
tests/unit/test_true_wealth_mapping_candidates_c3.py:84:    conn.execute("INSERT INTO transactions(transaction_id,transaction_type,account_id,instrument_id,trade_date,quantity,currency_original,fx_status,source_type,is_confirmed,quality_status,created_at) VALUES('tx1','initial_position_snapshot','a1','inst1','2025-12-31','1','USD','missing','broker_import_reviewed_snapshot',1,'warning','now')")
tests/unit/test_cash_truewealth_management.py:105:        "INSERT INTO transactions(transaction_id, transaction_type, account_id, trade_date, gross_amount_original, net_amount_original, currency_original, fx_rate_to_chf, fx_status, gross_amount_chf, net_amount_chf, source_type, is_confirmed, quality_status, created_at) VALUES ('tx_csv_1', 'csv_import', ?, '2026-05-05', '250.00', '250.00', 'CHF', '1', 'ok', '250.00', '250.00', 'csv_import', 1, 'ok', '2026-05-05')",
tests/unit/test_dashboard_functional_actions_v2.py:40:    conn.execute("INSERT INTO transactions(transaction_id, transaction_type, account_id, instrument_id, trade_date, quantity, gross_amount_original, net_amount_original, currency_original, fx_rate_to_chf, fx_status, gross_amount_chf, net_amount_chf, source_type, is_confirmed, quality_status, notes, created_at) VALUES ('buy1', 'buy', 'broker1', 'inst1', '2026-05-16', '10', '100', '100', 'CHF', '1', 'not_needed', '100', '100', 'fixture', 1, 'ok', 'opening buy', ?)", (now,))
tests/unit/test_true_wealth_mapping_candidates_c3d.py:66:    conn.execute("INSERT INTO transactions(transaction_id,transaction_type,account_id,instrument_id,trade_date,quantity,currency_original,fx_status,source_type,is_confirmed,quality_status,created_at) VALUES('tx1','initial_position_snapshot','a1','inst1','2025-12-31','1','USD','missing','broker_import_reviewed_snapshot',1,'warning','now')")


Output:
Chunk ID: 3a91b0
Wall time: 30.0013 seconds
Process running with session ID 63376
Original token count: 7
Output:
...........................


Output:
Chunk ID: 0c8625
Wall time: 30.0011 seconds
Process running with session ID 63376
Original token count: 0
Output:


Output:
Chunk ID: dc2642
Wall time: 30.0018 seconds
Process running with session ID 63376
Original token count: 0
Output:


Output:
Chunk ID: 4a10e7
Wall time: 30.0023 seconds
Process running with session ID 63376
Original token count: 0
Output:


Output:
Chunk ID: 99c050
Wall time: 30.0012 seconds
Process running with session ID 63376
Original token count: 0
Output:


Output: write_stdin failed: stdin is closed for this session; rerun exec_command with tty=true to keep stdin open


Output:
Chunk ID: dd6f6e
Wall time: 30.0009 seconds
Process running with session ID 63376
Original token count: 0
Output:


nl -ba src/jarvis_finance/services/cash_service.py | sed -n '245,315p'


Output:
Chunk ID: fe516d
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 946
Output:
   245	                amount_original = (
   246	                    component.amount_original if component else Decimal("0")
   247	                ) + delta_original
   248	                baseline_chf = component.amount_chf if component else Decimal("0")
   249	                amount_chf = (
   250	                    baseline_chf + delta_chf if delta_chf is not None else baseline_chf
   251	                )
   252	                status = (
   253	                    "Abgleich offen"
   254	                    if delta_chf is None
   255	                    else "Offiziell abgeglichen + Bewegungen"
   256	                    if delta_original or delta_chf
   257	                    else "Offiziell abgeglichen"
   258	                )
   259	                positions.append(
   260	                    CashPosition(
   261	                        id=f"{account_id}:{currency}",
   262	                        account_id=account_id,
   263	                        platform=row["platform"],
   264	                        account_label=row["account_name"],
   265	                        account_type=row["account_type"],
   266	                        currency=currency,
   267	                        balance_mode="official_components",
   268	                        amount=decimal_text(amount_original, 2),
   269	                        amount_chf=decimal_text(amount_chf, 2),
   270	                        calculated_balance_chf=decimal_text(amount_chf, 2),
   271	                        manual_balance_chf=None,
   272	                        csv_anchor_balance_chf=None,
   273	                        used_value_chf=decimal_text(amount_chf, 2),
   274	                        difference_chf="0.00",
   275	                        last_manual_reconciliation=snapshot_date,
   276	                        last_imported_booking=_last_imported_booking(conn, account_id),
   277	                        status=status,
   278	                    )
   279	                )
   280	            continue
   281	        manual = _latest_snapshot(conn, account_id, "manual_balance")
   282	        anchor = _latest_snapshot(conn, account_id, "csv_anchor_balance")
   283	        mode = row["balance_mode"] or "manual"
   284	        default_currency = str(row["currency"])
   285	        ledger_currencies = {
   286	            currency
   287	            for ledger_account_id, currency in ledger.balances
   288	            if ledger_account_id == account_id
   289	        }
   290	        currencies = set(ledger_currencies)
   291	        if not currencies or manual or anchor:
   292	            currencies.add(default_currency)
   293	        for currency in sorted(currencies):
   294	            ledger_balance = ledger.balances.get((account_id, currency))
   295	            if ledger_balance is not None:
   296	                calculated = ledger_balance.amount_chf or Decimal("0")
   297	                amount_original = ledger_balance.amount_original
   298	            else:
   299	                calculated = _calculated_balance(conn, account_id)
   300	                amount_original = (
   301	                    calculated if currency.upper() == "CHF" else Decimal("0")
   302	                )
   303	            manual_amount = (
   304	                Decimal(str(manual["amount_chf"]))
   305	                if manual and currency == default_currency
   306	                else None
   307	            )
   308	            used = (
   309	                manual_amount
   310	                if mode == "manual" and manual_amount is not None
   311	                else calculated
   312	            )
   313	            difference = (
   314	                manual_amount - calculated
   315	                if manual_amount is not None


grep -n "def _.*account" -n src/jarvis_finance/services/portfolio_data.py | head -30 && sed -n '1400,1468p' src/jarvis_finance/services/portfolio_data.py


Output:
Chunk ID: 8d8c8f
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1060
Output:
131:def _validate_scope(conn: Connection, scope_kind: str, account_id: str | None) -> list[dict[str, str]]:
212:def _legacy_account_records(
410:def _postfinance_account(conn: Connection) -> dict[str, str]:
913:def _records(conn: Connection, source_key: str, accounts: list[dict[str, str]], period_from: str, period_to: str, cutoff: str, request: dict[str, Any] | None = None) -> list[SourceRecord]:
            "last_successful_confirmed_at": batch["confirmed_at"] if batch else None,
            "last_attempt_at": (attempt["finished_at"] if attempt else None) or (batch["confirmed_at"] if batch else None),
            "last_attempt_status": (attempt["status"] if attempt else None) or (batch["status"] if batch else None),
            "known_gaps": ["Keine neue externe Provider-Abfrage", "Keine automatische Ingestion beim Start"],
        })
    fingerprint = _hash([[row["source_key"], row["record_count"], row["last_successful_confirmed_at"]] for row in rows])
    return {"sources": rows, "data_cutoff": _now().isoformat(), "input_fingerprint": fingerprint}

def ingestion_history(conn: Connection, *, limit: int = 50, offset: int = 0) -> dict[str, Any]:
    if limit < 1 or limit > 100 or offset < 0:
        raise ValueError("Pagination ist ungültig")
    total = int(conn.execute("SELECT COUNT(*) AS count FROM portfolio_ingestion_batches").fetchone()["count"])
    rows = conn.execute(
        """SELECT batch_id, source_key, scope_kind, scope_id, period_from, period_to, data_cutoff,
                  input_fingerprint, status, counts_json, confirmed_at
           FROM portfolio_ingestion_batches ORDER BY confirmed_at DESC LIMIT ? OFFSET ?""", (limit, offset)
    ).fetchall()
    return {
        "items": [{key: value for key, value in {
                    **dict(row), "batch_id": _safe_ref("batch", str(row["batch_id"])),
                    "scope_id": _safe_ref("account", str(row["scope_id"])) if row["scope_id"] else None,
                    "counts": json.loads(row["counts_json"]),
                  }.items() if key != "counts_json"} for row in rows],
        "limit": limit, "offset": offset, "total": total,
    }

def _status(diff: Decimal, base: Decimal | None, absolute: Decimal, relative: Decimal) -> str:
    if diff == 0:
        return "matched"
    if abs(diff) <= absolute:
        return "within_tolerance"
    if base is not None and base != 0 and abs(diff / base) <= relative:
        return "within_tolerance"
    return "mismatch"

def _latest_fx(conn: Connection, currency: str, as_of: str, cutoff: str) -> tuple[Decimal | None, str | None, str | None]:
    if currency == "CHF":
        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 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()
    if not row:
        return None, None, None
    return _stored_decimal(row["rate"], "FX", positive=True), str(row["rate_date"]), str(row["provider"])

def build_portfolio_reconciliation(
    conn: Connection, *, as_of: str, data_cutoff: str | None = None, account_id: str | None = None,
    base_currency: str = "CHF", absolute_tolerance: str = "0.01", relative_tolerance: str = "0.001",
    limit: int = 200, offset: int = 0,
) -> dict[str, Any]:
    _date(as_of, "Stichtag")
    cutoff = _instant(data_cutoff or _now().isoformat(), "Data-Cutoff").isoformat()
    if base_currency.upper() != "CHF":
        raise ValueError("Reconciliation v1 unterstützt als Basiswährung ausschliesslich CHF")
    absolute = _decimal(absolute_tolerance, "Absolute Toleranz", non_negative=True)
    relative = _decimal(relative_tolerance, "Relative Toleranz", non_negative=True)
    if limit < 1 or limit > 500 or offset < 0:
        raise ValueError("Pagination ist ungültig")
    accounts = _validate_scope(conn, "account" if account_id else "portfolio", account_id)
    account_map = {row["account_id"]: row for row in accounts}
    if not account_map:


sed -n '120,180p' src/jarvis_finance/services/portfolio_data.py


Output:
Chunk ID: 8ce686
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 725
Output:

def _safe_ref(prefix: str, value: str) -> str:
    return f"{prefix}-" + hashlib.sha256(value.encode()).hexdigest()[:12]

def _safe_label(value: str) -> str:
    sensitive = r"\b[A-Z]{2}\s?\d{2}(?:\s?[A-Z0-9]){11,30}\b|\b\d{10,}\b|\b0x[a-f0-9]{20,}\b|(?:^|\s)[~.]?[/\\]|\.(?:csv|xlsx|sqlite|db|json)\b"
    return "Konto (redigiert)" if re.search(sensitive, value, flags=re.IGNORECASE) else value

def _validate_scope(conn: Connection, scope_kind: str, account_id: str | None) -> list[dict[str, str]]:
    if scope_kind not in {"portfolio", "account"}:
        raise ValueError("Scope muss portfolio oder account sein")
    if scope_kind == "account" and not account_id:
        raise ValueError("Für den Account-Scope ist ein Konto erforderlich")
    params: tuple[object, ...] = ()
    where = "WHERE is_active=1 AND performance_included=1"
    if account_id:
        where += " AND account_id=?"
        params = (account_id,)
    rows = conn.execute(f"SELECT account_id, account_name, account_type, currency FROM accounts {where} ORDER BY account_id", params).fetchall()
    if scope_kind == "account" and not rows:
        raise ValueError("Portfolio-Konto wurde nicht gefunden")
    return [dict(row) for row in rows]

def _validate_request(conn: Connection, request: dict[str, Any]) -> tuple[list[dict[str, str]], str, str, str]:
    source_key = str(request.get("source_key") or "")
    if source_key not in SOURCE_KEYS:
        raise ValueError("Datenquelle ist nicht vorhanden oder nicht für Ingestion freigegeben")
    period_from = str(request.get("period_from") or "")
    period_to = str(request.get("period_to") or "")
    start, end = _date(period_from, "Von"), _date(period_to, "Bis")
    if start > end:
        raise ValueError("Zeitraum: 'von' darf nicht nach 'bis' liegen")
    cutoff = str(request.get("data_cutoff") or _now().isoformat())
    cutoff = _instant(cutoff, "Data-Cutoff").isoformat()
    scope_kind = str(request.get("scope_kind") or "portfolio")
    account_id = str(request.get("account_id") or "") or None
    accounts = _validate_scope(conn, scope_kind, account_id)
    return accounts, period_from, period_to, cutoff

def _record_ref(source_key: str, raw_id: str) -> str:
    return _safe_ref("record", f"{source_key}:{raw_id}")

def _existing_lineage(conn: Connection, lineage_hash: str) -> tuple[str, str] | None:
    row = conn.execute(
        """SELECT target_type, target_id FROM portfolio_ingestion_items
           WHERE lineage_hash=? AND disposition IN ('new','versioned','unchanged') AND target_id IS NOT NULL
           ORDER BY created_at DESC LIMIT 1""",
        (lineage_hash,),
    ).fetchone()
    return (str(row["target_type"]), str(row["target_id"])) if row else None

def _valuation_snapshot_id(source_key: str, lineage_hash: str, version: int) -> str:
    return "valuation-" + _hash([source_key, lineage_hash, version])[:24]


grep -n "def _resolve_accounts\|def _ensure_efinance" -A100 src/jarvis_finance/services/postfinance_service.py


Output:
Chunk ID: 9dbe9a
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1983
Output:
69:def _resolve_accounts(conn: Connection, request: dict[str, Any] | None = None) -> dict[str, str]:
70-    request = request or {}
71-    explicit = request.get("account_roles") or {}
72-    prospective_efinance = False
73-    if explicit:
74-        if set(explicit) != {"efinance", "etrading_depot", "etrading_cash"}:
75-            raise ValueError("All three PostFinance account roles are required")
76-        result = {key: str(value) for key, value in explicit.items()}
77-    else:
78-        rows = conn.execute(
79-            """SELECT a.account_id,a.account_name,a.account_type,a.platform_id
80-               FROM accounts a JOIN platforms p ON p.platform_id=a.platform_id
81-               WHERE a.is_active=1 AND replace(replace(lower(p.name),' ',''),'-','') LIKE '%postfinance%'
82-               ORDER BY a.account_id"""
83-        ).fetchall()
84-        depot = [
85-            row
86-            for row in rows
87-            if "trading" in str(row["account_name"]).lower()
88-            and str(row["account_type"]).lower() in {"brokerage", "depot"}
89-            or str(row["account_type"]).lower() in {"brokerage", "depot"}
90-        ]
91-        cash = [
92-            row
93-            for row in rows
94-            if row not in depot
95-            and (
96-                "cash" in str(row["account_name"]).lower()
97-                or "finance" in str(row["account_name"]).lower()
98-                or str(row["account_type"]).lower() in {"cash", "bank", "checking"}
99-            )
100-        ]
101-        efinance = [
102-            row
103-            for row in cash
104-            if "efinance"
105-            in str(row["account_name"])
106-            .lower()
107-            .replace("postfinance", "")
108-            .replace("-", "")
109-            .replace(" ", "")
110-        ]
111-        trading_cash = [row for row in cash if row not in efinance]
112-        if len(depot) != 1 or len(trading_cash) != 1 or len(efinance) > 1:
113-            raise ValueError(
114-                "PostFinance E-Finance, E-Trading depot and E-Trading cash cannot be resolved unambiguously"
115-            )
116-        if efinance:
117-            efinance_id = str(efinance[0]["account_id"])
118-        else:
119-            efinance_id = f"account_pf_efinance_{_hash(depot[0]['platform_id'])[:16]}"
120-            prospective_efinance = True
121-        result = {
122-            "efinance": efinance_id,
123-            "etrading_depot": str(depot[0]["account_id"]),
124-            "etrading_cash": str(trading_cash[0]["account_id"]),
125-        }
126-    if len(set(result.values())) != 3:
127-        raise ValueError("PostFinance account roles must map to three distinct canonical accounts")
128-    for role, account_id in result.items():
129-        row = conn.execute(
130-            "SELECT is_active FROM accounts WHERE account_id=?", (account_id,)
131-        ).fetchone()
132-        if not row and role == "efinance" and prospective_efinance:
133-            continue
134-        if not row or not row["is_active"]:
135-            raise ValueError(f"PostFinance account role {role} is unavailable")
136-    return result
137-
138-
139:def _ensure_efinance_account(conn: Connection, accounts: dict[str, str], now: str) -> None:
140-    if conn.execute(
141-        "SELECT 1 FROM accounts WHERE account_id=?", (accounts["efinance"],)
142-    ).fetchone():
143-        return
144-    depot = conn.execute(
145-        "SELECT platform_id FROM accounts WHERE account_id=? AND is_active=1",
146-        (accounts["etrading_depot"],),
147-    ).fetchone()
148-    if not depot:
149-        raise ValueError("PostFinance depot account is unavailable")
150-    conn.execute(
151-        """INSERT INTO accounts(account_id,platform_id,account_name,account_type,currency,
152-               performance_included,is_active,created_at,portfolio_bucket,notes)
153-           VALUES(?,?,'PostFinance E-Finance','cash','CHF',1,1,?,'cash',
154-                  'Created by verified PostFinance source-role separation')""",
155-        (accounts["efinance"], depot["platform_id"], now),
156-    )
157-
158-
159-def _revision(conn: Connection, accounts: dict[str, str]) -> str:
160-    account_ids = sorted(set(accounts.values()))
161-    marks = ",".join("?" for _ in account_ids)
162-    tables = [
163-        "account_value_snapshots",
164-        "positions_snapshot",
165-        "cash_balances",
166-        "transactions",
167-        "postfinance_import_batches",
168-    ]
169-    state: list[object] = []
170-    for table in tables:
171-        if table == "postfinance_import_batches":
172-            row = conn.execute(
173-                f"SELECT COUNT(*) c,COALESCE(MAX(confirmed_at),'') m FROM {table}"
174-            ).fetchone()
175-        else:
176-            row = conn.execute(
177-                f"SELECT COUNT(*) c,COALESCE(MAX(created_at),'') m FROM {table} WHERE account_id IN ({marks})",
178-                account_ids,
179-            ).fetchone()
180-        state.extend((table, row["c"], row["m"]))
181-    return _hash(state)
182-
183-
184-def _instrument_rows(conn: Connection, account_id: str) -> list[Any]:
185-    rows = conn.execute(
186-        """SELECT DISTINCT i.instrument_id,i.name,i.ticker,i.provider_symbol,i.isin
187-           FROM positions_snapshot p JOIN instruments i ON i.instrument_id=p.instrument_id
188-           WHERE p.account_id=? AND i.is_active=1 ORDER BY i.instrument_id""",
189-        (account_id,),
190-    ).fetchall()
191-    if len(rows) != 22:
192-        raise ValueError("Confirmed PostFinance depot baseline must contain exactly 22 instruments")
193-    return rows
194-
195-
196-def _match_instruments(conn: Connection, bundle: PFBundle, depot_id: str) -> dict[str, str]:
197-    rows = _instrument_rows(conn, depot_id)
198-    by_isin = {str(row["isin"]).upper(): str(row["instrument_id"]) for row in rows if row["isin"]}
199-    aliases: dict[str, set[str]] = defaultdict(set)
200-    for row in rows:
201-        for key in (row["name"], row["ticker"], row["provider_symbol"]):
202-            if key:
203-                aliases[normalize_label(str(key))].add(str(row["instrument_id"]))
204-    mapping: dict[str, str] = {}
205-    used: set[str] = set()
206-    for position in bundle.snapshot.positions:
207-        exact = aliases.get(position.normalized_label, set())
208-        candidates = set(exact)
209-        if not candidates:
210-            tokens = set(position.normalized_label.split())
211-            for alias, ids in aliases.items():
212-                other = set(alias.split())
213-                if (
214-                    tokens
215-                    and other
216-                    and (
217-                        tokens <= other
218-                        or other <= tokens
219-                        or len(tokens & other) >= min(2, len(tokens), len(other))
220-                    )
221-                ):
222-                    candidates.update(ids)
223-        candidates -= used
224-        if len(candidates) != 1:
225-            raise ValueError(
226-                "PostFinance snapshot position cannot be mapped unambiguously to the confirmed depot baseline"
227-            )
228-        instrument_id = next(iter(candidates))
229-        mapping[position.row_reference] = instrument_id
230-        used.add(instrument_id)
231-    if len(mapping) != 22 or len(used) != 22:
232-        raise ValueError("PostFinance snapshot-to-depot mapping is incomplete")
233-    # Trade and income ISINs independently verify that no document points to another instrument.
234-    for event in bundle.events:
235-        if event.isin and event.isin.upper() not in by_isin:
236-            raise ValueError(
237-                "PostFinance ledger document references an instrument outside the confirmed depot"
238-            )
239-    return mapping


sed -n '600,700p' src/jarvis_finance/imports/postfinance_documents.py && grep -R "source_value_chf" -n src/jarvis_finance