Output:
Chunk ID: abf7b8
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1720
Output:
            positions.append(
                PFSnapshotPosition(
                    _hash("snapshot-row", len(positions) + 1, normalize_label(label))[:32],
                    label,
                    normalize_label(label),
                    section,
                    quantity,
                    average,
                    provider_total,
                    price,
                    currency,
                    value,
                    weight,
                )
            )
            continue
        if stripped.startswith("Gesamt CHF") and not in_positions:
            values = [
                _number(value, required=True)
                for value in re.findall(r"[+\-−]?[0-9][0-9'’.,]*", stripped)
            ]
            if len(values) >= 3:
                cash_total, securities_total, total = values[0], values[1], values[2]
            continue
        if stripped.startswith("Zwischensumme Aktien in CHF"):
            values = [
                _number(value, required=True)
                for value in re.findall(r"[+\-−]?[0-9][0-9'’.,]*", stripped)
            ]
            if len(values) >= 3:
                stock_total = values[2]
            continue
        if stripped.startswith("Zwischensumme ETFs in CHF"):
            values = [
                _number(value, required=True)
                for value in re.findall(r"[+\-−]?[0-9][0-9'’.,]*", stripped)
            ]
            if len(values) >= 3:
                etf_total = values[2]
            continue
        m = re.search(r"Offene Aufträge\s+([0-9]+)", stripped)
        if m:
            open_orders = int(m.group(1))
        m = re.search(r"Wert der Wertschriften\s+([0-9][0-9'’.,]*(?: [0-9]{3})*)", stripped)
        if m:
            securities_total = _number(m.group(1), required=True)
        m = re.search(r"Barguthaben\s+([0-9][0-9'’.,]*(?: [0-9]{3})*)", stripped)
        if m:
            cash_total = _number(m.group(1), required=True)
        m = re.search(r"Totalwert\s+([0-9][0-9'’.,]*(?: [0-9]{3})*)", stripped)
        if m and total is None:
            total = _number(m.group(1), required=True)
        m = re.search(r"Zwischensumme Aktien in CHF\s+([0-9][0-9'’.,]*(?: [0-9]{3})*)", stripped)
        if m:
            stock_total = _number(m.group(1), required=True)
        m = re.search(r"Zwischensumme ETFs in CHF\s+([0-9][0-9'’.,]*(?: [0-9]{3})*)", stripped)
        if m:
            etf_total = _number(m.group(1), required=True)
    if len(positions) != 22 or len(cash) != 2:
        raise ValueError(
            "PostFinance account overview does not contain the expected complete snapshot"
        )
    if None in (cash_total, securities_total, total, stock_total, etf_total, open_orders):
        raise ValueError("PostFinance account overview lacks required totals")
    assert (
        cash_total is not None
        and securities_total is not None
        and total is not None
        and stock_total is not None
        and etf_total is not None
        and open_orders is not None
    )
    if abs(sum((p.market_value_chf for p in positions), Decimal("0")) - securities_total) > Decimal(
        "0.05"
    ):
        raise ValueError("PostFinance position sum does not reconcile")
    if abs(securities_total + cash_total - total) > Decimal("0.05"):
        raise ValueError("PostFinance account total does not reconcile")
    if abs(stock_total + etf_total - securities_total) > Decimal("0.05"):
        raise ValueError("PostFinance asset-class totals do not reconcile")
    cash = _reconcile_cash_components(cash, cash_total)
    return PFSnapshot(
        sha256(raw).hexdigest(),
        page_count,
        valuation_at,
        tuple(positions),
        tuple(cash),
        securities_total,
        cash_total,
        total,
        stock_total,
        etf_total,
        open_orders,
    )

def parse_postfinance_bundle(zip_raw: bytes, overview_raw: bytes) -> PFBundle:
    if not zip_raw or len(zip_raw) > MAX_ZIP_BYTES:
        raise ValueError("PostFinance ZIP is empty or too large")
    documents: list[PFDocument] = []
    try:
