# JARVIS Finance System – Spezifikation v0.4 Implementation Blueprint

**Version:** 0.4  
**Datum:** 2026-05-14  
**Status:** Implementation Blueprint / kein Code  
**Scope:** MVP 1 technische Umsetzungsgrundlage  
**Basiswährung:** CHF  
**Primäre Technologie MVP:** SQLite + Python + Streamlit  
**Leitlinie:** Finanzdaten bleiben privat. Git enthält Code, Dokumentation und synthetische Testdaten – niemals echte Finanzdaten.

---

## 1. Ziel von v0.4

v0.4 ist keine Funktionssammlung mehr, sondern die konkrete technische Baugrundlage für MVP 1.

Nach v0.4 muss klar sein:

- welche SQLite-Tabellen existieren;
- welche Felder, Datentypen, Pflichtfelder, Fremdschlüssel, Indizes und Validierungen gelten;
- wie Transaktionen zu Positionen, Cash, Crypto-Beständen und Reports werden;
- wie CHF- und FX-Logik funktioniert;
- welche CSV-Templates importiert werden können;
- welche Streamlit-Seiten gebaut werden;
- wie Crypto-PDF-Reports aufgebaut sind;
- wie mit Dummy-Daten getestet wird;
- wie Git, Datenschutz und Backup geregelt sind;
- wie Telegram-Erfassung vorbereitet, aber nicht priorisiert wird.

MVP 1 bleibt: **Ledger zuerst, Dashboard danach, Meinung später.**

---

## 2. Technische Grundentscheidungen MVP 1

### 2.1 Stack

- Sprache: Python.
- Dashboard: Streamlit.
- Datenbank: SQLite.
- DB-Zugriff: SQLAlchemy oder sqlite-utils/Repository-Schicht; später PostgreSQL-fähig halten.
- Tabellen-/Analyseverarbeitung: Pandas.
- Charts: Plotly.
- PDF Reports: HTML/Markdown Template → PDF Renderer, z.B. WeasyPrint oder Playwright.
- Tests: pytest.
- Dummy-Daten: synthetisch unter `examples/` oder `tests/fixtures/`.

### 2.2 Architekturprinzip

MVP 1 wird modular aufgebaut:

- `storage`: SQLite Schema, Migrationen, Repositories.
- `ledger`: Transaktionslogik, Positionen, Cash, FX.
- `crypto`: Wallets, Holdings, Transfers, CoinGecko-Preise.
- `imports`: CSV-Import/Export, Import-Sessions, Validierung.
- `dashboard`: Streamlit-Seiten.
- `reports`: PDF/HTML/Markdown Reports.
- `quality`: Datenqualität, Alerts.
- `audit`: History/Audit-Log.
- `tests`: synthetische Testfälle.

---

## 3. SQLite-Datenbankschema MVP 1

### 3.1 Allgemeine Konventionen

Datentypen:

- IDs: `TEXT`, UUID als String.
- Datum: `TEXT`, ISO-Format `YYYY-MM-DD`.
- Timestamp: `TEXT`, ISO-Format `YYYY-MM-DDTHH:MM:SSZ` oder lokal mit Zeitzone.
- Geldbeträge: `NUMERIC`, Speicherung mit Decimal-Logik in Python; SQLite speichert flexibel, Python validiert.
- Mengen: `NUMERIC`, Decimal.
- Booleans: `INTEGER`, 0/1.
- JSON: `TEXT`, JSON-String.

Standardfelder, wo sinnvoll:

- `created_at TEXT NOT NULL`
- `updated_at TEXT`
- `notes TEXT`

Validierung erfolgt zweistufig:

1. DB Constraints für Grundregeln.
2. Python-Service-Validierung für fachliche Regeln.

---

## 4. Tabellen im Detail

## 4.1 `platforms`

**Zweck:** Abbildung von Banken, Brokern, Wallet-Anbietern und aggregierten Plattformen.

**Felder:**

- `platform_id TEXT PRIMARY KEY` – Pflicht.
- `name TEXT NOT NULL UNIQUE` – z.B. Raiffeisen, PostFinance, True Wealth, Crypto.
- `platform_type TEXT NOT NULL` – bank, broker, robo_advisor, crypto_exchange, crypto_wallet, manual, other.
- `country TEXT` – optional.
- `default_currency TEXT NOT NULL DEFAULT 'CHF'`.
- `is_active INTEGER NOT NULL DEFAULT 1`.
- `notes TEXT`.
- `created_at TEXT NOT NULL`.
- `updated_at TEXT`.

**Indizes:**

- unique index auf `name`.
- index auf `platform_type`.

**Validierung:**

- `platform_type` nur erlaubte Werte.
- `default_currency` ISO 4217, z.B. CHF/EUR/USD.

**Dummy-Beispiel:**

```text
platform_id=plt_postfinance_demo, name=PostFinance, platform_type=broker, country=CH, default_currency=CHF, is_active=1
```

---

## 4.2 `accounts`

**Zweck:** Konkrete Konten/Depots innerhalb einer Plattform.

**Felder:**

- `account_id TEXT PRIMARY KEY`.
- `platform_id TEXT NOT NULL REFERENCES platforms(platform_id)`.
- `account_name TEXT NOT NULL`.
- `account_type TEXT NOT NULL` – cash, brokerage, robo_portfolio, crypto, reserve, other.
- `currency TEXT NOT NULL DEFAULT 'CHF'`.
- `performance_included INTEGER NOT NULL DEFAULT 1`.
- `is_health_reserve INTEGER NOT NULL DEFAULT 0`.
- `target_cash_min_chf NUMERIC`.
- `target_cash_max_chf NUMERIC`.
- `is_active INTEGER NOT NULL DEFAULT 1`.
- `notes TEXT`.
- `created_at TEXT NOT NULL`.
- `updated_at TEXT`.

**Indizes:**

- `idx_accounts_platform_id` auf `platform_id`.
- unique index auf `(platform_id, account_name)`.

**Validierung:**

- gleicher Account-Name darf je Plattform nicht doppelt sein.
- Reserve-Konto kann aus Performance ausgeschlossen werden.

**Dummy-Beispiel:**

```text
account_id=acc_pf_demo_depot, platform_id=plt_postfinance_demo, account_name=Demo Depot, account_type=brokerage, currency=CHF
```

---

## 4.3 `instruments`

**Zweck:** Stammdaten für Aktien, ETFs, Cash-ähnliche Instrumente und andere Assets ausser detaillierten Crypto-Holdings.

**Felder:**

- `instrument_id TEXT PRIMARY KEY`.
- `asset_class TEXT NOT NULL` – equity, etf, crypto, cash, bond, commodity, other.
- `name TEXT NOT NULL`.
- `ticker TEXT`.
- `isin TEXT`.
- `exchange TEXT`.
- `currency TEXT NOT NULL`.
- `country TEXT`.
- `sector TEXT`.
- `industry TEXT`.
- `provider_symbol TEXT`.
- `data_provider_primary TEXT`.
- `data_provider_fallback TEXT`.
- `is_active INTEGER NOT NULL DEFAULT 1`.
- `notes TEXT`.
- `created_at TEXT NOT NULL`.
- `updated_at TEXT`.

**Indizes:**

- index auf `asset_class`.
- index auf `ticker`.
- index auf `isin`.
- unique partial logic in Python: ISIN eindeutig, wenn vorhanden.

**Validierung:**

- Aktien/ETFs sollten ISIN oder Ticker haben.
- Währung Pflicht.

**Dummy-Beispiel:**

```text
instrument_id=ins_demo_stock_1, asset_class=equity, name=Demo Global AG, ticker=DGA, isin=CH0000000001, currency=CHF
```

---

## 4.4 `transactions`

