Python bot (bot/alpaclaudia): alpaca-py client, wheel strategy (CSP + covered calls) plus equity trailing stops, risk gates (cash buffer, cost-basis guard, per-symbol concentration cap), SQLite state log, Typer CLI (tick/loop/status/ report/dump-state), Discord daily report, pytest suite. Next.js 14 dashboard (dashboard/): read-only — reads the bot's SQLite directly and pulls live account/positions/orders from Alpaca. KPIs, equity chart, positions, bot-intents audit table, and orders table. Dark UI with Tailwind. systemd/: user-unit templates for the polling loop and the post-close report timer. docs/STRATEGY.md: wheel mechanics, risk invariants, later candidates. Defaults to BOT_MODE=dry — nothing is submitted to Alpaca until explicitly enabled in .env. ALPACA_ENV=paper by default; flipping to live requires an explicit second guard.
2.1 KiB
Strategy notes
The Wheel
A short-volatility, cash-secured income strategy. Two phases per underlying:
Phase 1 — no shares held: sell a cash-secured put (CSP). The bot picks a
contract 14-28 DTE (WHEEL_PUT_DTE_MIN/MAX) with absolute delta closest to
WHEEL_PUT_TARGET_DELTA (default 0.30). If the trade's annualised yield falls
below WHEEL_MIN_ANNUAL_YIELD, it's skipped. If delta data is missing (Alpaca
snapshot), it falls back to "closest to WHEEL_PUT_OTM_PCT below spot".
Two outcomes at expiry:
- Put expires worthless → keep premium, repeat Phase 1.
- Put is assigned → own 100 shares per contract at strike, go to Phase 2.
Phase 2 — shares held: for each uncovered 100-share lot, sell a covered
call ~same DTE, same target delta, at or above cost basis (hard floor in
risk.check_covered_call). Two outcomes at expiry:
- Call expires worthless → keep premium, keep shares, sell another call.
- Call is assigned → 100 shares called away at strike (a locked-in gain, because strike ≥ cost basis), back to Phase 1.
Assumption: the underlying is one we'd be happy to own. That's why
WHEEL_UNIVERSE should be small and considered.
Trailing stops
Separate from the wheel — applies to any long equity position. On each tick,
for every long stock position without an existing open trailing-stop order,
the bot submits a trailing_stop sell at TRAILING_STOP_PCT trail.
In practice this is idempotent: duplicates are avoided by the open-order check.
Why not long calls / verticals?
Deliberately kept out of v0. They require managing more Greeks than the wheel
and increase the surface area of risk mistakes. Easy to add under
strategies/ once the wheel is stable on paper for weeks.
Candidates to consider later
- Trailing-stop on short puts once they're deep ITM vs. the underlying's drift — close early and re-roll.
- Cash-sweep into BIL/SGOV for idle cash while waiting for assignments.
- Volatility filter — skip CSPs when IV rank is below a threshold (low premium).
- Earnings blackout — skip CSP entries within N days of earnings.