atchdip.
Catch the dip. Automatically.
Bot który czeka na paniczne spadki ≥3% na 50 tickerach US. Kupuje limit orderem z us-east-1, sprzedaje przy +5% odbiciu, stop-loss −10%. WebSocket fills · real-time KPI · otwarte backtesty.
- 50 tickerów. Scan co 30s.
- −3% dip → limit BUY · +5% → SELL.
- 690 trade'ów · 71.3% win rate.
- +159.75% w 25 m-cy backtest. SPY: +17%.
Cztery rzeczy które bot robi lepiej.
Bo nie ma FOMO, nie panikuje przy czerwonych świecach i nie czeka aż "rynek się ustabilizuje".
Tyle pokazał walk-forward.
Każdy miesiąc out-of-sample — strategia nigdy nie widziała danych testowych. Implementacja w produkcji jest 1:1 z backtestem.
Indeks: 17%. CatchDip: 159%.
Te same 25 miesięcy. Ten sam start. CatchDip kupuje gdy rynek panikuje — indeks tylko czeka i bywa drogi.
Production-grade, open source.
Docker z healthcheckami. WebSocket realtime fills. SQLite WAL. 155 testów na CI. Pre-commit z secret scanning. us-east-1 deployed. Otwarte backtesty.
Broker → strategia → ty.
Alpaca = source of truth dla pieniędzy. SQLite = tylko historia (trade'y, snapshoty equity, sygnały). Stan rachunku nigdy nie cache'owany — co widzisz, to co masz.
Zobacz CatchDip live.
Aktualne pozycje · P&L w czasie rzeczywistym · slippage bps per trade · KPI dashboard · otwarte backtesty. WebSocket push, bez polling'u.
Otwórz dashboard →| Ticker | Ilość | Entry | Kupione | Cena | Wartość | Δ % | Unrealized P&L | Exit |
|---|---|---|---|---|---|---|---|---|
| Brak otwartych pozycji | ||||||||
| Suma · 0 pozycji | — | — | — | |||||
| Ticker | Cena | Δ dnia | Sygnał | Skan |
|---|---|---|---|---|
| Czekam na pierwszy skan... | ||||
| Czas (UTC) | Ticker | Akcja | Fill | Slip | Ilość | Cena | P&L |
|---|---|---|---|---|---|---|---|
| Bot startuje... | |||||||
| Ticker | Avg bps | Trades |
|---|---|---|
| Ładowanie... | ||
| Czas | Ticker | Akcja | Fill | Snap | Fill price | Diff $ | Slippage bps |
|---|---|---|---|---|---|---|---|
| Ładowanie... | |||||||
data/KILL_SWITCH — bot pomija scan + nowe BUY. Otwarte pozycje zostają nietknięte (Alpaca trzyma ich stan).| Mode | PAPER |
| Endpoint | — |
| Konto | — |
| Seed (wpłata) | — |
| Realized P&L | — |
| Aktualny kapitał | — |
| Alokacja per slot | — |
| Slotów aktywnych | — |
config.yaml + docker compose restart bot| strategia | Strategy name | B_wider |
| strategia | Universe size | — |
| strategia | Max slots | — |
| strategia | Scan interval | 60s |
| strategia | Dip threshold (entry) | — |
| strategia | Bounce target (SELL) | — |
| strategia | Stop loss | — |
| risk | Daily loss limit | — |
| risk | Max drawdown (od ATH) | — |
| risk | Cooldown po stop-lossie | — |
| risk | Max day trades / 5d (PDT) | — |
| live | Aktualny status risk | — |
| live | Day trades w 5d | — |
config.yaml → strategy.ticker_universefeat: → MINOR, fix: → PATCH, BREAKING: → MAJOR).CHANGELOG.md w repo| Ticker | Kupno | Sprz. | Qty | Cena kup. | Cena sprz. | Zysk USD | NBP kup. | NBP sprz. | Koszt PLN | Przychód PLN | Zysk PLN | Dni |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| — | ||||||||||||
01 B_wider — strategia produkcyjna +159.75% / -7.71% DD · walk-forward 25m
Bot kupuje akcje gdy spadną ≥ 3% w jeden dzień, sprzedaje przy +5% odbicia lub -10% stop-loss. Z 50 tickerów dziennie wybiera top dipy — wypełnia max 12 slotów. Każdy slot = ~$1083 z budżetu $13k.
Cykl skanu (co 1 min)
| FETCH | Snapshoty + pozycje | 50 snapshots + 1 bulk positions call. Total ~52 calls/min (limit Alpaki: 200/min). |
| EXIT | Zamknięcia first | Każda otwarta pozycja: ret ≥ +5% → SELL · ret ≤ -10% → STOP-LOSS + 24h cooldown. |
| ENTRY | Top dipy fill slots | Tickery bez pozycji & cooldown z day_chg ≤ -3% → sortuj po głębokości → fill puste sloty top kandydatami. |
Universe — 50 tickerów (V5 walk-forward)
Parametry (config.yaml)
Czemu B wygrywa nad baseline
| +61pp | Diversyfikacja | 50 vs 8 nazw — mniejsze idiosyncratic risk, znacznie więcej okazji dziennie. |
| +8pp | Top-dip selection | Bierzemy najgłębsze dipy dnia z 50 nazw — nie czekamy aż konkretny ticker dipnie. |
| ×2.6 | Risk-adjusted | return/|DD| = 20.7× vs baseline 7.9× — lepszy stosunek zwrot/ryzyko. |
02 Walk-forward backtest 25 miesięcznych okien · out-of-sample
Klasyczny backtest cierpi na lookahead bias: optymalizujesz na 5 latach → świetnie → live klapa. Walk-forward eliminuje to — każdy miesiąc testowy nigdy nie był widziany przez strategię w treningu.
Algorytm
| 01 | Pobierz 2 lata bars | Daily OHLCV z Alpaki dla wszystkich 50 tickerów (V5 expansion). |
| 02 | 25 miesięcznych okien | Każdy miesiąc to izolowany test set. Strategia jest deterministyczna — brak fazy treningu. |
| 03 | Symuluj close-to-close | day_chg ≤ dip → BUY na close. Hold do bounce/SL. Pozycje otwarte na koniec miesiąca: nie liczone (uczciwie). |
| 04 | Stitch equity | Cumulative = $13k + suma P&L wszystkich okien. Realistyczna trajektoria którą bot mógłby osiągnąć. |
Czego backtest NIE zawiera
| missing | L1 filter | Pokazuje raw strategy. Z L1 będzie mniej trade'ów ale lepsza jakość. |
| missing | Risk manager halt-y | Symulacja może mieć DD > 10% (w live byłby HALT). |
| missing | Slippage / fee | Alpaca paper $0 fee; real broker doda 0.05-0.2% slippage. |
| caveat | Survivorship bias | Testujemy tickery które dziś istnieją; te które padły nie są w portfelu. |
Re-run
03 Daily digest — LLM raport po sesji $0.01-0.03 per dzień
Po zamknięciu sesji NYSE bot raz dziennie wysyła dane do Claude API. Claude pisze 5-7-zdaniowy briefing po polsku — co bot zrobił, kluczowe trade'y, otwarte pozycje, risk events. LLM nie podejmuje decyzji handlowych, tylko podsumowuje.
Pipeline
| TRIGGER | Po close NYSE | Bot main loop: clock.is_open == False + brak digestu na dziś → wywołaj. Idempotent (kolejne iteracje skip-ują). |
| PROMPT | Składa portfolio_snapshots + trades + scan_analysis + bot_status + opis strategii. | |
| API | claude-sonnet-4-5 | POST /v1/messages, max 1024 tokens output, anthropic-version 2023-06-01. |
| PERSIST | daily_digests | PRIMARY KEY = date. 1 digest/dzień. Renderuje się w zakładce Raport. |
Bez API key
Brak ANTHROPIC_API_KEY → graceful skip + auto-fallback na template digest (statystyki z DB, deterministyczny, bez kosztu). Bot nie crashuje.
04 L1 Filter — anti-falling-knife RSI · volume · ATR · domyślnie OFF
BTD ma jedną piętę: łapanie spadającego noża. Spadek -5% może być technicznym dipem (mean revert) albo złą wiadomością (dalej -20%). L1 próbuje rozróżnić 3 regułami przed BUY.
3 reguły (wszystkie muszą się zgodzić)
| RSI | < 35 oversold | 14-dniowy momentum. <30 = oversold, >70 = overbought. Chcemy oversold. |
| VOL | > 1.3× śr. 20d | Panic selling = duże volume. Drift down z małym volumem = no buyers, prawdziwy problem. |
| ATR | dip < 4× ATR | Jeśli dziś dip jest >4× normalna zmienność = news event, nie naturalna oscylacja. |
Status w UI
Filter blokuje BUY → ticker dostaje FILTER w sygnałach. Powód w logach. Następny skan reevaluuje.
Fail-open
Brak historical bars (timeout) → filter przepuszcza BUY + warning. Lepsze niż blokować trade'y przez problemy infra.
05 Risk Manager — kill-switch 4 limity · HALT blokuje BUY tylko
Strategia decyduje co kupić; risk manager — czy w ogóle wolno kupować teraz. To osobna warstwa, bezpiecznik na bug, flash-crash, złą passę. HALT blokuje tylko BUY — otwarte pozycje zamykają się normalnie SELL/SL.
4 limity
| -2% | Daily loss | today_pnl ≤ -2% startowego equity → HALT do końca dnia. Resetuje się o 00:00 UTC. |
| -10% | Max drawdown | equity ≤ 90% all-time-high → HALT. Wymaga ręcznego restartu po fixie. |
| 24h | Cooldown / ticker | Po stop-lossie blokuje BUY tego tickera przez 24h (anti-falling-knife). |
| 8 | Max otwartych | Sanity check — bot nie może mieć > 8 pozycji równocześnie. |
Przykład HALT
06 Compound growth — alokacja rośnie z zyskami +1006% backtest vs +263% flat
Bot reinwestuje zyski. Alokacja per slot = (seed + realized P&L) / max_slots. Po każdym SELL'u kapitał strategii rośnie, więc kolejne BUY mają większy sizing — wykładniczy wzrost zamiast liniowego.
Mechanika
| Seed (wpłata) | Z config.yaml: strategy.total_budget_usd. To kotwica — alokacja nie spadnie poniżej seed/max_slots. |
| Realized P&L | Suma pnl ze wszystkich zamkniętych SELL'i (tabela trades). Tylko zamknięte — unrealized celowo POMINIĘTY (żeby alokacja nie skakała co minutę). |
| Alokacja | Liczona dynamicznie w current_alloc_per_ticker(). Aktualna wartość widoczna w zakładce Konfiguracja. |
| Hard cap | Pre-BUY check: jeśli cost_basis + alloc > capital × 1.02 → BUY pominięty. Zapobiega overspendowi gdy stare pozycje używały innego sizingu. |
Backtest 38m (zakładka Backtest → Compound)
Compound 4× więcej zarabia bo każdy zysk powiększa pozycje. Caveat: max DD podwojony; przy live z compound'em możliwe -25% spadki w bear marketach (vs -5-10% przy flat).
07 PDT rule — Pattern Day Trader Kluczowe dla seed <$25k
FINRA: 4+ day trades w 5 dniach roboczych w margin account z equity <$25k = konto blocked na 90 dni (no day trading). Bot blokuje 4-tym SELL'em zanim się zdarzy.
Co to day trade?
BUY i SELL tej samej akcji w tym samym dniu giełdowym ET. Strategia często to robi — kupuje dip o 9:35, SELL na +5% o 14:22 = day trade.
Bot enforcement
| Tracker | count_day_trades_last_5d() — liczy unique (ticker, ET-date) z BUY+SELL tego samego dnia w ostatnich 5 dni roboczych. |
| Blokada | W process_exit: jeśli SELL byłby day-tradem ORAZ count ≥ 3 (próg config risk.max_day_trades_per_5d) → SELL pominięty, "pdt_hold" w sygnałach. Pozycja zamknie się następnego dnia. |
| Wyjątek | STOP-LOSS zawsze przechodzi — capital preservation > PDT compliance. Jeden 90-dni block lepszy niż -10% strata. |
| Counter | Aktualny stan widoczny w zakładce Konfiguracja: "Day trades w 5d: X/3". Również jako kolorowy badge pod kapitałem strategii w Akcjach. |
Ucieczka
Wpłać $25k+ → margin equity ≥ $25k → unlimited day trades, brak PDT. Lub zostań w cash account (no PDT) ale wtedy T+1 settlement zablokuje 30-50% trade'ów.
08 Margin vs Cash account — który wybrać Margin (bez używania marginu) wygrywa
Bot oczekuje margin account w Alpaca, ALE bez używania marginu (zero leverage). Powód: omijasz T+1 settlement bottleneck który zabiłby strategy.
T+1 settlement (od maja 2024)
| Cash account | Po SELL'u cash JEST UNAVAILABLE przez 1 dzień roboczy (FINRA rule). Bot zamyka SOFI o 10:00, do BUY o 14:00 → blocked. Strategia z 31 trade'ami/m stałaby 30-50% czasu. |
| Margin account | Broker pożycza unsettled funds bezpłatnie. Cash z SELL'u dostępny natychmiast. Bot nie wykonuje margin orders — używa "as if cash" — ale dostaje T+0 effective settlement. |
Pułapka: free riding rule (cash only)
W cash account: kupisz akcję A za unsettled funds, sprzedasz przed T+1 → 90-day cash restriction. Margin account omija to całkowicie.
Onboarding Alpaca
Wybierz Margin Account w aplikacji (nawet jeśli nie planujesz używać marginu). Wymaga $2k min equity. Test inwestorski: kilka pytań about derivatives experience.
09 Kill switch — emergency stop 1 klik = bot stoi w <30s
Gdy bot wariuje (bug, anomalia, panic) — natychmiastowy stop bez SSH. Plik flag data/KILL_SWITCH + dashboard endpoint.
Jak użyć
| UI | Dashboard | Konfiguracja → 🛑 ZATRZYMAJ BOTA. Confirm dialog. Banner pokazuje stan. |
| CLI | SSH/terminal | touch data/KILL_SWITCH (alternatywa gdy dashboard niedostępny). |
| API | curl | curl -X POST http://<host>/api/kill i /api/unkill |
Co się dzieje gdy active
- Bot wykrywa flag w pętli (max 30s opóźnienia)
- Pomija scan + nowe BUY blocked
- Otwarte pozycje zostają nietknięte — Alpaca trzyma stan
- Stop-lossy NIE są wykonywane (bo bot nie iteruje) — pozycje mogą rosnąć w stratę bez ochrony
- Dashboard pokazuje banner "🛑 KILL SWITCH ACTIVE"
Ostrzeżenie
Kill switch wyłącza wszystko, włącznie ze stop-loss protection. Używaj tylko w nagłych przypadkach. Przy długiej pauzie zamknij ręcznie pozycje przez Alpaca dashboard.
10 LIVE_TRADING_ENABLED — przełącznik paper→live Default paper, dwa zamki przed real money
Bot DOMYŚLNIE handluje na paper-api (zero ryzyka). Przełącz na live wymaga 2 niezależnych zmian — jedna pomyłka literówki ≠ katastrofa.
Aby włączyć live
| 1 | ALPACA_BASE_URL | Zmień na https://api.alpaca.markets (bez "paper-") |
| 2 | ALPACA_KEY / SECRET | Klucze z brokerage dashboard (NIE paper) |
| 3 | LIVE_TRADING_ENABLED=true | Explicite flaga w env. Bez tego bot wymusza paper-api niezależnie od ALPACA_BASE_URL. |
Startup banner gdy live
5-sekundowe opóźnienie + countdown daje czas na "moment, czemu live?" myśl. Plus banner wyraźnie rozróżnialny od paper logu.
11 Sanity checks — pre-BUY safety net 4 warstwy ochrony przed buggy BUY
Przed każdym BUY: 4 niezależne sanity checks. Każdy może zablokować order. Inspirowane Knight Capital (-$440M w 30 min, 2012) — bug w sizing rozsadził firmę.
| 1 | Min price | Cena ≥ $1.00 (no penny stocks). Penny stocks = większy slippage, częste LULD halts. |
| 2 | Max qty hard cap | qty ≤ 10000. Defensywny ceiling — chroni przed bug w alloc/price calc. |
| 3 | Notional range | qty × price musi być w [alloc × 0.5, alloc × 1.5]. Łapie zaokrąglenia / rozsynchronizowanie alloc i price. |
| 4 | Stale snapshot | Real-time quote vs snap price max ±2% odchyłka. Jeśli snap stary o >30s, ceny często się rozjeżdżają. Bot odrzuca BUY zamiast kupować "po nieaktualnej cenie". |
Trigger rate
W normalnej pracy żaden trigger NIE powinien się odpalać (oznaczałoby bug w computacji). Każde "sanity fail" w logu = sygnał do investigation.
12 DB backup — automatyczne snapshoty SQLite Co 6h · retencja 30 dni
Osobny container backup robi sqlite3 .backup co 6h. Atomic, nie blokuje WAL. Stare backupy >30 dni purge automatycznie.
Lokalizacja
| DB live | data/trades.db |
| Backupy | data/backups/trades-YYYYMMDD-HHMM.db |
| Skrypt | backup_cron.sh (alpine container) |
Restore
Co backup chroni
- Trade'y (potrzebne do PIT-38)
- Snapshoty equity (chart historyczny)
- Status (ATH, last_scan, risk)
- Daily digests, scan analysis, cooldowns
Po przejściu na AWS
Container backup zastąpiony przez EBS snapshots via DLM (Data Lifecycle Manager) — zero overhead, snapshot całego volume co 6h, retention auto-managed.
13 PIT-38 — rozliczenie podatkowe (PL) 19% · FIFO/LIFO/HIFO · NBP D-1
Polski rezydent rozlicza zyski kapitałowe z US stocks na PIT-38, stawka 19%. Bot exportuje CSV ready dla urzędu.
Jak liczone
| Matching | FIFO (default), LIFO lub HIFO — wybierasz raz na rok. Każdy SELL "jada" lots z BUY w tej kolejności. |
| Kurs USD→PLN | Średni NBP D-1 (dzień przed transakcją) — zgodne z art. 11a ustawy o PIT. |
| Edge case | Święto NBP (D-1 brak kursu) → fallback na D-2. Implementowane w tax.py. |
W-8BEN — 15% withholding zamiast 30%
Złóż w Alpaca przed funding'iem konta live. Bez tego US-IRS bierze 30% z każdej dywidendy (np. NVDA, JPM). Z W-8BEN: 15% (PL-US tax treaty). Ale zysk z SELL'u (capital gain) idzie do PL — zero US withholding.
Net projection po PIT (z 38m backtest)
14 Production deployment — AWS EC2 + Terraform ~$15/m · Frankfurt · IaC
Target hosting dla live: EC2 t4g.micro (ARM, tani) w eu-central-1 (Frankfurt — najbliżej NYSE w Europie). Terraform module = repeatable, version-controlled.
Stack
| network | VPC + public subnet | Pojedyncza AZ, security group port 22 (SSH) + 443 (HTTPS) z whitelisted IP |
| compute | EC2 t4g.micro | 2 vCPU ARM, 1GB RAM, ~$6/m. Bot zużywa ~150MB RAM = z naddatkiem. |
| storage | EBS gp3 20GB | ~$2/m. SQLite + logs + bars cache |
| backup | DLM snapshots | Auto-snapshot every 6h, retention 30d. Zastępuje lokalny backup container. |
| secrets | SSM Parameter Store | ALPACA_KEY/SECRET encrypted, IAM role na EC2 daje access. Zero plaintext .env w prod. |
| domain | Route53 + ACM | Optional: custom domain + Let's Encrypt cert dla HTTPS dashboard |
| logs | CloudWatch | Bot logs ship out z journald |
Status w todo: INFRA-1 (Terraform module) + INFRA-2 (userdata bootstrap). Deploy: terraform apply + DNS update.
15 Backtesty — walk-forward, compound, crash periods 3 niezależne testy · zakładka Backtest
Strategia walidowana przez 3 różne podejścia. Wszystkie dostępne w zakładce Backtest jako osobne taby z tabelami i wykresami.
| Walk-forward (38m) | Każdy miesiąc out-of-sample — strategia nigdy nie widzi danych testowych. 4 warianty alokacji (baseline, A_dynamic, B_wider, C_pyramid). B_wider winner: +159.75%, max DD -7.71%, 71.3% win rate. |
| Compound vs Flat | 38m, 51 tickerów, 2023-2026. Flat $13k stały: +263%. Compound (alloc rośnie z realized): +1006%, te same trade'y, większe pozycje. DD -5% vs -15%. |
| Crash periods | 3 historyczne korekty: GFC 2008 (-12.7%, +27pp vs SPY), COVID 2020 (+21.3% — V-shape recovery!), Bear 2022 (-16.7%, +3pp). Z halt -15% wszystkie ≤-25% DD. |
Re-run backtestów
Pierwszy run pobiera bars z Alpaca + Yahoo (cache w data/yf_cache/) — re-runs są szybkie.
16 BUY flow — od scan do filled order 5 etapów · Sanity → Limit → WS fill → Market fallback
Pełna ścieżka od momentu kiedy bot widzi dipa do momentu kiedy ma pozycję. Każdy etap ma fallbacki — bot stara się minimalizować slippage, ale nigdy nie blokuje się jeśli któryś krok zawiedzie.
Diagram
(top 30, age <30s)"} WS -->|TAK| MID["mid quote z WS
(in-memory)"] WS -->|NIE| RQ{"REST latestQuote
fresh <60s?"} RQ -->|TAK| MID2["mid = (bid+ask) / 2"] RQ -->|NIE| RT{"latestTrade
fresh <60s?"} RT -->|TAK| TP["trade.p
(bid/ask=null)"] RT -->|NIE| SKIP(["SKIP ticker
NIE kupujemy
na stale data"]) MID --> SC["2. sanity_check_buy"] MID2 --> SC TP --> SC SC --> SC1{"price ≥ $1.50
qty ≤ cap
notional 50-150%
snap vs ask ≤ 2%"} SC1 -->|NIE| BLOCK(["BUY pominięty"]) SC1 -->|TAK| POS["3. place_order_smart"] POS --> TWS{"Trading WS
connected?"} TWS -->|NIE| MKT["market order
direct"] TWS -->|TAK| LIM{"bid + ask
dostępne?"} LIM -->|TAK| ML["marketable limit
BUY: ask × 1.0005
SELL: bid × 0.9995"] LIM -->|NIE| SL["limit @ snap_price
(legacy ~0% fill)"] ML --> WAIT["4. wait_for_fill
(timeout 10s)"] SL --> WAIT WAIT --> RES{"Result?"} RES -->|FILL| OK(["fill_method = limit ✓
target ≤5 bps"]) RES -->|PARTIAL| PART(["cancel rest
partial filled"]) RES -->|TIMEOUT| FB["5. Fallback:
cancel + market"] MKT --> FBOK FB --> FBOK(["fill_method = market
typowy 10-25 bps"]) classDef good fill:#0a2b1e,stroke:#34d399,color:#6ee7b7 classDef warn fill:#2b1f0a,stroke:#fbbf24,color:#fcd34d classDef bad fill:#2b0e14,stroke:#fb7185,color:#fda4af classDef skip fill:#161b22,stroke:#8b949e,color:#8b949e class OK,MID,MID2 good class PART,FBOK warn class SKIP,BLOCK skip
Co może pójść źle
| stale | Stale IEX quote | IEX free plan ma ~3% wolumenu — niektóre tickery mają quote sprzed minut. Quote freshness gate (60s) eliminuje BUY na takie tickery (return None w _parse_snapshot). |
| 2% | Sanity stale snap | Jeśli snap_price odchyla >2% od fresh quote.ask → BUY pominięty. Chroni przed kupowaniem na anomalii cenowej. |
| 10s | Limit timeout | Paper Alpaca symulator nie zawsze fillnie marketable limit (zwłaszcza poza top 30 IEX). Po 10s bot fallbackuje do market — payup ~15-25 bps zamiast 5. |
| PDT | Day-trade tracker | Po 3 day-tradach w 5d bot blokuje SELL (nie BUY). Profit-taking przesuwany na jutro — pozycja zostaje otwarta. |
Live monitoring
W Historia tab widać per-trade:
fill_method badge (LIMIT zielony / MARKET neutral) + Slip column (signed bps).
W Slippage tab — agregaty (avg/max/limit fill rate).
Cele: limit fill rate ≥80% + avg slippage ≤8 bps.
17 Slippage diagnostics — czerwone flagi i fixes Per-trade signed bps · stale data detection
Slippage = różnica między ceną którą bot widział (snap_price) a faktycznym fill. Signed bps cost-positive: BUY paid more = positive (cost), SELL got less = positive (cost). Negative = lucky.
Formuła
Czerwone flagi
| >100 bps | Stale snap | Bot widzi cenę X ale faktyczny rynek to Y. Najczęściej: ticker poza top 30 WS, REST IEX zwraca stale quote. Fix: quote freshness gate (60s) odrzuca takie ticker w scan. |
| 0% limit | Marketable nie fillnie | Paper Alpaca symulator może nie fillnąć limit nawet gdy limit_price > ask. Realny test = live trading. Tymczasem bot fallbackuje do market. |
| avg >15 | Drift długoterminowy | Średnia z 7 dni rośnie → infrastructure issue (latency, WS down). Sprawdź /api/admin/alpaca_pool bench + WS uptime. |
2026-05-04 BA case (real example)
Live widgets
Historia hero panel (DZIŚ): trades count, limit fill rate %, avg slippage bps.
Slippage tab: per-trade table z signed bps + per-ticker avg + daily chart.
Konfiguracja: WS health widgets (Trading + Market) z heartbeat 5s.
18 Architektura — co siedzi pod spodem Bot + Dashboard + Backup w docker
Trzy osobne kontenery dockerowe + jedno volume. Każdy ma minimal scope, można restart niezależnie.
| bot | paper_bot.py | 24/7 loop, scan co 60s, składa orders przez Alpaca REST. Healthcheck: ostatni scan <180s temu. |
| dashboard | dashboard.py | Flask, port 5050. Endpointy: /api/account, /api/positions, /api/trades, /api/snapshots, /api/walkforward, /api/compound_backtest, /api/crash_backtest, /api/config, /api/kill, /api/stream (SSE realtime push). |
| backup | alpine + cron | SQLite .backup co 6h, retention 30d w data/backups/ |
Storage
| SQLite (WAL) | trades, portfolio_snapshots, scan_analysis, daily_digests, cooldowns, bot_status, tax_lots |
| JSON cache | walkforward.json, compound_backtest.json, crash_backtest.json, bars_cache_ideas.json, yf_cache/ |
| Logs | data/bot.log (rotated 10MB × 5) |
External
- Alpaca REST API — orders, positions, account, market data (paper-api lub api)
- NBP API — kursy USD/PLN dla PIT-38 export
- Anthropic API — daily digest LLM (opcjonalne, fallback na deterministic template)
- Yahoo Finance v8 — historical bars dla crash backtest 2008/2020