**Zweck:** Haupt-Ledger für Portfolio- und Cash-Transaktionen.

**Felder:**

- `transaction_id TEXT PRIMARY KEY`.
- `transaction_type TEXT NOT NULL` – buy, partial_sell, full_sell, dividend, etf_distribution, fee, tax, cash_deposit, cash_withdrawal, fx_conversion, initial_position_snapshot, initial_cash_snapshot, manual_correction.
- `account_id TEXT NOT NULL REFERENCES accounts(account_id)`.
- `instrument_id TEXT REFERENCES instruments(instrument_id)`.
- `trade_date TEXT NOT NULL`.
- `settlement_date TEXT`.
- `quantity NUMERIC`.
- `price_original NUMERIC`.
- `gross_amount_original NUMERIC`.
- `fee_original NUMERIC DEFAULT 0`.
- `tax_original NUMERIC DEFAULT 0`.
- `net_amount_original NUMERIC`.
- `currency_original TEXT NOT NULL`.
- `fx_rate_to_chf NUMERIC`.
- `fx_source TEXT` – api, manual_override, imported, missing, not_needed.
- `fx_status TEXT NOT NULL DEFAULT 'ok'` – ok, missing, stale, conflict, manual_override, not_needed.
- `gross_amount_chf NUMERIC`.
- `fee_chf NUMERIC`.
- `tax_chf NUMERIC`.
- `net_amount_chf NUMERIC`.
- `source_type TEXT NOT NULL` – manual, csv, broker_export, snapshot, system, telegram_draft.
- `source_id TEXT` – import_session_id oder externe Referenz.
- `external_transaction_id TEXT`.
- `row_hash TEXT`.
- `is_confirmed INTEGER NOT NULL DEFAULT 1`.
- `quality_status TEXT NOT NULL DEFAULT 'ok'` – ok, warning, error, incomplete.
- `notes TEXT`.
- `created_at TEXT NOT NULL`.
- `updated_at TEXT`.

**Indizes:**

- `idx_transactions_account_date` auf `(account_id, trade_date)`.
- `idx_transactions_instrument_date` auf `(instrument_id, trade_date)`.
- `idx_transactions_type` auf `transaction_type`.
- unique index auf `external_transaction_id`, wenn nicht leer.
- unique index auf `row_hash`, wenn nicht leer.

**Validierung:**

- bestätigte Transaktion braucht Audit-Log.
- Fremdwährung braucht `fx_rate_to_chf` oder `fx_status='missing'`.
- Kauf/Verkauf braucht Menge > 0.
- Initial Snapshot muss `transaction_type` entsprechend markieren.
- manuelle Korrektur braucht Notiz.

**Dummy-Beispiel:**

```text
transaction_id=txn_demo_buy_1, transaction_type=buy, account_id=acc_pf_demo_depot, instrument_id=ins_demo_stock_1, trade_date=2026-01-15, quantity=10, price_original=100, currency_original=CHF, gross_amount_original=1000, fee_original=5, net_amount_chf=1005
```

---

## 4.5 `positions_snapshot`

**Zweck:** Berechnete Positionsstände zu einem Zeitpunkt. Kein Primärledger, sondern Ergebnis/Cache.

**Felder:**

- `position_snapshot_id TEXT PRIMARY KEY`.
- `snapshot_date TEXT NOT NULL`.
- `account_id TEXT NOT NULL REFERENCES accounts(account_id)`.
- `platform_id TEXT NOT NULL REFERENCES platforms(platform_id)`.
- `instrument_id TEXT NOT NULL REFERENCES instruments(instrument_id)`.
- `quantity NUMERIC NOT NULL`.
- `average_cost_original NUMERIC`.
- `cost_basis_original NUMERIC`.
- `cost_basis_chf NUMERIC`.
- `market_price_original NUMERIC`.
- `market_value_original NUMERIC`.
- `market_fx_rate_to_chf NUMERIC`.
- `market_value_chf NUMERIC`.
- `unrealized_price_pnl_chf NUMERIC`.
- `unrealized_fx_pnl_chf NUMERIC`.
- `realized_pnl_chf NUMERIC`.
- `income_chf NUMERIC`.
- `fees_chf NUMERIC`.
- `taxes_chf NUMERIC`.
- `total_return_chf NUMERIC`.
- `portfolio_weight_pct NUMERIC`.
- `category TEXT` – Core, Opportunity, Watch, Reserve, Unknown.
- `data_quality_status TEXT NOT NULL DEFAULT 'ok'`.
- `created_at TEXT NOT NULL`.

**Indizes:**

- unique index auf `(snapshot_date, account_id, instrument_id)`.
- index auf `instrument_id`.
- index auf `platform_id`.

**Validierung:**

- Snapshot ist reproduzierbar aus Transaktionen.
- Quantity darf negativ nur mit quality_status error und bestätigter Korrektur sein.

**Dummy-Beispiel:**

```text
position_snapshot_id=pos_snap_demo_1, snapshot_date=2026-02-01, account_id=acc_pf_demo_depot, instrument_id=ins_demo_stock_1, quantity=10, market_value_chf=1050
```

---

## 4.6 `cash_balances`

**Zweck:** Cash-Bestände pro Konto/Währung als Initialstand oder berechneter Snapshot.

**Felder:**

- `cash_balance_id TEXT PRIMARY KEY`.
- `account_id TEXT NOT NULL REFERENCES accounts(account_id)`.
- `balance_date TEXT NOT NULL`.
- `currency TEXT NOT NULL`.
- `amount_original NUMERIC NOT NULL`.
- `fx_rate_to_chf NUMERIC`.
- `amount_chf NUMERIC`.
- `source_type TEXT NOT NULL` – initial_snapshot, calculated, manual, csv.
- `quality_status TEXT NOT NULL DEFAULT 'ok'`.
- `notes TEXT`.
- `created_at TEXT NOT NULL`.

**Indizes:**

- unique index auf `(account_id, balance_date, currency, source_type)`.

**Validierung:**

- CHF-Cash braucht FX 1 oder not_needed.
- Initial Cash muss als Snapshot markiert sein.

**Dummy-Beispiel:**

```text
cash_balance_id=cash_demo_1, account_id=acc_raiff_demo_cash, balance_date=2026-01-01, currency=CHF, amount_original=10000, amount_chf=10000, source_type=initial_snapshot
```

---

## 4.7 `fx_rates`

**Zweck:** Historische und aktuelle FX-Kurse zu CHF.

**Felder:**

- `fx_rate_id TEXT PRIMARY KEY`.
- `base_currency TEXT NOT NULL`.
- `quote_currency TEXT NOT NULL DEFAULT 'CHF'`.
- `rate_date TEXT NOT NULL`.
- `rate_timestamp TEXT`.
- `rate NUMERIC NOT NULL`.
- `provider TEXT NOT NULL`.
- `rate_type TEXT NOT NULL` – historical, latest, manual_override, imported.
- `quality_status TEXT NOT NULL DEFAULT 'ok'` – ok, missing, stale, conflict, manual.
- `created_at TEXT NOT NULL`.

**Indizes:**

- unique index auf `(base_currency, quote_currency, rate_date, provider, rate_type)`.
- index auf `(base_currency, rate_date)`.

**Validierung:**

- Rate > 0.
- CHF/CHF = 1.
- Manual Override muss Audit-Log haben.

**Dummy-Beispiel:**

```text
fx_rate_id=fx_demo_usd_chf_20260115, base_currency=USD, quote_currency=CHF, rate_date=2026-01-15, rate=0.9000, provider=dummy, rate_type=historical
```

---

## 4.8 `market_prices`

**Zweck:** Preise für Aktien/ETFs/Instrumente.

**Felder:**

