39875112a0
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.
48 lines
2.1 KiB
Markdown
48 lines
2.1 KiB
Markdown
# 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.
|