Eén Python script (orchestrator.py) dat alle taken combineert:
Draait 6× per dag via cron (03:05–23:05 NL, ma-vr) als no_agent watchdog. Model: deepseek-v4-flash.
Elke zondag 08:00 NL draait een Walk-Forward Analysis via cron 45f0050b1328. Deze backtest de strategie over de nieuwste data en rapporteert:
Bij verslechtering beslist Hugo over parameter-aanpassing (SMA window, RSI drempel, ATR multipliers). Volledig geautomatiseerde parameter-tuning (optie B) is een toekomstige stap — voor nu is de feedback-loop: WFA → Hugo → aanpassing.
ghcr.io/gnzsnz/ib-gateway — actief 🟢 verbondenHermes-container verbindt via Tailscale IP — geen Docker bridge of NAT nodig.
| Onderdeel | Waarde |
|---|---|
| Host | 100.108.36.111 |
| API-poort PAPER | 4004 ✅ |
| API-poort LIVE | 4002 |
| ib_insync | ib.connect('100.108.36.111', 4004, clientId=1, readonly=True) |
| Server-versie | 176 |
De gnzsnz-image gebruikt een socat-relay: extern poort 4004 → intern 127.0.0.1:4002 (paper). Verbinden met 4002 extern = TCP open maar 0 bytes terug → TimeoutError. Gebruik altijd 4004 voor paper.
De IB Gateway Java-applicatie overschrijft de [IBGateway] sectie van jts.ini bij elke login — ongeacht volume of template. De enige werkende aanpak: patch ná de login in de draaiende container, en herhaal dit automatisch.
✅ Permanent opgelost: socat (poort 4004) forwardt naar 127.0.0.1:4002 — gateway ziet altijd 127.0.0.1 als source, TrustedIPs speelt geen rol. De watchdog hoeft geen TrustedIPs meer te patchen. API_PORT=4002 en SOCAT_PORT=4004 env-vars zijn vereist in het redeploy-script.
Bij herdeployment: bash /root/redeploy_ib_gateway.sh op de VPS — dit script start de container correct (volume + juiste poortmapping) en wacht op login.
# Handmatige noodpatch (als watchdog nog niet gedraaid heeft): docker exec ib-gateway sed -i '/^\[IBGateway\]/,/^\[/ s/^TrustedIPs=.*/TrustedIPs=127.0.0.1,172.16.0.1,172.16.0.2,172.16.0.3,172.16.1.1,172.16.1.2/' /home/ibgateway/Jts/jts.ini
⚠️ De patch werkt alleen als de API-daemon hem nog niet heeft ingelezen. De daemon laadt TrustedIPs bij opstart — daarna heeft een live-patch geen effect totdat de daemon herstart. De socat-relay (poort 4004→intern 4002) omzeilt dit: socat forwardet verkeer als 127.0.0.1, wat altijd vertrouwd is.
Zonder -e API_PORT=4002 -e SOCAT_PORT=4004 env-vars start socat met lege waarden en luistert nergens op. Gevolg: poort 4004 is open maar geen relay → Error 162.
✅ Permanent gefixt: /root/redeploy_ib_gateway.sh bevat nu deze env-vars. Na elke redeploy draait socat correct.
run_trade_watchdog.sh gebruikte enkele quotes in de bash -c call: '... $VENV ... $ORCHESTRATOR ...'. Variabelen binnen enkele quotes worden NIET geïnterpoleerd in bash — de orchestrator werd nooit aangeroepen. Gevolg: 3 dagen lang (24-27 jun) draaide de cron stil zonder dat de orchestrator het log bijwerkte.
✅ Fix: enkele quotes vervangen door dubbele quotes: "... $VENV ... $ORCHESTRATOR ...".
🌐 Hostinger VPS (srv1659405)
└── 🐳 Docker
├── ib-gateway (gnzsnz/ib-gateway)
│ └── Poort 4004 (Paper) via socat relay → intern 4002
└── hermes-agent
├── 📊 Forex orchestrator.py (6× per dag, ma-vr)
├── ⏰ healthcheck_hourly (elk uur, stille poortcheck)
├── ⏰ forex rapport (21:10 UTC, dagelijks)
└── 📱 Telegram → Hugo
⚠️ Eén haperende schakel = hele keten stil.
| Tijd (NL zomer) | Wat | Cron |
|---|---|---|
| Elk uur | IB Gateway poortcheck — alleen alert bij wijziging | 4b3ffc53d10d |
| Elk uur (:20) | 📊 Data pipeline check — échte data ophalen + validatie · Stil bij ok, alert bij 0 bars/verouderd/crash | 2788d5867414 (nieuw!) |
| 03:05, 07:05, 11:05, 15:05, 19:05, 23:05 | Forex orchestrator — no_agent trade watchdog · Alleen melding bij trade/error/timeout · Incl. auto-patch TrustedIPs via SSH | 0ea6d4550bb1 (deepseek-v4-flash) |
| 23:10 | Dagelijkse rapport — healthcheck + HTML status (merged) | d0fe10245b00 (no_agent) |
✅ Monitoring: 4 lagen — poort (elk uur), data pipeline (elk uur :20), trade watchdog (6×/dag), dagelijks rapport (21:10 UTC). Stil bij normaal, melding bij elk probleem.
| Tijd (NL zomer) | Wat | Cron |
|---|---|---|
| 08:00 | Walk-Forward Analysis — backtest 4H strategie, rapport naar Telegram | 45f0050b1328 (deepseek-v4-flash) |
Referentiewaarden (live 24 jun 2026): OOS +5,30% · 14 trades/jr · WR 37,5% · PF 1,61. Bij verslechtering ⚠️ — Hugo beslist over aanpassing (optie A, geen auto-aanpassing).
Symptoom: Poort 4004 onbereikbaar. Telegram alert "IB Gateway Offline".
Oplossing — SSH naar VPS:
docker start ib-gateway timeout 3 bash -c "echo >/dev/tcp/100.108.36.111/4004" && echo "OPEN" || echo "DICHT"
Bij twijfel: docker logs ib-gateway --tail 20
Bij VPS-reboot of container-crash — één commando op de VPS:
bash /root/redeploy_ib_gateway.sh
Start de gateway opnieuw met correct volume + poortmapping, wacht op IBC login, patcht TrustedIPs. Hermes-crons draaien daarna automatisch door.
ssh-keygen -R srv1659405 ssh-keygen -R 100.108.36.111
Symptoom: Account info werkt via poort 4004, maar reqHistoricalData retourneert 0 bars met stille Error 162 in de TWS logs.
Oorzaak 1 — TrustedIPs: De IB Gateway Java-applicatie overschrijft [IBGateway] in jts.ini bij elke login — volume en template-patch hebben geen effect.
Oorzaak 2 — IBKR live sessie (ontdekt 29 jun 2026): Als je ergens anders bent ingelogd op IBKR (telefoon-app, desktop TWS), vergrendelt dat de marktdata voor andere sessies. De error is exact hetzelfde als TrustedIPs — je ziet het verschil alleen aan de gateway-logs.
Fix Oorzaak 2: Log uit bij IBKR op alle apparaten → Client Portal → Uitloggen → daarna gateway herstarten.
Permanente oplossing: socat (poort 4004) forwardt naar 127.0.0.1:4002 — TrustedIPs is altijd correct mits API_PORT=4002 en SOCAT_PORT=4004 env-vars in de container staan.
Herdeployment (bij VPS-reboot of container-crash):
bash /root/redeploy_ib_gateway.sh
Dit script op de VPS: stop/rm oude container → start nieuw met volume + alle 4 poorten (4004:4004) + -e API_PORT=4002 -e SOCAT_PORT=4004 → wacht 90s op IBC login.
Symptoom: De dagelijkse cron-run toont "Insufficient data points" ondanks dat de gateway online is (poort 4004 open).
Oorzaken (twee mogelijk):
250 D → 100 D → 50 D → 30 D met dalende drempels (200 → 80 → 40 → 20 bars). Als een kortere duration wél data oplevert, gebruikt de orchestrator die met een dynamische SMA window.Fix toegepast 23 jun 2026 in /opt/data/orchestrator.py — DataPipeline.get_market_data() probeert 4 durations met fallback, en past de SMA window dynamisch aan op basis van beschikbare bars.
Basislijn vastgelegd vóór eerste live paper trade. Hier meten we wekelijks tegenaan.
| Parameter | Waarde baseline |
|---|---|
| Entry | Close > MA200 + RSI(14) < 30 |
| Stop-loss | 2× ATR |
| Trail activatie | ≥3× ATR winst → peak − 1× ATR |
| Time exit | 60 bars (10 dagen) |
| Cooldown | 12 bars (48 uur) |
| Weekend | Vrijdag 19:05: BE trades → stop naar entry |
| Max risico/trade | 2% |
| Max drawdown | 5% |
| Metriek | Waarde |
|---|---|
| Periode | 2013–2026 (13 jaar, Brownian Bridge 4H gesimuleerd) |
| Trades | 208 |
| Trades/jaar | ~14 |
| Rendement | +64,1% |
| Winrate | 26,4% |
| Sharpe ratio | 0,47 |
| Max drawdown | −5,4% |
| Profit factor | 3,13 |
| Component | Status |
|---|---|
| Account | DUQ630468 (paper) · poort 4004 |
| Orchestrator | /opt/data/orchestrator.py · auto-order + exits |
| Cronjobs | 3 forex (gemerged) · deepseek-v4-flash |
| Watchdog | no_agent · alleen melding bij actie |
| WFA | Zondag 08:00 NL · rapport voor evaluatie |
| Wiki | ai-agent-plannen.html · up to date |
| Wanneer | Wat checken |
|---|---|
| Zondag 28 jun | Eerste WFA · eerste live trades · bugs? |
| Zondag 5 jul | 2 weken live · short leg toevoegen? |
| Elke zondag | WFA vs baseline — verslechtering? |
EUR/USD 4H candles sluiten op UTC: 01:00, 05:00, 09:00, 13:00, 17:00, 21:00. De orchestrator draait op :05 na elke close om de net afgesloten candle te analyseren.
get_market_data() vangt dit op via kortere durations.Signalen worden direct na candle-close berekend en uitgevoerd. Geen vertraging — market order op de eerstvolgende beschikbare prijs.
De watchdog stuurt alleen een bericht bij een relevante event: trade geplaatst, fout, timeout of trade geblokkeerd. Geen melding = passief, alles normaal.
Het systeem functioneert correct, maar de markt voldoet niet aan de twee instapeisen (prijs > MA200 én RSI < 30). Geen actie vereist.
Alle condities ✅ + cooldown verstreken + risk goedgekeurd → orchestrator plaatst automatisch een market buy + stop-loss op paper account. Geen handmatige actie nodig. Het rapport dat je ontvangt is een bevestiging, geen vraag om toestemming.
Een signaal was aanwezig, maar de RiskManager heeft ingegrepen. Dit treedt op in twee scenario's:
trading_state.jsontail -50 /opt/data/trading_logs/daily.log | grep RISK
Na een stop-out (of handmatige sluiting) start een 12-bar cooldown (48 uur). De orchestrator detecteert de exit door had_position in trading_state.json te vergelijken met de huidige positie. Zelfs als condities meteen weer ✅ zijn, wordt er pas na 48 uur weer een entry overwogen. Voorkomt whipsaw na stop-loss.