- `market_price_id TEXT PRIMARY KEY`.
- `instrument_id TEXT NOT NULL REFERENCES instruments(instrument_id)`.
- `price_date TEXT NOT NULL`.
- `price_timestamp TEXT`.
- `open NUMERIC`.
- `high NUMERIC`.
- `low NUMERIC`.
- `close NUMERIC NOT NULL`.
- `adjusted_close NUMERIC`.
- `currency TEXT NOT NULL`.
- `provider TEXT NOT NULL`.
- `provider_symbol TEXT`.
- `quality_status TEXT NOT NULL DEFAULT 'ok'` – ok, stale, missing, conflict.
- `created_at TEXT NOT NULL`.

**Indizes:**

- unique index auf `(instrument_id, price_date, provider)`.
- index auf `price_date`.

**Validierung:**

- Close > 0.
- Currency muss zur Instrument-Währung passen oder bewusst markiert werden.

**Dummy-Beispiel:**

```text
market_price_id=mp_demo_1, instrument_id=ins_demo_stock_1, price_date=2026-01-16, close=105, currency=CHF, provider=dummy
```

---

## 4.9 `crypto_wallets`

**Zweck:** Wallets, Exchanges, Broker-/Bank-Crypto-Konten.

**Initiale Wallet-/Plattform-Struktur:**

- PostFinance Crypto
- Ledger
- MetaMask
- Kraken
- Binance
- Coinbase
- Sonstige Wallet
- Sonstige Exchange

**Felder:**

- `wallet_id TEXT PRIMARY KEY`.
- `wallet_name TEXT NOT NULL UNIQUE`.
- `wallet_type TEXT NOT NULL` – Hardware Wallet, Software Wallet, Exchange, Bank/Broker, DeFi, Sonstiges.
- `platform_provider TEXT` – Ledger, MetaMask, Binance, PostFinance, Kraken, Coinbase.
- `network_chain TEXT` – optional.
- `wallet_address TEXT` – optional, standardmässig leer; MVP nicht erforderlich.
- `owner TEXT` – optional.
- `is_active INTEGER NOT NULL DEFAULT 1`.
- `last_verified_at TEXT`.
- `notes TEXT`.
- `created_at TEXT NOT NULL`.
- `updated_at TEXT`.

**Indizes:**

- unique index auf `wallet_name`.
- index auf `wallet_type`.
- index auf `platform_provider`.

**Validierung:**

- gleicher Wallet-Name darf nicht doppelt sein.
- Wallet-Adresse ist nie Pflicht.
- Inactive Wallet bleibt historisch erhalten.

**Dummy-Beispiel:**

```text
wallet_id=wal_demo_ledger, wallet_name=Ledger Demo, wallet_type=Hardware Wallet, platform_provider=Ledger, network_chain=Multiple
```

---

## 4.10 `crypto_assets`

**Zweck:** Stammdaten für Coins/Tokens.

**Felder:**

- `asset_id TEXT PRIMARY KEY`.
- `coin_name TEXT NOT NULL`.
- `symbol TEXT NOT NULL`.
- `coingecko_id TEXT`.
- `network_chain_default TEXT`.
- `is_stablecoin INTEGER NOT NULL DEFAULT 0`.
- `price_provider_primary TEXT NOT NULL DEFAULT 'CoinGecko'`.
- `price_provider_fallback TEXT`.
- `is_active INTEGER NOT NULL DEFAULT 1`.
- `notes TEXT`.
- `created_at TEXT NOT NULL`.
- `updated_at TEXT`.

**Indizes:**

- index auf `symbol`.
- unique index auf `coingecko_id`, wenn nicht leer.

**Validierung:**

- CoinGecko-ID stark empfohlen.
- Symbol allein ist nicht eindeutig; bei Konflikt manuelle Auswahl.

**Dummy-Beispiel:**

```text
asset_id=ca_demo_btc, coin_name=Demo Bitcoin, symbol=DBTC, coingecko_id=demo-bitcoin
```

---

## 4.11 `crypto_holdings`

**Zweck:** Aktueller Coin-Bestand pro Wallet; abgeleitet aus Crypto-Transaktionen plus bestätigten Snapshots/Korrekturen.

**Felder:**

- `crypto_holding_id TEXT PRIMARY KEY`.
- `asset_id TEXT NOT NULL REFERENCES crypto_assets(asset_id)`.
- `wallet_id TEXT NOT NULL REFERENCES crypto_wallets(wallet_id)`.
- `quantity NUMERIC NOT NULL`.
- `acquisition_source TEXT` – manual, csv, snapshot, telegram, calculated.
- `last_verified_at TEXT`.
- `verification_status TEXT NOT NULL DEFAULT 'unverified'` – verified, stale, unverified, estimated.
- `legacy_snapshot_value_original NUMERIC`.
- `legacy_snapshot_value_chf NUMERIC`.
- `legacy_snapshot_currency TEXT`.
- `legacy_snapshot_date TEXT`.
- `notes TEXT`.
- `created_at TEXT NOT NULL`.
- `updated_at TEXT`.

**Indizes:**

- unique index auf `(asset_id, wallet_id)`.
- index auf `wallet_id`.
- index auf `asset_id`.

**Validierung:**

- Quantity darf nicht negativ sein, ausser bestätigte Korrektur mit Alert.
- Legacy-Werte nicht als aktuelle Bewertung verwenden.

**Dummy-Beispiel:**

```text
crypto_holding_id=ch_demo_btc_ledger, asset_id=ca_demo_btc, wallet_id=wal_demo_ledger, quantity=0.10, acquisition_source=snapshot, verification_status=verified
```

---

## 4.12 `crypto_transactions`

**Zweck:** Crypto-spezifische Käufe, Verkäufe, Transfers, Gebühren, Initial-Snapshots.

**Felder:**

- `crypto_transaction_id TEXT PRIMARY KEY`.
- `transaction_id TEXT REFERENCES transactions(transaction_id)` – optional Link zum allgemeinen Ledger.
- `transaction_type TEXT NOT NULL` – crypto_buy, crypto_sell, crypto_transfer, crypto_fee, initial_snapshot, manual_adjustment, staking_reward_later.
- `asset_id TEXT NOT NULL REFERENCES crypto_assets(asset_id)`.
- `quantity NUMERIC NOT NULL`.
- `price_original NUMERIC`.
- `currency_original TEXT`.
- `gross_amount_original NUMERIC`.
- `fee_quantity NUMERIC`.
- `fee_original NUMERIC`.
- `fee_currency TEXT`.
- `fx_rate_to_chf NUMERIC`.
- `fx_source TEXT`.
- `amount_chf NUMERIC`.
- `from_wallet_id TEXT REFERENCES crypto_wallets(wallet_id)`.
- `to_wallet_id TEXT REFERENCES crypto_wallets(wallet_id)`.
- `transaction_datetime TEXT NOT NULL`.
- `tx_hash TEXT`.
- `source TEXT NOT NULL` – dashboard, csv, telegram, snapshot, manual_correction.
- `confirmation_status TEXT NOT NULL DEFAULT 'confirmed'` – draft, pending_confirmation, confirmed, rejected.
- `parse_confidence NUMERIC`.
- `original_input_text TEXT` – vertraulich, nicht exportieren/Git.
- `notes TEXT`.
- `created_at TEXT NOT NULL`.
- `updated_at TEXT`.

**Indizes:**

- index auf `(asset_id, transaction_datetime)`.
- index auf `from_wallet_id`.
- index auf `to_wallet_id`.
- index auf `transaction_type`.

**Validierung:**

- Crypto-Kauf braucht `to_wallet_id`.
- Crypto-Verkauf braucht `from_wallet_id`.
- Crypto-Transfer braucht `from_wallet_id` und `to_wallet_id` und beide müssen unterschiedlich sein.
- Menge > 0.
- Bestätigung vor finaler Speicherung bei Telegram.

