Skip to content

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.json jĂĽ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.db laden, offene Positionen identifizieren
  • Sofort SL/Trailing prĂĽfen gegen aktuelle Preise
  • Wenn SL ĂĽberschritten → Not-Close sofort
  • Wenn Position > max_hold Bars 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 #system Channel
  • 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.py implementiert
  • Watchdog V2 prĂĽft heartbeat.json (zusätzlich zu PID-Check)
  • Position Recovery in paper_engine.py Restart-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.py dokumentiert
  • 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)