docs: Design Live-Paper-Engine (Phase 3, Paper-Probelauf der besten Variante)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
127
docs/specs/2026-06-10-live-paper-engine-design.md
Normal file
127
docs/specs/2026-06-10-live-paper-engine-design.md
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
# trade-kuns — Live-Paper-Engine (Phase 3, Design)
|
||||||
|
|
||||||
|
**Datum:** 2026-06-10
|
||||||
|
**Status:** Umsetzung (User-Entscheidung: bewusster Paper-Probelauf)
|
||||||
|
**Basis:** Spec `2026-06-09-trade-kuns-design.md` §4.3–4.6, §6, §8
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Kontext & Entscheidung
|
||||||
|
|
||||||
|
Das Walk-Forward-Gate wurde von keiner der 7 Varianten bestanden (siehe
|
||||||
|
`docs/walkforward-ergebnisse-2026-06-09.md`). Beste Variante: **long-only mit
|
||||||
|
fixen Spec-Default-Parametern** (Donchian 20 / ATR×3 / EMA 200 / ADX 20) —
|
||||||
|
OOS-PF 1.21, 249 Trades, MaxDD 16 %, Overfitting-Ratio 1.51; einziger
|
||||||
|
Fail: 11/32 Fenster mit PF < 0.5.
|
||||||
|
|
||||||
|
**Entscheidung (User, 2026-06-10):** Live-**Paper**-Engine bauen und diese
|
||||||
|
Variante im Paper-Probelauf validieren. Das Gate wird nicht aufgeweicht —
|
||||||
|
der Paper-Lauf *ist* die nächste Validierungsstufe. Kein echtes Geld, keine
|
||||||
|
Order-Ausführung, keine Schreib-API-Keys (Spec §10 unverändert).
|
||||||
|
|
||||||
|
## 2. Abweichungen von der Ursprungs-Spec
|
||||||
|
|
||||||
|
| Spec | Jetzt | Grund |
|
||||||
|
|---|---|---|
|
||||||
|
| Subdomain `trade.kuns.dev` | **`trading.kuns.dev`** | User-Vorgabe |
|
||||||
|
| Hono | **`Bun.serve`** | 6 GET-Routen, kein Framework nötig |
|
||||||
|
| Vue 3 + Vite Dashboard | **statische Single-Page** (Vanilla JS, Canvas-Chart) | kein Build-Step, kein Over-Engineering |
|
||||||
|
| Zitadel-JWT-Auth | **keine Auth** | API ist read-only (Paper-Daten, nichts Sensibles); Schreib-Endpoints (`toggle`/`reset`/`config`) entfallen in v1 |
|
||||||
|
| Port 8080 | 8080 | unverändert |
|
||||||
|
|
||||||
|
## 3. Architektur
|
||||||
|
|
||||||
|
Ein Bun-Prozess (`src/server/index.ts`): HTTP-Server + 5-min-Loop.
|
||||||
|
|
||||||
|
```
|
||||||
|
src/server/
|
||||||
|
live/
|
||||||
|
engine.ts LiveEngine: Zyklus-Orchestrierung, Recovery, Persistenz
|
||||||
|
process-cycle.ts pure Funktion: (candles, state) → actions (Entries/Exits/Decisions/Equity)
|
||||||
|
api/
|
||||||
|
server.ts Bun.serve: /health, /api/*, statisches Dashboard
|
||||||
|
db/schema.ts + positions, paper_trades, decision_logs, bot_state, equity_snapshots
|
||||||
|
public/index.html Dashboard
|
||||||
|
```
|
||||||
|
|
||||||
|
**Ein Code-Pfad-Prinzip bleibt:** `process-cycle.ts` nutzt exakt dieselben
|
||||||
|
puren Funktionen wie der Backtest-Runner (`computeIndicators`, `evaluateAt`,
|
||||||
|
`updateChandelier`, `sizePosition`, `Portfolio`) mit identischer Semantik:
|
||||||
|
4h-Entries (Close > Donchian-High(20) ∧ Close > EMA-200 ∧ ADX ≥ 20),
|
||||||
|
15m-Stop-Checks (Low ≤ Stop → Exit, Gap → Open als schlechterer Fill),
|
||||||
|
Chandelier-Update pro abgeschlossener 4h-Bar, Fees 0.1 % + Slippage 5 bps.
|
||||||
|
|
||||||
|
## 4. Zyklus (alle 5 Minuten)
|
||||||
|
|
||||||
|
1. **Fetch:** je Pair neueste 15m-Candles von Crypto.com (Lücke seit Cursor,
|
||||||
|
`end_ts`-Paginierung wie Backfill, max 300/Request), nur abgeschlossene
|
||||||
|
(`ts + 15m ≤ now`) → `candles` (Dedup via PK).
|
||||||
|
2. **Load:** je Pair letzte ~6500 15m-Candles (≈ 400 4h-Bars — Warmup für
|
||||||
|
EMA-200 + Donchian) aus DB.
|
||||||
|
3. **Process** (pure, deterministisch): alle 15m-Candles mit `ts > cursor`
|
||||||
|
chronologisch gemergt (Tie-Break PAIRS-Reihenfolge wie Runner):
|
||||||
|
- neue abgeschlossene 4h-Bars des Pairs: Chandelier-Update → Entry-Evaluation
|
||||||
|
(jede Evaluation → DecisionLog, inkl. Blockierungsgrund/Sizing-Block)
|
||||||
|
- 15m-Stop-Check der offenen Position
|
||||||
|
- Equity-Punkt einmal pro 4h-Bucket
|
||||||
|
4. **Persist** (eine Transaktion): Positions-Upsert/Delete, Trades,
|
||||||
|
DecisionLogs, Equity-Snapshots, `bot_state` (cash, cursor).
|
||||||
|
5. **Outcome-Backfill:** `decision_logs` mit NULL-Outcomes füllen, sobald
|
||||||
|
Candles 4h/24h/72h später vorliegen (Edge-Frühindikator, Spec §5.5).
|
||||||
|
|
||||||
|
**Initialisierung (erster Start):** `bot_state` mit 1000 USDT Cash,
|
||||||
|
Cursor = neueste abgeschlossene 15m-Candle. Keine historische Replay —
|
||||||
|
der erste mögliche Entry ist der nächste frische 4h-Close.
|
||||||
|
|
||||||
|
**Restart-Recovery (Spec §6):** Positionen + Cash + Cursor aus DB; verpasste
|
||||||
|
Candles werden nachgeholt und Stops rückwirkend geprüft — identisch zum
|
||||||
|
Normalzyklus, da der Prozess-Schritt nur vom Cursor abhängt.
|
||||||
|
|
||||||
|
**Fehler:** API-Fehler eines Pairs → Pair in diesem Zyklus überspringen,
|
||||||
|
andere laufen weiter; Fetch-Retry (3×, Backoff) existiert im Client.
|
||||||
|
DB-Fehler → Zyklus abbrechen, Status rot. Überlappende Zyklen durch
|
||||||
|
`running`-Flag verhindert. Letzter Zyklus-Status sichtbar in `/api/portfolio`.
|
||||||
|
|
||||||
|
## 5. Datenbank (neu)
|
||||||
|
|
||||||
|
- `positions` — pair (PK), side, qty, entry_ts, entry_price, entry_cost,
|
||||||
|
initial_stop, stop, trail_extreme, risk_amount
|
||||||
|
- `paper_trades` — id, pair, side, entry/exit (ts+price), qty, pnl, r, exit_reason
|
||||||
|
- `decision_logs` — id, pair, bar_ts (4h), signal, blocked_by, close, atr, adx,
|
||||||
|
donchian_high, trend_ema, price_after_4h/24h/72h (nullable), unique(pair, bar_ts)
|
||||||
|
- `bot_state` — id=1, cash, start_capital, cursor_ts, updated_at
|
||||||
|
- `equity_snapshots` — ts (PK, 4h-Bucket), equity, cash
|
||||||
|
|
||||||
|
## 6. API & Dashboard
|
||||||
|
|
||||||
|
| Route | Inhalt |
|
||||||
|
|---|---|
|
||||||
|
| `GET /health` | `{ok, lastCycle, cycleError}` — Coolify-Healthcheck |
|
||||||
|
| `GET /api/portfolio` | Equity, Cash, offene Positionen (inkl. Stop, unrealized PnL), Zyklus-Status |
|
||||||
|
| `GET /api/trades?limit` | abgeschlossene Trades, neueste zuerst |
|
||||||
|
| `GET /api/decisions?pair&limit` | DecisionLog inkl. Outcomes |
|
||||||
|
| `GET /api/stats` | PF, WinRate, MaxDD, avgR, Trade-Anzahl, Equity-Kurve |
|
||||||
|
| `GET /api/candles?pair&tf=15m|4h&limit` | Candles fürs Dashboard |
|
||||||
|
|
||||||
|
Dashboard: eine statische Seite, 30-s-Polling. KPI-Leiste (Equity, PnL, PF,
|
||||||
|
WinRate, MaxDD), Equity-Kurve (Canvas), Tabellen: offene Positionen, Trades,
|
||||||
|
letzte Decisions je Pair.
|
||||||
|
|
||||||
|
## 7. Tests
|
||||||
|
|
||||||
|
- `process-cycle`: Determinismus; Entry auf 4h-Close; Stop-Check inkl.
|
||||||
|
Gap-Fill; Cursor-Idempotenz (zweiter Lauf ohne neue Candles = no-op);
|
||||||
|
Restart-Äquivalenz (ein Lauf über N Candles ≡ zwei Läufe mit Cut dazwischen).
|
||||||
|
- Engine-Persistenz gegen Test-DB wird nicht automatisiert (kein CI mit DB);
|
||||||
|
manuelle Verifikation beim Deploy.
|
||||||
|
|
||||||
|
## 8. Deployment
|
||||||
|
|
||||||
|
- Dockerfile `oven/bun:1.3`, Start: Migrationen → Server, Port 8080,
|
||||||
|
Healthcheck `/health`.
|
||||||
|
- Coolify-App `trade-kuns`, Domain `https://trading.kuns.dev`, Netz `coolify`.
|
||||||
|
- `DATABASE_URL=postgres://mika:…@l8kogcggsc80sgcgk8kswww4:5432/tradekuns`
|
||||||
|
(shared-postgres über Coolify-Docker-Netz; vom Host aus weiterhin
|
||||||
|
`localhost:54320`).
|
||||||
|
- Backfill läuft weiter vom Host (`bun run backfill`) oder implizit über den
|
||||||
|
Lücken-Fetch des Loops.
|
||||||
Reference in New Issue
Block a user