**Dummy-Beispiel:**

```text
crypto_transaction_id=ctx_demo_transfer_1, transaction_type=crypto_transfer, asset_id=ca_demo_btc, quantity=0.01, from_wallet_id=wal_demo_exchange, to_wallet_id=wal_demo_ledger, transaction_datetime=2026-01-20T10:00:00Z
```

---

## 4.13 `crypto_prices`

**Zweck:** Aktuelle/historische Coin-Preise.

**Felder:**

- `crypto_price_id TEXT PRIMARY KEY`.
- `asset_id TEXT NOT NULL REFERENCES crypto_assets(asset_id)`.
- `coingecko_id TEXT`.
- `price_currency TEXT NOT NULL` – CHF, USD, EUR.
- `price NUMERIC NOT NULL`.
- `provider TEXT NOT NULL DEFAULT 'CoinGecko'`.
- `provider_timestamp TEXT`.
- `fetched_at TEXT NOT NULL`.
- `quality_status TEXT NOT NULL DEFAULT 'fresh'` – fresh, stale, missing, error, conflict.
- `error_message TEXT`.

**Indizes:**

- unique index auf `(asset_id, price_currency, provider, fetched_at)`.
- index auf `(asset_id, price_currency)`.

**Validierung:**

- Preis > 0.
- Wenn Preis fehlt, keine Fantasiebewertung; Warnung erzeugen.

**Dummy-Beispiel:**

```text
crypto_price_id=cp_demo_btc_chf, asset_id=ca_demo_btc, price_currency=CHF, price=50000, provider=CoinGecko, quality_status=fresh
```

---

## 4.14 `watchlist`

**Zweck:** Investment Pipeline.

**Felder:**

- `watchlist_id TEXT PRIMARY KEY`.
- `instrument_id TEXT REFERENCES instruments(instrument_id)`.
- `crypto_asset_id TEXT REFERENCES crypto_assets(asset_id)`.
- `name TEXT NOT NULL`.
- `asset_class TEXT NOT NULL`.
- `reason TEXT NOT NULL`.
- `target_entry_price NUMERIC`.
- `target_entry_currency TEXT`.
- `desired_position_size_chf NUMERIC`.
- `desired_weight_pct NUMERIC`.
- `trigger_rules TEXT` – JSON/Text.
- `risk_notes TEXT`.
- `investment_case TEXT`.
- `bear_case TEXT`.
- `sources TEXT` – JSON/Text.
- `status TEXT NOT NULL DEFAULT 'active'` – active, triggered, bought, rejected, archived.
- `next_review_date TEXT`.
- `created_at TEXT NOT NULL`.
- `updated_at TEXT`.

**Indizes:**

- index auf `status`.
- index auf `asset_class`.

**Validierung:**

- Grund der Beobachtung Pflicht.
- Entweder Instrument oder Crypto Asset oder Name muss vorhanden sein.

**Dummy-Beispiel:**

```text
watchlist_id=wl_demo_1, name=Demo ETF World, asset_class=etf, reason=Core-Kandidat, target_entry_price=100, target_entry_currency=CHF, status=active
```

---

## 4.15 `reports`

**Zweck:** Metadaten erzeugter Reports.

**Felder:**

- `report_id TEXT PRIMARY KEY`.
- `report_type TEXT NOT NULL` – portfolio, crypto_inventory, watchlist, daily, monthly.
- `title TEXT NOT NULL`.
- `period_start TEXT`.
- `period_end TEXT`.
- `generated_at TEXT NOT NULL`.
- `file_path TEXT` – lokaler Pfad, niemals Git.
- `format TEXT NOT NULL` – pdf, html, md.
- `data_quality_status TEXT NOT NULL DEFAULT 'ok'`.
- `summary_json TEXT`.
- `created_at TEXT NOT NULL`.

**Indizes:**

- index auf `report_type`.
- index auf `generated_at`.

**Validierung:**

- Report mit echten Daten liegt nur in lokalem Reports-Ordner.
- Keine Reports mit echten Zahlen in Git.

**Dummy-Beispiel:**

```text
report_id=rep_demo_crypto_1, report_type=crypto_inventory, title=Demo Crypto Bestand, format=pdf, data_quality_status=ok
```

---

## 4.16 `alerts`

**Zweck:** Warnungen und Aufgaben aus Datenqualität, Kursbewegung, FX, Ledger.

**Felder:**

- `alert_id TEXT PRIMARY KEY`.
- `priority TEXT NOT NULL` – info, wichtig, kritisch.
- `category TEXT NOT NULL` – data_quality, price_move, fx, ledger, crypto, report, watchlist.
- `entity_type TEXT`.
- `entity_id TEXT`.
- `rule_id TEXT`.
- `message TEXT NOT NULL`.
- `evidence_json TEXT`.
- `status TEXT NOT NULL DEFAULT 'new'` – new, acknowledged, snoozed, resolved, false_positive.
- `created_at TEXT NOT NULL`.
- `resolved_at TEXT`.

**Indizes:**

- index auf `(priority, status)`.
- index auf `category`.
- index auf `created_at`.

**Validierung:**

- Kritische Alerts dürfen nicht stillschweigend verschwinden.
- Resolved braucht `resolved_at`.

**Dummy-Beispiel:**

```text
alert_id=al_demo_fx_missing, priority=kritisch, category=fx, message=FX-Kurs fehlt für Demo-Transaktion, status=new
```

---

## 4.17 `audit_log`

**Zweck:** Unverzichtbare History jeder Änderung.

**Felder:**

- `audit_id TEXT PRIMARY KEY`.
- `timestamp TEXT NOT NULL`.
- `source TEXT NOT NULL` – dashboard, csv_import, telegram, manual_correction, system_job, snapshot_import.
- `action TEXT NOT NULL` – buy, sell, transfer, correction, dividend, snapshot, import, delete, verify, report_generated, alert_acknowledged, fx_override.
- `entity_type TEXT NOT NULL`.
- `entity_id TEXT NOT NULL`.
- `old_values_json TEXT`.
- `new_values_json TEXT`.
- `user_text_note TEXT`.
- `original_input_text TEXT`.
- `confirmed INTEGER NOT NULL DEFAULT 1`.
- `confirmation_timestamp TEXT`.
- `auto_parsed INTEGER NOT NULL DEFAULT 0`.
- `parse_confidence NUMERIC`.
- `created_by TEXT NOT NULL DEFAULT 'system'` – user, system.
- `quality_status TEXT NOT NULL DEFAULT 'ok'`.
- `created_at TEXT NOT NULL`.

**Indizes:**

- index auf `timestamp`.
- index auf `(entity_type, entity_id)`.
- index auf `source`.
- index auf `action`.

**Validierung:**

- jede bestätigte Transaktion braucht Audit-Eintrag.
- manuelle Korrektur braucht Notiz.
- Telegram-Parsing vor Speicherung als pending dokumentieren.

**Dummy-Beispiel:**

```text
audit_id=aud_demo_1, source=dashboard, action=buy, entity_type=transaction, entity_id=txn_demo_buy_1, confirmed=1
```

---

## 4.18 `import_sessions`

**Zweck:** Nachvollziehbarkeit von CSV-/Snapshot-Importen.

**Felder:**

- `import_session_id TEXT PRIMARY KEY`.
- `import_type TEXT NOT NULL` – accounts, instruments, transactions, crypto_wallets, crypto_holdings_initial, crypto_transactions, watchlist, cash_balances_initial.
- `source_filename TEXT NOT NULL`.
- `source_hash TEXT`.
- `started_at TEXT NOT NULL`.
- `finished_at TEXT`.
- `status TEXT NOT NULL` – started, completed, failed, partial, dry_run.
- `rows_total INTEGER DEFAULT 0`.
- `rows_imported INTEGER DEFAULT 0`.
- `rows_failed INTEGER DEFAULT 0`.
- `errors_json TEXT`.
- `notes TEXT`.