grep: src/jarvis_finance/imports/__pycache__/postfinance_documents.cpython-311.pyc: binary file matches
grep: src/jarvis_finance/imports/__pycache__/truewealth_tax_statement.cpython-311.pyc: binary file matches
grep: src/jarvis_finance/imports/__pycache__/postfinance_documents.cpython-312.pyc: binary file matches
grep: src/jarvis_finance/storage/__pycache__/migrations.cpython-311.pyc: binary file matches
src/jarvis_finance/imports/postfinance_documents.py:91:    source_value_chf: Decimal
src/jarvis_finance/imports/postfinance_documents.py:143:        replace(item, source_value_chf=value)
src/jarvis_finance/imports/truewealth_tax_statement.py:34:    source_value_chf: Decimal
src/jarvis_finance/imports/truewealth_tax_statement.py:59:        return sum((row.source_value_chf for row in self.positions), Decimal("0"))
src/jarvis_finance/imports/truewealth_tax_statement.py:182:                    source_value_chf=_decimal(position.group(7)),
src/jarvis_finance/imports/truewealth_tax_statement.py:194:    difference = source_total - (sum((row.source_value_chf for row in positions), Decimal("0")) + sum((row.value_chf for row in cash), Decimal("0")))
grep: src/jarvis_finance/services/__pycache__/postfinance_service.cpython-311.pyc: binary file matches
src/jarvis_finance/storage/migrations.py:2011:            source_value_chf TEXT NOT NULL,
src/jarvis_finance/storage/migrations.py:2028:            source_value_chf TEXT NOT NULL,
grep: src/jarvis_finance/services/__pycache__/truewealth_service.cpython-311.pyc: binary file matches
grep: src/jarvis_finance/services/__pycache__/postfinance_service.cpython-312.pyc: binary file matches
src/jarvis_finance/services/postfinance_service.py:804:                    _money(cash.source_value_chf),
src/jarvis_finance/services/postfinance_service.py:817:                    _money(cash.source_value_chf),
src/jarvis_finance/services/truewealth_service.py:142:                "source_value_chf": _decimal_text(row.source_value_chf),
src/jarvis_finance/services/truewealth_service.py:151:                "source_value_chf": _decimal_text(row.value_chf),
src/jarvis_finance/services/truewealth_service.py:359:                     price_currency,source_price,source_value_chf,source_evidence_json,created_at)
src/jarvis_finance/services/truewealth_service.py:370:                    _decimal_text(row.source_value_chf),
src/jarvis_finance/services/truewealth_service.py:379:                     source_value_chf,source_evidence_json,created_at)
src/jarvis_finance/services/truewealth_service.py:594:                      source_price,source_value_chf
src/jarvis_finance/services/truewealth_service.py:599:            """SELECT snapshot_cash_id,currency,amount_original,fx_rate_to_chf,source_value_chf


sed -n '70,100p' src/jarvis_finance/imports/postfinance_documents.py && sed -n '780,825p' src/jarvis_finance/services/postfinance_service.py


Output:
Chunk ID: 04ea1f
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 762
Output:

@dataclass(frozen=True)
class PFSnapshotPosition:
    row_reference: str
    source_label: str
    normalized_label: str
    asset_class: str
    quantity: Decimal
    provider_average_cost_original: Decimal | None
    provider_cost_total_original: Decimal | None
    market_price_original: Decimal
    price_currency: str
    market_value_chf: Decimal
    weight_pct: Decimal | None

@dataclass(frozen=True)
class PFSnapshotCash:
    currency: str
    amount_original: Decimal
    fx_rate_to_chf: Decimal
    source_value_chf: Decimal

@dataclass(frozen=True)
class PFSnapshot:
    source_hash: str
    page_count: int
    valuation_at: str
    positions: tuple[PFSnapshotPosition, ...]
    cash: tuple[PFSnapshotCash, ...]
                    _text(position.provider_average_cost_original),
                    _text(position.provider_cost_total_original),
                    _text(position.market_price_original),
                    _text(position.quantity * position.market_price_original),
                    None,
                    _money(position.market_value_chf),
                    _text(position.weight_pct),
                    position.asset_class,
                    "verified",
                    "postfinance_official_import",
                    snapshot_id,
                    now,
                    accounts["etrading_depot"],
                ),
            )
        for cash in snapshot.cash:
            conn.execute(
                "INSERT INTO postfinance_snapshot_cash(snapshot_cash_id,snapshot_id,currency,amount_original,fx_rate_to_chf,value_chf,created_at) VALUES(?,?,?,?,?,?,?)",
                (
                    _id("pfcash"),
                    snapshot_id,
                    cash.currency,
                    _text(cash.amount_original),
                    _text(cash.fx_rate_to_chf),
                    _money(cash.source_value_chf),
                    now,
                ),
            )
            conn.execute(
                "INSERT INTO cash_balances(cash_balance_id,account_id,balance_date,currency,amount_original,fx_rate_to_chf,amount_chf,source_type,quality_status,notes,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?)",
                (
                    _id("cash"),
                    accounts["etrading_cash"],
                    snapshot.valuation_at[:10],
                    cash.currency,
                    _text(cash.amount_original),
                    _text(cash.fx_rate_to_chf),
                    _money(cash.source_value_chf),
                    "postfinance_official_import",
                    "verified",
                    "Official PostFinance account overview",
                    now,
                ),
            )
        conn.execute(
            "INSERT INTO account_value_snapshots(snapshot_id,account_id,valuation_date,total_value_chf,currency,source_type,quality_status,notes,created_at,valuation_at,source_reference,is_active) VALUES(?,?,?,?,'CHF','postfinance_official_import','ok','Official PostFinance account overview',?,?,?,1)",


grep -R "\"quantity\".*positions" -n src/jarvis_finance/services src/jarvis_finance/market_data | head -20 && grep -R "summary_json" -n src/jarvis_finance/services/portfolio_analytics.py | head -20


Output:
Chunk ID: 644577
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 45
Output:
733:                fx_coverage_pct,benchmark_coverage_pct,quality_status,reason_codes_json,summary_json,created_at)
946:    summary = json.loads(latest["summary_json"] or "{}")


sed -n '640,740p' src/jarvis_finance/services/portfolio_analytics.py && sed -n '560,640p' src/jarvis_finance/services/portfolio_analytics.py


Output:
Chunk ID: a6639b
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 2945
Output:
                quote = None
            benchmark_quote_date = _quote_date(quote, effective) if quote else effective
            expected_benchmark_currency = str(benchmark_mapping["currency"] or "").upper()
            if quote and benchmark_quote_date > effective:
                reasons.add("benchmark_future_price")
                quote = None
            elif quote and _business_day_age(benchmark_quote_date, effective) > 2:
                reasons.add("benchmark_stale_price")
                quote = None
            elif quote and expected_benchmark_currency and str(quote.currency or "").upper() != expected_benchmark_currency:
                reasons.add("benchmark_currency_mismatch")
                quote = None
            elif quote and not exchange_matches(str(benchmark_mapping["provider_market"] or ""), quote.provider_market):
                reasons.add("benchmark_exchange_mismatch")
                quote = None
            if not quote or quote.close is None or quote.close <= ZERO:
                reasons.add("benchmark_quote_missing")
            elif quote.currency.upper() not in rates:
                benchmark_currency = quote.currency.upper()
                try:
                    benchmark_rate, benchmark_rate_as_of = _fetch_fx_rate(fx, benchmark_currency, effective)
                except Exception:
                    benchmark_rate = None
                    benchmark_rate_as_of = None
                if benchmark_rate is None or benchmark_rate_as_of is None:
                    reasons.add("benchmark_fx_missing")
                else:
                    rates[benchmark_currency] = benchmark_rate
                    rate_dates[benchmark_currency] = benchmark_rate_as_of
                    if benchmark_currency not in currencies:
                        currencies.append(benchmark_currency)
                    existing_benchmark_rate = conn.execute(
                        """SELECT rate FROM fx_rates
                            WHERE base_currency=? AND quote_currency='CHF' AND rate_date=?
                              AND provider=? AND rate_type='close'""",
                        (
                            benchmark_currency,
                            benchmark_rate_as_of.isoformat(),
                            "identity" if benchmark_currency == "CHF" else getattr(fx, "name", "fx_provider"),
                        ),
                    ).fetchone()
                    if not existing_benchmark_rate or _decimal(existing_benchmark_rate["rate"]) != benchmark_rate:
                        upsert_fx_rate(
                            conn, base_currency=benchmark_currency, quote_currency="CHF", rate_date=benchmark_rate_as_of.isoformat(),
                            rate=benchmark_rate, provider="identity" if benchmark_currency == "CHF" else getattr(fx, "name", "fx_provider"),
                            rate_type="close", quality_status="fresh", fetched_at=now, run_id=run_id,
                        )
                        fx_writes += 1
            if quote and quote.close is not None and quote.close > ZERO and quote.currency.upper() in rates:
                return_type = "etf_proxy" if benchmark_mapping["asset_class"] == "etf" else ("total_return" if quote.adjusted_close is not None else "price_return")
                if return_type == "total_return":
                    assert quote.adjusted_close is not None
                    benchmark_level = quote.adjusted_close
                else:
                    benchmark_level = quote.close
                benchmark_value = benchmark_level * rates[quote.currency.upper()]
                conn.execute(
                    """INSERT OR REPLACE INTO benchmark_snapshots(
                        benchmark_snapshot_id,run_id,policy_id,benchmark_reference,provider,provider_symbol,
                        price_currency,close,adjusted_close,fx_rate_to_chf,value_chf,return_type,as_of,source_as_of,fetched_at,
                        quality_status,reason_codes_json) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
                    (stable_id("benchmark", run_id, policy["policy_id"], benchmark_mapping["reference"]), run_id,
                     policy["policy_id"], benchmark_mapping["reference"], quote.provider,
                     benchmark_mapping["provider_symbol"], quote.currency.upper(), _fmt(quote.close),
                     _fmt(quote.adjusted_close), _fmt(rates[quote.currency.upper()]), _fmt(benchmark_value),
                     return_type, effective.isoformat(), benchmark_quote_date.isoformat(), now, "fresh", "[]"),
                )
                benchmark_stored = 1

        price_total = len(positions)
        price_stored = price_writes
        fx_total = len(currencies)
        fx_stored = fx_writes
        current_prices = sum(1 for _, _, quote_as_of, _ in quotes.values() if 0 <= _business_day_age(quote_as_of, effective) <= 2)
        price_coverage = _pct(current_prices, price_total)
        fx_covered_positions = sum(
            1 for _, quote, _, _ in quotes.values()
            if quote.currency.upper() in rates and 0 <= _business_day_age(rate_dates[quote.currency.upper()], effective) <= 2
        )
        fx_coverage = _pct(fx_covered_positions, price_total)
        benchmark_coverage = _pct(benchmark_stored, benchmark_total) if benchmark_total else ZERO
        if price_total == 0:
            quality = "unavailable"
            reasons.add("no_confirmed_positions")
        elif price_coverage == HUNDRED and fx_coverage == HUNDRED and (benchmark_total == 0 or benchmark_coverage == HUNDRED):
            quality = "complete"
        else:
            quality = "partial"
        risk = _risk_summary(conn, values, account_values, missing)
        analysis_id = stable_id("portfolio-analysis", run_id)
        conn.execute(
            """INSERT OR REPLACE INTO portfolio_analysis_snapshots(
                analysis_snapshot_id,run_id,as_of,base_currency,total_value_chf,price_coverage_pct,
                fx_coverage_pct,benchmark_coverage_pct,quality_status,reason_codes_json,summary_json,created_at)
                VALUES(?,?,?,?,?,?,?,?,?,?,?,?)""",
            (analysis_id, run_id, effective.isoformat(), "CHF", _fmt(sum(account_values.values(), ZERO)),
             _fmt(price_coverage), _fmt(fx_coverage), _fmt(benchmark_coverage), quality,
             json.dumps(sorted(reasons)), json.dumps({"positions": values, "risk": risk}, sort_keys=True), now),
        )
        audit_id = record_audit_event(
            conn, source=SOURCE_KEY, action="daily_market_valuation_completed", entity_type="market_data_run",
            rate = rates.get(quote.currency.upper())
            close = quote.close
            if rate is None or close is None:
                missing.append({"instrument_id": instrument_id, "reason_code": "fx_rate_missing"})
                account_missing.add(position.account_id)
                continue
            value_original = position.quantity * close
            value_chf = value_original * rate
            account_values[position.account_id] += value_chf
            values.append({
                "account_id": position.account_id, "instrument_id": instrument_id, "isin": position.isin,
                "name": position.name, "asset_class": position.asset_class,
                "currency": quote.currency.upper(), "quantity": _fmt(position.quantity),
                "close": _fmt(close), "fx_rate_to_chf": _fmt(rate), "value_chf": _fmt(value_chf),
                "as_of": quote_as_of.isoformat(), "quality_status": "fresh" if 0 <= _business_day_age(quote_as_of, effective) <= 2 else "stale",
                "provider": quote.provider, "provider_symbol": quote.provider_symbol,
                "provider_market": quote.provider_market,
                **({"price_input_provenance": cached_price_inputs[instrument_id]} if instrument_id in cached_price_inputs else {}),
            })
        missing_ids = {item["instrument_id"] for item in missing}
        for position in positions:
            if position.instrument_id in missing_ids:
                account_missing.add(position.account_id)
        for account_id, amount in _latest_cash_by_account(conn).items():
            account_values[account_id] += amount
        for item in missing:
            position = position_details.get(item["instrument_id"])
            if position:
                item["label"] = position.name
                item["isin"] = position.isin or ""

        valuation_stored = 0
        for item in values:
            version = int(conn.execute(
                "SELECT COALESCE(MAX(snapshot_version),0)+1 FROM portfolio_valuation_snapshots WHERE scope_kind='instrument' AND scope_id=? AND valuation_at=?",
                (item["instrument_id"], effective.isoformat()),
            ).fetchone()[0])
            value_original = _decimal(item["quantity"]) * _decimal(item["close"])
            is_stale = item["quality_status"] == "stale"
            conn.execute(
                """INSERT OR REPLACE INTO portfolio_valuation_snapshots(
                    snapshot_id,scope_kind,scope_id,account_id,value_original,currency,base_currency,
                    fx_rate_to_base,fx_direction,valuation_at,source,captured_at,snapshot_version,
                    source_reference,quality_status,reason_codes_json)
                    VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
                (stable_id("instrument-valuation", run_id, item["account_id"], item["instrument_id"]),
                 "instrument", item["instrument_id"], item["account_id"], _fmt(value_original), item["currency"],
                 "CHF", item["fx_rate_to_chf"], "original_to_base", effective.isoformat(), SOURCE_KEY,
                 now, version, run_id, "partial" if is_stale else "complete",
                 json.dumps(["stale_price"] if is_stale else [])),
            )
            valuation_stored += 1
        for account_id, total in sorted(account_values.items()):
            version = int(conn.execute(
                "SELECT COALESCE(MAX(snapshot_version),0)+1 FROM portfolio_valuation_snapshots WHERE scope_kind='account' AND scope_id=? AND valuation_at=?",
                (account_id, effective.isoformat()),
            ).fetchone()[0])
            conn.execute(
                """INSERT OR REPLACE INTO portfolio_valuation_snapshots(
                    snapshot_id,scope_kind,scope_id,account_id,value_original,currency,base_currency,
                    fx_rate_to_base,fx_direction,valuation_at,source,captured_at,snapshot_version,
                    source_reference,quality_status,reason_codes_json)
                    VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
                (stable_id("portfolio-valuation", run_id, account_id), "account", account_id, account_id,
                 _fmt(total), "CHF", "CHF", "1", "original_to_base", effective.isoformat(), SOURCE_KEY,
                 now, version, run_id, "partial" if account_id in account_missing else "complete",
                 json.dumps(["missing_instrument"] if account_id in account_missing else [])),
            )
            valuation_stored += 1

        benchmark_mapping, benchmark_issue = _benchmark_mapping(conn, policy)
        benchmark_total = 1 if policy and policy.get("benchmarks") else 0
        benchmark_stored = 0
        if benchmark_issue:
            reasons.add(benchmark_issue)
        elif benchmark_mapping and policy:
            try:
                provider = providers.get(benchmark_mapping["provider"]) or equity_price_provider_by_name(benchmark_mapping["provider"])
                quote = provider.get_price(benchmark_mapping["provider_symbol"], price_date=effective.isoformat())
            except Exception:
                quote = None


python - <<'PY'
from decimal import Decimal
from jarvis_finance.storage.database import connect_memory
from jarvis_finance.storage.migrations import apply_migrations
from jarvis_finance.services.cash_service import get_cash_summary
conn=connect_memory(); apply_migrations(conn)
conn.execute("INSERT INTO platforms(platform_id,name,platform_type,default_currency,is_active,created_at) VALUES('p','Bank','bank','CHF',1,'now')")
conn.execute("INSERT INTO accounts(account_id,platform_id,account_name,account_type,currency,balance_mode,performance_included,is_active,created_at) VALUES('a','p','Multi','cash','CHF','manual',1,1,'now')")
conn.execute("INSERT INTO cash_account_snapshots(snapshot_id,account_id,snapshot_type,balance_date,amount_original,currency,amount_chf,source,created_at,created_by) VALUES('s','a','manual_balance','2026-01-01','1000','CHF','1000','manual','now','u')")
conn.execute("INSERT INTO transactions(transaction_id,transaction_type,account_id,trade_date,gross_amount_original,net_amount_original,currency_original,fx_rate_to_chf,source_type,is_confirmed,quality_status,created_at) VALUES('t','cash_deposit','a','2026-01-02','100','100','USD','0.9','csv_import',1,'ok','now')")
conn.commit()
s=get_cash_summary(conn)
print(s.cash_chf, [(p.id,p.used_value_chf,p.amount,p.manual_balance_chf,p.calculated_balance_chf) for p in s.positions])
PY


Output:
Chunk ID: eed05d
Wall time: 0.1984 seconds
Process exited with code 0
Original token count: 107
Output:
1000.00 [('acct_bf05117303424e69:CHF', '0.00', '0.00', None, '0.00'), ('acct_60e4e9c758f14115:CHF', '0.00', '0.00', None, '0.00'), ('acct_cb9d82240d0d417f:CHF', '0.00', '0.00', None, '0.00'), ('acct_312d67487d354ce3:CHF', '0.00', '0.00', None, '0.00'), ('a:CHF', '1000.00', '1000.00', '1000.00', '0.00'), ('acct_cf29bc626ec04273:CHF', '0.00', '0.00', None, '0.00'), ('acct_2146c5a6ab694e5e:CHF', '0.00', '0.00', None, '0.00')]


python - <<'PY'
from jarvis_finance.storage.database import connect_memory
from jarvis_finance.storage.migrations import apply_migrations
from jarvis_finance.ledger.cash import calculate_cash_balances
conn=connect_memory(); apply_migrations(conn)
conn.execute("INSERT INTO platforms(platform_id,name,platform_type,default_currency,is_active,created_at) VALUES('p','Bank','bank','CHF',1,'now')")
conn.execute("INSERT INTO accounts(account_id,platform_id,account_name,account_type,currency,balance_mode,performance_included,is_active,created_at) VALUES('a','p','Multi','cash','CHF','manual',1,1,'now')")
conn.execute("INSERT INTO transactions(transaction_id,transaction_type,account_id,trade_date,gross_amount_original,net_amount_original,currency_original,fx_rate_to_chf,source_type,is_confirmed,quality_status,created_at) VALUES('t','cash_deposit','a','2026-01-02','100','100','USD','0.9','csv_import',1,'ok','now')")
print(calculate_cash_balances(conn,persist_alerts=False).balances)
PY


Output:
Chunk ID: 4368c4
Wall time: 0.0000 seconds
Process exited with code 1
Original token count: 41
Output:
Traceback (most recent call last):
  File "<stdin>", line 8, in <module>
TypeError: calculate_cash_balances() got an unexpected keyword argument 'persist_alerts'


PYTHONPATH=src python - <<'PY'
from jarvis_finance.storage.database import connect_memory
from jarvis_finance.storage.migrations import apply_migrations
from jarvis_finance.ledger.cash import calculate_cash_balances
from jarvis_finance.services.cash_service import get_cash_summary
conn=connect_memory(); apply_migrations(conn)
conn.execute("INSERT INTO platforms(platform_id,name,platform_type,default_currency,is_active,created_at) VALUES('p','Bank','bank','CHF',1,'now')")
conn.execute("INSERT INTO accounts(account_id,platform_id,account_name,account_type,currency,balance_mode,performance_included,is_active,created_at) VALUES('a','p','Multi','cash','CHF','manual',1,1,'now')")
conn.execute("INSERT INTO cash_account_snapshots(snapshot_id,account_id,snapshot_type,balance_date,amount_original,currency,amount_chf,source,created_at,created_by) VALUES('s','a','manual_balance','2026-01-01','1000','CHF','1000','manual','now','u')")
conn.execute("INSERT INTO transactions(transaction_id,transaction_type,account_id,trade_date,gross_amount_original,net_amount_original,currency_original,fx_rate_to_chf,source_type,is_confirmed,quality_status,created_at) VALUES('t','cash_deposit','a','2026-01-02','100','100','USD','0.9','csv_import',1,'ok','now')")
conn.commit()
print(calculate_cash_balances(conn,persist_alerts=False).balances)
s=get_cash_summary(conn)
print(s.cash_chf, [(p.id,p.used_value_chf,p.amount,p.manual_balance_chf,p.calculated_balance_chf) for p in s.positions if p.account_id=='a'])
PY


Output:
Chunk ID: 103114
Wall time: 0.2214 seconds
Process exited with code 0
Original token count: 62
Output:
{('a', 'USD'): CashBalance(account_id='a', currency='USD', amount_original=Decimal('100'), amount_chf=Decimal('90.0'), quality_status='ok')}
1090.00 [('a:CHF', '1000.00', '90.00', '1000.00', '90.00'), ('a:USD', '90.00', '100.00', None, '90.00')]


[P2] Avoid double-counting manual multi-currency cash accounts

When a cash account has a manual balance or CSV anchor and also ledger activity in another currency, this adds the account's default-currency row in addition to all ledger-currency rows. The default row can use the account-level manual/anchor CHF amount (or `_calculated_balance`, which sums movements across currencies), while the foreign-currency rows still contribute their CHF values, so `get_cash_summary()` reports the manual/anchored account total plus the ledger balances instead of the intended single account total.

The new cash read model can overstate cash totals for manual or anchored multi-currency accounts by adding ledger currency rows on top of the account-level snapshot. This affects summary totals and downstream portfolio aggregation.