ADR-008: Crash & Uptime Strategy (Phase 2)
Status: Proposed
Date: 2026-04-21
Context: Phase 2 — Testnet API Orders (echte Orders auf Testnet)
Problem
Wenn das System offline ist, laufen offene Positionen ungeschĂĽtzt. Kein SL, kein Trailing, keine Signal-Generierung. In Phase 1 (Paper Mode) verschmerzbar. In Phase 2 (echte API Orders) katastrophal.
Failure Scenarios
| # | Szenario | Wahrscheinlichkeit | Max Downtime | Risiko |
|---|---|---|---|---|
| 1 | Paper Engine crasht, VPS läuft | Mittel | 60 Min (Watchdog) | Position unmanaged |
| 2 | Docker/OpenClaw crasht, VPS läuft | Niedrig | Bis manueller Restart | Position unmanaged |
| 3 | VPS down (Hostinger Wartung/HW-Fehler) | Sehr niedrig | 5-120 Min | Position unmanaged, kein Restart |
| 4 | VPS unreachable (Netzwerk/DDoS) | Sehr niedrig | Variabel | Position unmanaged, kein Restart |
| 5 | Hyperliquid API down | Sehr niedrig | 5-30 Min | Keine Orders möglich, Position bleibt |
Strategie: 4-Layer Defense
Layer 1: Heartbeat File (intern)
Löst: Szenario 1 — Engine crasht, Rest läuft
- Paper Engine schreibt Timestamp alle 30s nach
heartbeat.json - Watchdog prĂĽft alle 60s: ist
heartbeat.jsonjünger als 90s? - Nein → Engine als "stale" markiert → Auto-Restart + Discord Alert
- Reduziert Erkennungszeit: 60 Min → 1-2 Min
# In paper_engine.py — main loop
HEARTBEAT_PATH = "heartbeat.json"
# Alle 30s:
json.dump({"ts": time.time(), "pid": os.getpid(), "positions": len(open_positions)},
open(HEARTBEAT_PATH, "w"))
# In watchdog_v2.py — additional check
hb = json.load(open("heartbeat.json"))
if time.time() - hb["ts"] > 90:
# Engine stale → escalate
Layer 2: Position Recovery (intern)
Löst: Szenarien 1+2 — Nach Restart, Positionen sicher wieder aufnehmen
- Nach jedem Restart:
state.dbladen, offene Positionen identifizieren - Sofort SL/Trailing prĂĽfen gegen aktuelle Preise
- Wenn SL überschritten → Not-Close sofort
- Wenn Position >
max_holdBars alt → Not-Close - Recovery-Log an Discord
def recover_positions(engine):
for pos in engine.state_manager.get_open_positions():
current_price = engine.data_feed.get_mid_price(pos.symbol)
# SL Check
if pos.side == "LONG" and current_price <= pos.stop_loss:
engine.close_position(pos, reason="RECOVERY_SL")
# Max Hold Check
if (datetime.utcnow() - pos.opened_at).total_seconds() > MAX_HOLD_SECONDS:
engine.close_position(pos, reason="RECOVERY_MAX_HOLD")
Layer 3: External Uptime Monitor (extern)
Löst: Szenarien 3+4 — VPS down oder unreachable
- UptimeRobot (gratis Plan): HTTP-Ping auf VPS-IP alle 5 Min
- Bei Ausfall: Discord-Webhook an
#systemChannel - Dave bekommt Push-Notification auf Handy
- Erkennungszeit: max 5 Min
Setup:
1. UptimeRobot Account → Monitor → HTTP(s) → VPS-IP:Port
2. Alert Contact → Discord Webhook (#system Channel)
3. Fallback: E-Mail an Dave
Erweitert (optional):
- Healthcheck-Endpoint in Paper Engine: /health → {"status":"ok","pid":123,"positions":1,"last_heartbeat":...}
- UptimeRobot pingt /health statt nur TCP-Connect
Layer 4: Kill-Switch Fallback (Hyperliquid)
Löst: Alle Szenarien — Letzter Ausweg
- Hyperliquid API-Wallet Timeout: Leider kein natives Auto-Cancel wie bei CEX (Bybit/Bybit haben das)
- Workaround: Pre-signed Cancel-Order (nicht verfĂĽgbar auf Hyperliquid)
- Realistischer Fallback: Manuelles SchlieĂźen via Hyperliquid UI oder Mobile App
- Dave hat Main-Wallet Access → kann Positionen manuell schließen
V2-Ziel: Sentiment-Kill-Switch als automatischer Not-Aus (ADR-005)
Escalation Ladder
| Zeit seit Ausfall | Aktion | Automatisch? |
|---|---|---|
| 0-90s | Heartbeat stale erkannt | âś… Watchdog |
| 90s-5min | Auto-Restart + Discord Alert | âś… Watchdog V2 |
| 5-15min | UptimeRobot Alert (VPS down) | âś… External |
| 15-30min | Dave muss manuell intervenieren | ❌ Manuell |
| 30min+ | Circuit Breaker → keine neuen Trades bis bestätigt | ✅ Watchdog V2 |
Phase 2 Checklist (vor Start)
- Heartbeat File in
paper_engine.pyimplementiert - Watchdog V2 prĂĽft
heartbeat.json(zusätzlich zu PID-Check) - Position Recovery in
paper_engine.pyRestart-Flow eingebaut - UptimeRobot Account eingerichtet + Discord Webhook
- Healthcheck-Endpoint oder VPS-IP-Monitor aktiv
- Dave hat Hyperliquid Testnet UI Access fĂĽr manuelle SchlieĂźung
- Escalation Ladder in
watchdog_v2.pydokumentiert - Test: Engine killen → Watchdog erkennt + restartet → Position recovered
Decision
Parken bis Phase 2. In Phase 1 (Paper Mode) ist das Risiko rein virtuell. Die Strategie wird vor Phase 2-Start implementiert und getestet.
Consequences
- Phase 2 Start blockiert bis alle 8 Checklist-Items erledigt
- UptimeRobot = externe Abhängigkeit (gratis, aber Drittanbieter)
- Manuelle Intervention immer noch möglich (Dave + Main Wallet)