**Indizes:**

- index auf `import_type`.
- index auf `started_at`.
- index auf `status`.

**Validierung:**

- Import kann zuerst als Dry Run laufen.
- Fehlerhafte Zeilen werden dokumentiert, nicht verschwiegen.

**Dummy-Beispiel:**

```text
import_session_id=imp_demo_1, import_type=crypto_holdings_initial, source_filename=demo_crypto_holdings.csv, status=completed, rows_total=3, rows_imported=3
```

---

## 4.19 `decision_journal`

**Zweck:** Dokumentation menschlicher Entscheidungen.

**Felder:**

- `decision_id TEXT PRIMARY KEY`.
- `decision_date TEXT NOT NULL`.
- `decision_type TEXT NOT NULL` – buy, sell, hold, rebalance, override, watchlist_add, correction.
- `entity_type TEXT`.
- `entity_id TEXT`.
- `system_recommendation TEXT`.
- `human_decision TEXT NOT NULL`.
- `rationale TEXT NOT NULL`.
- `investment_case TEXT`.
- `risks TEXT`.
- `exit_rule TEXT`.
- `alternatives_considered TEXT`.
- `sources TEXT`.
- `review_date TEXT`.
- `outcome_status TEXT` – open, good, bad, neutral, pending.
- `outcome_return_chf NUMERIC`.
- `created_at TEXT NOT NULL`.
- `updated_at TEXT`.

**Indizes:**

- index auf `decision_date`.
- index auf `decision_type`.
- index auf `(entity_type, entity_id)`.

**Validierung:**

- menschliche Übersteuerung braucht Begründung.
- Kauf/Verkauf sollte Journal-Eintrag erlauben, aber MVP kann ihn optional machen; Overrides Pflicht.

**Dummy-Beispiel:**

```text
decision_id=dec_demo_1, decision_type=buy, human_decision=Demo-Kauf, rationale=Synthetischer Testfall, outcome_status=open
```

---

## 5. Ledger-Logik: Von Transaktionen zu Positionen

### 5.1 Grundsatz

`transactions` und `crypto_transactions` sind die Wahrheit für neue Bewegungen ab Systemstart. `positions_snapshot`, `cash_balances` und `crypto_holdings` sind berechnete oder kontrollierte Zustände.

Initial-Snapshots sind Startbestände, aber keine vollständige historische Transaktionswahrheit.

### 5.2 Kauf

- Menge erhöht Position.
- Cost Basis erhöht sich um `gross_amount_chf + fee_chf + tax_chf`.
- Cash reduziert sich um `net_amount_chf`.
- Durchschnittskosten werden neu berechnet.
- Audit-Log: action `buy`.

### 5.3 Teilverkauf

- Menge reduziert Position.
- Realisierter Gewinn/Verlust wird auf Basis der Cost-Basis-Methode berechnet.
- MVP-Methode: weighted average cost.
- Cash erhöht sich um Verkaufserlös minus Gebühren/Steuern.
- Bestand darf nicht negativ werden, ausser bestätigte Korrektur.
- Audit-Log: action `sell`.

### 5.4 Komplettverkauf

- Wie Teilverkauf, aber Menge wird auf 0 gesetzt.
- Restliche Rundungsdifferenzen werden bereinigt.
- Position bleibt historisch sichtbar.

### 5.5 Dividende/Ausschüttung

- Keine Mengenänderung.
- Cash erhöht sich um Nettobetrag.
- Brutto, Quellensteuer, Schweizer Verrechnungssteuer werden gespeichert, aber Aktien-/ETF-Steuermodul bleibt MVP-optional.
- Income fliesst in Total Return ein.

### 5.6 Gebühren

- Gebühren können an Transaktion hängen oder separat gebucht werden.
- Gebühren reduzieren Cash oder erhöhen Cost Basis bei Kauf.
- Bei Verkauf reduzieren sie Erlös.

### 5.7 Cash-Einzahlung

- Cash-Bestand Konto/Währung erhöht sich.
- Keine Performance.
- Audit-Log.

### 5.8 Cash-Auszahlung

- Cash-Bestand reduziert sich.
- Keine Performance.
- Negative Cash-Bestände erzeugen Warnung.

### 5.9 FX-Wechsel

- Reduziert Cash in Ausgangswährung.
- Erhöht Cash in Zielwährung.
- Speichert impliziten FX-Kurs, Gebühren und CHF-Gegenwert.
- FX-Wechsel erzeugt keinen Wertpapiergewinn, kann aber FX-Differenz dokumentieren.

### 5.10 Initial Snapshot

- Erzeugt Startposition oder Startcash.
- Wird als Snapshot markiert.
- Keine fiktive historische Rendite.
- Qualitätshinweis: Historie vor Systemstart unvollständig.

### 5.11 Crypto-Kauf

- `crypto_transactions`: `crypto_buy`.
- Zielwallet Pflicht.
- Coin-Menge erhöht Wallet-Bestand.
- Fiat-Wert und FX optional/erforderlich je Eingabe.
- Wenn über Cash-Konto gebucht: Cash reduziert.
- Audit-Log.

### 5.12 Crypto-Verkauf

- Quellwallet Pflicht.
- Coin-Menge reduziert Wallet-Bestand.
- Bestand darf nicht negativ werden.
- Fiat-Gegenwert/Cash wird erfasst.
- Realisierte P&L für Crypto kann vorbereitet werden, aber vollständige Steuerlogik nicht MVP 1.

### 5.13 Crypto-Transfer

- Quellwallet reduziert Menge.
- Zielwallet erhöht Menge.
- Fee reduziert Bestand oder Cash, je Fee-Währung.
- Kein Kauf/Verkauf.
- TxHash optional.
- Audit-Log.

### 5.14 Manuelle Korrektur

- Nur mit Notiz.
- Erzeugt Audit-Log mit alten/neuen Werten.
- Wenn sie Bestand verändert, entsteht Data-Quality-Hinweis.
- Korrekturen sind sichtbar, nicht stillschweigend versteckt.

---

## 6. CHF- und FX-Logik

### 6.1 Pflichtfelder bei Fremdwährung

Jede Fremdwährungstransaktion speichert:

- Originalwährung;
- Originalbetrag;
- historischen FX-Kurs zum Transaktionszeitpunkt;
- FX-Quelle;
- CHF-Gegenwert;
- späteren aktuellen CHF-Wert bei Bewertung;
- Trennung von Wertpapierperformance und FX-Effekt.

### 6.2 Fehlender historischer FX-Kurs

Wenn historischer FX fehlt:

- Transaktion darf als Entwurf oder `quality_status='incomplete'` gespeichert werden.
- `fx_status='missing'`.
- Alert Priorität Kritisch, wenn die Transaktion bestätigt werden soll.
- Position kann mit Warnung angezeigt werden, aber Total Return darf nicht als präzise ausgegeben werden.

### 6.3 Manueller FX-Override

Erlaubt, aber:

- `fx_source='manual_override'`.
- `fx_status='manual_override'`.
- Notiz/Quelle Pflicht.
- Audit-Log action `fx_override`.
- Report markiert Override.

### 6.4 Veraltete oder widersprüchliche FX-Daten

- `quality_status='stale'`, wenn Kurs zu alt für Bewertungszweck.
- `quality_status='conflict'`, wenn Provider stark abweichen.
- Alert: Wichtig oder Kritisch je betroffener Bewertung.
- Dashboard zeigt keine Scheingenauigkeit.

### 6.5 Trennung Wertpapierperformance vs FX

MVP-Methode:

- Cost Basis CHF aus historischem FX zum Kaufzeitpunkt.
- Aktueller Marktwert CHF aus aktuellem Preis × aktuellem FX.
- Preis-PnL approximiert mit Einstands-FX.
- FX-PnL = Differenz aus aktueller CHF-Bewertung minus Preis-PnL-Komponente und Cost Basis.
- Methodik im Report sichtbar machen.

---

## 7. Streamlit UI-Flows je Dashboard-Seite

### 7.1 Command Center

**Zweck:** Tagesübersicht und Entscheidungsstartpunkt.

**Kennzahlen:** Gesamtwert CHF, Cashquote, Crypto-Wert CHF, Alerts, Datenqualität, letzte Updates.

**Tabellen:** Top Bewegungen, offene Alerts, veraltete Daten.

**Filter:** Plattform, Assetklasse, Zeitraum.

**Buttons:** Report erzeugen, Daten aktualisieren, Alerts prüfen.

**Datenquellen:** positions_snapshot, crypto_holdings/prices, alerts, audit_log.

**Typische Handlungen:** prüfen was wichtig ist, Report starten, Alert öffnen.

### 7.2 Gesamtportfolio

Zeigt aggregierte Aktien/ETF/Cash/Crypto-Sicht in CHF. Filter nach Plattform, Assetklasse, Währung. Buttons für Export, Snapshot berechnen, Report.

### 7.3 Plattformansichten

Tabs für Raiffeisen, PostFinance, True Wealth, Crypto. Zeigt Konten, Cash, Positionen, P&L, Datenqualität. Nutzer wechselt Kontext ohne separate App.

### 7.4 Aktien/ETF-Portfolio

Tabelle Positionen mit Name, ISIN/Ticker, Menge, Einstand, Marktwert, P&L, FX-Effekt, Kategorie. Formulare für Kauf, Verkauf, Dividende, Korrektur. Buttons: Position bearbeiten, Journal öffnen.

### 7.5 Crypto-Seite

Kennzahlen: Gesamtwert Crypto CHF, optional USD/EUR, Anzahl Coins, Anzahl Wallets, fehlende Preise.

Tabellen:

- Coins nach Gesamtwert;
- Wallet-Aufteilung;
- letzte Crypto-Transaktionen;
- Datenqualitätswarnungen.

Buttons:

- Crypto-PDF erstellen;
- Bestand manuell verifizieren;
- Kauf erfassen;
- Verkauf erfassen;
- Transfer erfassen;
- CoinGecko-Preise aktualisieren.

### 7.6 Wallet-Übersicht

Wallets mit Typ, Provider, Chain, Aktivstatus, letzter Verifikation, Gesamtwert. Formulare für Wallet anlegen/bearbeiten/deaktivieren. Keine Wallet-Adresse Pflicht.

### 7.7 Transactions/Ledger

Ledger-Tabelle mit Filter nach Konto, Plattform, Typ, Datum, Instrument, Quelle. Formulare für Transaktionserfassung. Aktionen: CSV Import, Dry Run, Korrektur, Audit anzeigen.

### 7.8 Watchlist

Pipeline mit Zielpreis, gewünschter Grösse, Triggern, Quellen. Buttons: hinzufügen, bearbeiten, als gekauft übernehmen, archivieren.

### 7.9 Reports

Reporttypen: Gesamtportfolio, Crypto-Bestand, Watchlist, einfache Monatsübersicht. Buttons: PDF/HTML/Markdown erzeugen. Tabelle bisheriger Reports.

### 7.10 Alerts

Alert Center nach Info/Wichtig/Kritisch. Aktionen: acknowledge, snooze, resolve, false positive. Evidence JSON lesbar anzeigen.

### 7.11 History/Audit

Timeline aller Änderungen. Filter: Quelle, Aktion, Entity, Zeitraum. Detailansicht alte/neue Werte. Keine stillen Änderungen.

### 7.12 Settings/Data Quality

Einstellungen für Provider, FX-Quelle, Basiswährung, Git-/Datenordnerhinweise, Datenqualitätsregeln. Tabellen fehlende ISINs, fehlende CoinGecko-IDs, fehlende FX-Kurse, veraltete Preise.

---

## 8. Crypto-PDF-Report-Template

### 8.1 Titel

`JARVIS Finance System – Crypto-Bestandsübersicht`

### 8.2 Kopfbereich

- Erstellungsdatum/-zeit;
- Basiswährung CHF;
- Datenstand Preise;
- Datenstand Wallet-Verifikation;
- optional USD/EUR Ansicht;
- Hinweis: Bestandsübersicht, keine vollständige Steuererklärung.

### 8.3 Executive Summary

- Gesamtwert Crypto in CHF;
- Anzahl Coins;
- Anzahl Wallets/Plattformen;
- grösste Coin-Positionen;
- Datenqualitätsstatus.

### 8.4 Coin-Übersicht

Spalten:

- Coin;
- Symbol;
- Gesamtmenge;
- aktueller Kurs CHF;
- Gesamtwert CHF;
- Anteil Crypto-Portfolio;
- Preisquelle;
- letzte Preisaktualisierung;
- Warnung.

### 8.5 Wallet-Übersicht

Spalten:

- Wallet/Plattform;
- Wallet-Typ;
- Provider;
- Coins Anzahl;
- Gesamtwert CHF;
- letzte Verifikation;
- Notiz.

### 8.6 Detail pro Coin nach Wallet

Für jeden Coin:

- Wallet;
- Menge;
- Kurs;
- Wert CHF;
- letzte Verifikation;
- Notizen.

### 8.7 Datenqualitätswarnungen

- fehlende CoinGecko-ID;
- fehlender Preis;
- veralteter Preis;
- nicht verifizierte Wallet;
- negativer Bestand;
- Legacy-Snapshot-Wert vorhanden, aber nicht als aktuelle Bewertung verwendet.

### 8.8 Disclaimer

> Dieser Report ist eine technische Bestandsübersicht zur eigenen Dokumentation. Er ist keine vollständige Steuererklärung, keine Anlageberatung und kein Nachweis gegenüber Dritten ohne separate Prüfung.

---

## 9. Finale CSV-Templates MVP 1

Allgemein:

- Encoding: UTF-8.
- Separator: Komma oder Semikolon; Importer erkennt oder verlangt Einstellung.
- Dezimalformat intern: Punkt `.`.
- Datum: `YYYY-MM-DD`.
- Timestamp: ISO 8601.
- IDs optional, wenn System IDs generiert.

### 9.1 `accounts.csv`

Spalten:

```text
platform_name,platform_type,account_name,account_type,currency,performance_included,is_health_reserve,notes
```

Pflicht: platform_name, platform_type, account_name, account_type, currency.

Beispiel:

```text
PostFinance,broker,Demo Depot,brokerage,CHF,1,0,Synthetisches Beispiel
```

Validierung: eindeutiges Paar Plattform + Account.

### 9.2 `instruments.csv`

```text
asset_class,name,ticker,isin,exchange,currency,country,sector,data_provider_primary,notes
```

Pflicht: asset_class, name, currency. Aktien/ETFs: ticker oder ISIN empfohlen.

Beispiel:

```text
equity,Demo Global AG,DGA,CH0000000001,SIX,CHF,CH,Technology,dummy,Nur Testdaten
```

### 9.3 `transactions.csv`

```text
transaction_type,platform_name,account_name,trade_date,settlement_date,name,ticker,isin,quantity,price_original,gross_amount_original,fee_original,tax_original,net_amount_original,currency_original,fx_rate_to_chf,fx_source,external_transaction_id,notes
```

Pflicht: transaction_type, platform_name, account_name, trade_date, currency_original. Kauf/Verkauf zusätzlich Instrument und quantity.

Beispiel:

```text
buy,PostFinance,Demo Depot,2026-01-15,2026-01-17,Demo Global AG,DGA,CH0000000001,10,100,1000,5,0,1005,CHF,1,not_needed,demo-txn-001,Synthetischer Kauf
```

### 9.4 `crypto_wallets.csv`

```text
wallet_name,wallet_type,platform_provider,network_chain,owner,is_active,last_verified_at,notes
```

Pflicht: wallet_name, wallet_type.

Beispiel:

```text
Ledger Demo,Hardware Wallet,Ledger,Multiple,,1,2026-01-31T12:00:00Z,Synthetische Wallet
```

### 9.5 `crypto_holdings_initial.csv`

```text
snapshot_date,wallet_name,coin_name,symbol,coingecko_id,quantity,legacy_snapshot_value_original,legacy_snapshot_value_chf,legacy_snapshot_currency,last_verified_at,notes
```

Pflicht: snapshot_date, wallet_name, coin_name, symbol, quantity.

Beispiel:

```text
2026-01-31,Ledger Demo,Demo Bitcoin,DBTC,demo-bitcoin,0.10,,,,2026-01-31T12:00:00Z,Synthetischer Bestand
```

### 9.6 `crypto_transactions.csv`

```text
transaction_type,datetime,coin_name,symbol,coingecko_id,quantity,price_original,currency_original,fee_quantity,fee_original,fee_currency,from_wallet,to_wallet,fx_rate_to_chf,fx_source,tx_hash,notes
```

Pflicht je Typ:

- buy: to_wallet, quantity, coin.
- sell: from_wallet, quantity, coin.
- transfer: from_wallet, to_wallet, quantity, coin.

Beispiel:

```text
crypto_transfer,2026-02-01T10:00:00Z,Demo Bitcoin,DBTC,demo-bitcoin,0.01,,,0.0001,,DBTC,Kraken Demo,Ledger Demo,,,demo_hash_123,Synthetischer Transfer
```

### 9.7 `watchlist.csv`

```text
asset_class,name,ticker,isin,coingecko_id,reason,target_entry_price,target_entry_currency,desired_position_size_chf,desired_weight_pct,trigger_rules,risk_notes,investment_case,bear_case,sources,status,next_review_date
```

Pflicht: asset_class, name, reason.

Beispiel:

```text
etf,Demo World ETF,DWLD,CH0000000002,,Core-Kandidat,100,CHF,5000,5,"price<=100",Währungsrisiko,Synthetischer Case,Synthetischer Bear Case,dummy-source,active,2026-03-01
```

### 9.8 `cash_balances_initial.csv`

```text
platform_name,account_name,balance_date,currency,amount_original,fx_rate_to_chf,amount_chf,notes
```

Pflicht: platform_name, account_name, balance_date, currency, amount_original.

Beispiel:

```text
Raiffeisen,Demo Cash,2026-01-01,CHF,10000,1,10000,Synthetischer Initialbestand
```

---

## 10. Harte Validierungsregeln

1. Kaufmenge darf nicht 0 oder negativ sein.
2. Verkauf darf Bestand nicht negativ machen, ausser bewusst bestätigte Korrektur mit Notiz.
3. Crypto-Transfer braucht Quell- und Zielwallet.
4. Quell- und Zielwallet dürfen bei Transfer nicht gleich sein.
5. Wallet-Name muss eindeutig sein.
6. CoinGecko-ID ist stark empfohlen; fehlende ID erzeugt Warnung.
7. Fremdwährungstransaktion braucht FX-Kurs oder `fx_status='missing'`.
8. Bestätigte Transaktion braucht Audit-Log-Eintrag.
9. Manuelle Korrektur braucht Notiz.
10. Initial-Snapshot muss als solcher markiert sein.
11. Legacy-Crypto-Werte dürfen nicht als aktuelle Bewertung verwendet werden.
12. Report mit unvollständigen Kernwerten muss Datenqualitätswarnung enthalten.
13. Import muss idempotent sein: externe ID oder Row Hash verhindert Duplikate.
14. API-Key darf nie in DB, Git oder Report auftauchen.
15. Echte Finanzdateien dürfen nicht in Repo-Ordnern liegen.

---

## 11. Implementierungsplan MVP 1

### Phase 1: Projektstruktur, Datenbank, Dummy-Daten

**Ziel:** Grundgerüst ohne echte Finanzdaten.

**Ergebnis:** Repo-Struktur, SQLite Schema, synthetische Fixtures.

**Abhängigkeiten:** v0.4 Blueprint.

**Akzeptanzkriterien:** DB kann erstellt werden; Dummy-Daten laden; keine echten Daten im Repo.

**Nicht enthalten:** Dashboard-Logik, APIs.

### Phase 2: Ledger- und Positionsberechnung

**Ziel:** Transaktionen zu Positionen und Cash berechnen.

**Ergebnis:** Kauf/Verkauf/Cash/Dividende/Initial Snapshot funktionieren mit Dummy-Daten.

**Abhängigkeiten:** Phase 1.

**Akzeptanzkriterien:** Tests für Kauf, Teilverkauf, Komplettverkauf, Dividende, Cash.

**Nicht enthalten:** komplexe Steuerlogik.

### Phase 3: Crypto-Inventory und Wallets

**Ziel:** Wallets, Holdings, Transfers.

**Ergebnis:** Coin pro Wallet, Gesamt je Coin, Transferlogik.

**Abhängigkeiten:** Phase 1, Audit-Basis.

**Akzeptanzkriterien:** Test Bitcoin Demo auf 3 Wallets; Transfer reduziert/erhöht korrekt.

**Nicht enthalten:** On-chain APIs.

### Phase 4: Streamlit Dashboard Grundseiten

**Ziel:** MVP UI.

**Ergebnis:** Command Center, Portfolio, Crypto, Wallets, Ledger, Watchlist, Alerts, Audit.

**Abhängigkeiten:** Phasen 1–3.

**Akzeptanzkriterien:** Dummy-Daten sichtbar; Filter funktionieren; Formulare erzeugen Drafts/Einträge.

**Nicht enthalten:** perfektes Design/PWA.

### Phase 5: CSV Import/Export

**Ziel:** Standard-Templates importieren/exportieren.

**Ergebnis:** CSV Dry Run, Validierung, Import Sessions.

**Abhängigkeiten:** Phasen 1–3.

**Akzeptanzkriterien:** alle MVP Templates importierbar; fehlerhafte Zeile wird sauber gemeldet.

**Nicht enthalten:** Broker-spezifische Parser.

### Phase 6: Market Data und CoinGecko Preise

**Ziel:** Crypto-Preise lokal aktualisieren.

**Ergebnis:** CoinGecko Fetch, Cache, Staleness-Status.

**Abhängigkeiten:** Phase 3.

**Akzeptanzkriterien:** Dummy/Mock Provider Tests; fehlender Preis erzeugt Alert.

**Nicht enthalten:** Finnhub/FMP Vollintegration.

### Phase 7: FX-Kurse und CHF-Bewertung

**Ziel:** Historische und aktuelle FX-Kurse.

**Ergebnis:** FX Tabellen, Missing/Override Logik, CHF Bewertung.

**Abhängigkeiten:** Phasen 2 und 6.

**Akzeptanzkriterien:** USD-Kauf mit historischem FX; fehlender FX erzeugt Kritisch-Alert.

**Nicht enthalten:** Multi-Provider-Konfliktauflösung vollautomatisch.

### Phase 8: Reports, insbesondere Crypto-PDF

**Ziel:** Report Export.

**Ergebnis:** Crypto-PDF, Markdown/HTML optional, Report-Metadaten.

**Abhängigkeiten:** Phasen 3, 6, 7.

**Akzeptanzkriterien:** PDF mit Dummy-Daten, Preisquelle, Warnungen, Disclaimer.

**Nicht enthalten:** vollständiger Steuerreport.

### Phase 9: Audit-Log und Data Quality

**Ziel:** Nachvollziehbarkeit und Warnsystem härten.

**Ergebnis:** Audit-Pflicht, Alerts, Data Quality Seite.

**Abhängigkeiten:** alle vorherigen Phasen.

**Akzeptanzkriterien:** jede bestätigte Änderung hat Audit; Validierungsfehler sichtbar.

**Nicht enthalten:** Telegram Produktivworkflow.

### Phase 10: Tests und Sicherheitsprüfung

**Ziel:** MVP absichern.

**Ergebnis:** pytest Suite, Secret Scan, Gitignore-Prüfung, Dummy-only Repo.

**Abhängigkeiten:** Phasen 1–9.

**Akzeptanzkriterien:** Tests grün; keine echten Finanzdaten; API Keys nicht auffindbar.

**Nicht enthalten:** Deployment ins Internet.

---

## 12. Testkonzept mit synthetischen Daten

### 12.1 Grundregeln

- Keine echten Finanzdaten im Repo.
- Dummy-Namen eindeutig als Demo markieren.
- Keine realistischen Portfolio-Grössen nötig.
- Keine echten Wallet-Adressen.
- Keine echten API Keys.

### 12.2 Dummy-Daten

- Dummy-Aktie: Demo Global AG.
- Dummy-ETF: Demo World ETF.
- Dummy-Crypto: Demo Bitcoin, Demo Ether, Demo Solana.
- Dummy-Wallets: Ledger Demo, Kraken Demo, MetaMask Demo.
- Dummy-FX: USD/CHF, EUR/CHF.
- Dummy-Transaktionen: Kauf, Verkauf, Dividende, Cash, FX, Crypto-Transfer.

### 12.3 Testfälle

1. Kauf Aktie CHF: Position + Cash korrekt.
2. Kauf Aktie USD: FX gespeichert, CHF Cost Basis korrekt.
3. Teilverkauf: Menge reduziert, realisierter P&L berechnet.
4. Komplettverkauf: Menge 0, Historie bleibt.
5. Dividende: Cash netto erhöht, Income gespeichert.
6. Fehlender FX: Alert kritisch, Total Return unpräzise markiert.
7. FX Override: Audit-Log action `fx_override`.
8. Crypto Initial Snapshot: Holding angelegt, Legacy-Wert ignoriert.
9. Crypto Transfer: Quellwallet minus, Zielwallet plus, Fee berücksichtigt.
10. Negativer Crypto-Bestand: blockiert oder bestätigte Korrektur mit Alert.
11. Fehlender CoinGecko-Preis: Warnung, kein Fantasiewert.
12. CSV Duplikat: Row Hash verhindert Doppelimport.
13. Audit-Pflicht: bestätigte Transaktion ohne Audit schlägt Test fehl.
14. Git-Safety-Test: verbotene Dateitypen im Repo werden erkannt.

---

## 13. Datenschutz, Git und Backup

### 13.1 Ordnerstruktur

Empfohlen:

```text
jarvis-finance-system/
  src/
  tests/
  docs/
  examples/              # nur synthetische Daten
  config.example/
  .gitignore

local_runtime/           # ausserhalb Repo oder gitignored
  data/
  reports/
  exports/
  backups/
  secrets/
```

### 13.2 `.gitignore`

Muss mindestens enthalten:

```text
.env
*.env
*token*
*credential*
*client_secret*
*.key
*.pem

data/
reports/
exports/
backups/
local_runtime/

*.db
*.sqlite
*.sqlite3
*.parquet
*.xlsx
*.xls
*.docx
*.pdf
*.csv
*.json

!examples/**/*.csv
!examples/**/*.json
!config.example/**/*.json
```

Wichtig: Ausnahmen nur für synthetische Beispiele.

### 13.3 API Keys

- Nur in `.env` oder lokalem Secret Store.
- Nie in Code.
- Nie in DB.
- Nie in Reports.
- Nie in Git.

### 13.4 Lokale Datenbanken

- liegen unter `local_runtime/data/` oder ausserhalb Repo.
- Backups verschlüsseln, wenn möglich.
- Keine DB-Dateien committen.

### 13.5 Reports

- echte Reports lokal unter `local_runtime/reports/`.
- nicht in Git.
- optional Google Drive nur nach bewusster Entscheidung.

### 13.6 Backup-Strategie

MVP:

- lokales verschlüsseltes Backup der SQLite DB.
- Export CSV/JSON/Parquet für Datenhoheit.
- periodische Kopie in privaten Backup-Ort.

Später:

- verschlüsselte Cloud-Backups.
- automatischer Restore-Test.
- Backup-Rotation.

---

## 14. Telegram-Workflow vorbereitet, nicht MVP-prioritär

### 14.1 Unterstützte Parser-Inputs später

- „Kauf 0.25 ETH auf Wallet Ledger zum Preis 3200 USD, Gebühren 5 USD.“
- „Verkauf 100 SOL von Kraken zu 145 USD, Gebühren 2 USD.“
- „Transfer 0.1 BTC von Kraken zu Ledger, Gebühr 0.0001 BTC.“

### 14.2 Pflichtfelder erkennen

- Intent: Kauf, Verkauf, Transfer.
- Coin/Symbol.
- Menge.
- Preis und Währung bei Kauf/Verkauf, sofern angegeben.
- Gebühren.
- Quellwallet/Zielwallet.
- Datum/Uhrzeit, default Nachrichtzeit.

### 14.3 Bestätigung

- Parser erzeugt Draft.
- System fragt fehlende Felder nach.
- System zeigt Zusammenfassung.
- User bestätigt explizit.
- Erst dann Speicherung.

### 14.4 Audit

- Originaltext als vertrauliche Evidence.
- `auto_parsed=1`.
- `parse_confidence` speichern.
- finaler Audit bei Bestätigung.

### 14.5 Fehler/Nachfragen

- fehlende Wallet: nachfragen.
- unbekannter Coin: CoinGecko-Mapping anbieten.
- fehlende Währung: nachfragen.
- negativer Bestand: blockieren oder Korrekturprozess.

Priorität: nach Datenmodell, Dashboard, CSV, Crypto-PDF.

---

## 15. Akzeptanzdefinition v0.4

v0.4 ist akzeptiert, wenn:

- MVP 1 Tabellen vollständig definiert sind;
- Ledger-Logik eindeutig beschrieben ist;
- FX-Logik keine Scheingenauigkeit erzeugt;
- Crypto-Wallet-Struktur MVP-fähig ist;
- Streamlit-Seiten und UI-Flows klar sind;
- Crypto-PDF-Template konkret ist;
- CSV-Templates final genug für Implementierung sind;
- Validierungsregeln hart definiert sind;
- Implementierungsphasen priorisiert sind;
- Testkonzept ausschliesslich synthetische Daten nutzt;
- Git-/Datenschutzregeln echte Finanzdaten zuverlässig ausschliessen;
- Telegram vorbereitet, aber nicht vorgezogen wird.

---

## 16. Abschluss

Diese v0.4 ist der technische Bauplan für MVP 1. Der nächste Schritt nach Freigabe wäre kein weiteres Philosophieren, sondern ein sauberer Implementierungsplan mit Tasks, Tests und Repo-Struktur – selbstverständlich ohne echte Finanzdaten. Denn ein Finanzsystem, das zuerst Geheimnisse verliert und danach Tests schreibt, ist keine Architektur, sondern ein Unfall mit Benutzeroberfläche.
