Oracle3
Oracle3: 예측시장 거래 엔진·MCP 서버
Python으로 27요소 옵션 스크리너, 블랙-숄즈 가격, IV 교차검증, 순방향 보정, 부정 결과 자동 공개 모델 가드 포함
Case study: a 27-factor options screener in Python with Black-Scholes pricing, IV cross-validation, walk-forward calibration, and a model guard that publishes its own negative results.
금융Python · ShellGitHub @Ollie1o1온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
A Python-based options screening tool that identifies high-probability trading opportunities through advanced analytics, institutional-level metrics, and dynamic safety filters. Includes an AI-powered scoring layer that enriches top candidates with qualitative analysis and produces a final weighted ranked list.README 전문 보기 ↗A Python-based options screening tool that identifies high-probability trading opportunities through advanced analytics, institutional-level metrics, and dynamic safety filters. Includes an AI-powered scoring layer that enriches top candidates with qualitative analysis and produces a final weighted ranked list.
A high-quality trade occurs when multiple independent factors align simultaneously:
The screener finds opportunities where all four forces converge. Consistency comes from saying "no" to good volume on a bad chart.
Not every part of this repo is equally proven. These tiers are honest about where each module stands, drawn from the project's own test coverage and validation record.
| Tier | Modules | Why |
|---|---|---|
| Stable | Equity screener core (src/options_screener.py, scoring/filters, Greeks & pricing in utils.py, paper ledger in paper_manager.py) | Exercised daily; covered by the bulk of the test suite. Market data is now provenance-stamped and IV is cross-validated against each contract's mid price (see What You Can Trust Today). |
| Stable (non-predictive) | Long-term holdings desk (src/longterm) | Buy-zone/tranche accumulation bookkeeping and a support-resistance-based DISCOVER scan — deterministic, rule-based math, not a scored prediction. See Long-Term Holdings Desk. |
| Beta | AI ranking layer (ai_rank.py, src/ranking.py), intel briefings (src/intel), sector/asset outlook engine (src/outlook), FastAPI server (src/api.py), Discord/Telegram bots (src/bots) | Functional but lighter test coverage; depends on external LLM/API availability. Predictive edge is still under out-of-sample evaluation. The outlook engine's backtested edge is relative ranking only (IC +0.05–0.08 at 2–3 months); its absolute bearish calls are unreliable (~30% hit rate) and regime-gated — see docs/OUTLOOK_FINDINGS.md. |
| Experimental | Crypto strategist (src/crypto), leverage module (src/leverage), lottery sleeve (src/lottery) | Explicitly pre-validation. The leverage module's own banner reads "PRE-VALIDATION — NO EDGE YET" and shows no edge in walk-forward testing — do not trade it with real money. The lottery engine starts from a documented negative result — with real-IV calibration, blind far-OTM single-pick selection is negative-EV across every configuration tested (see docs/LOTTERY_BACKTEST_FINDINGS.md). The live (menu , ) is built around that honesty: it ranks a board of the wildest long-shot plays (bounce/crash/breakout/catalyst/squeeze) with , flags the subset that clears an evidence bar (cheap IV + reachable strike + a real catalyst/aligned momentum) and the crush-traps it will never pick, and auto-logs top picks into a () that measures hit-rate and — the whole point — whether the -edge subset actually beats the blind base rate. Still pre-validation; tiny size only. |
This tool separates verifiable facts from unproven predictions, and labels each honestly.
A summary of settled questions. It is a snapshot and will go stale; the live
numbers come from reports/TRACK_RECORD.md and the checkpoint reports, which
are generated, not written by hand.
| Question | Verdict | Evidence |
|---|---|---|
| Is real money switched on? | No. | No gate authorises it and no live path is wired. |
| Does the book have a measured edge? | No. | 912 closed. Equal-weighted on capital at risk, PF 1.044, CI [0.87, 1.24] — contains 1. Always name the denominator: on entry premium the same trades give 0.971. |
| Which structure makes money? | Bull Put, and only Bull Put. | Mean return on capital at risk +16.80% [+6.85%, +26.95%], P(highest true mean) = 99.0%. Short Put +0.59%, Long Call −0.18%, Bear Call −4.73%, Long Put −4.96%, Iron Condor −5.33%. |
| Should Bear Call / Iron Condor trade? | No — measured failure, not absence of evidence | Under the exits actually used, Iron Condor needs a 60.1% win rate and delivers 50.0% (−10.1pp over 142 closed); Bear Call needs 66.7% against 59.3% (−7.4pp). |
| Does the scorer rank long calls? | STOP — resolved 2026-07-31, do not re-litigate without new data | n=92 closed, Pearson −0.065, rank IC −0.132, P(true rank IC ≥ 0.08) = 4%. |
| Is the per-contract probability of closing green trustworthy? | Yes — it passed its own out-of-sample guard | Walk-forward over 612 held-out trades: reliability slope 0.572, 95% CI [0.329, 0.815]. With the strategy term removed entirely it is 0.871 [0.577, 1.165], so the discrimination comes from the contract, not the structure label. |
| Does anything predict expected profit per contract? | No. | Predicting return directly: slope 0.368, CI [−0.252, 0.987]. Decomposed as p·W + (1−p)·L: slope 0.442, CI [−0.891, 1.774]. Both refused by the guard; neither is displayed. |
| Does anything order the board? | Not established. | Pre-declared race, 2026-08-24, 612 out-of-sample closed trades, rank IC vs return on risk with days as bootstrap clusters: cal_pop +0.1431 [−0.0420, +0.3277], exp_return +0.0087, quality_score −0.0194, abs_delta −0.0821. Every interval contains zero. |
| Does EV order the board? |
What #1 on the board means. The highest EV after its own trading costs,
among candidates that survived the gates. It is not a claim that #1 wins
most often — no key has earned that, and the one the board sorts by is under a
frozen pre-registration. The board prints this under every comparison table.
Win rate is not profit, and on this book they disagree. Out-of-sample, the
calibrated probability orders win rate cleanly and monotonically while money
does not follow it: the 0.4–0.5 bucket wins 44.3% at PF 0.66 while the 0.3–0.4
bucket wins 36.5% at PF 1.13. Read CalPoP as the probability of closing
green and nothing more; the board says so beside the number.
Entries are drawn at random among gate survivors, by design. A cohort selected by rule X cannot be used to test rule X. So the pick at the top of your board and the trade the book actually enters are deliberately different things, and will stay that way until the pre-registration reads out.
Descriptive layer — verifiable (trust the numbers, mind the staleness). Market prices, IV rank/percentile, spreads, open interest, and Greeks are computed from the live chain. As of the trust-data-integrity work, every contract also carries:
quote_source, quote_as_of, quote_age_min, and a quote_freshness label (fresh / delayed / stale / unknown). Stale quotes are flagged in the output and lightly penalized, never silently served. Aggregate freshness is reported after each scan.IV corrected (yahoo 72% → solved 38%)) and logged.Calibrated layer — validated, and narrow on purpose.
CalPoP sits beside PoP on every board. PoP is the model's theoretical
probability; CalPoP is fitted to what the exit rules actually did on 912
closed trades and checked out of sample. It reads as, for example,
CalPoP 46% — at this level 47% of 137 out-of-sample analogues closed green [39%, 56%], and the table carries a stamp naming the sample, the walk-forward
slope and its CI.
It draws nothing unless a model was fitted, cleared its reliability guard,
and the row lands in a bucket that guard actually checked. There is deliberately
no fallback to the old pop_score — a silent substitution is how quality_score
ended up ranking these boards unnoticed. Refit with
python -m scripts.pop_calibration_report, which prints both reliability curves,
both verdicts, and a per-strategy diagnostic showing which structures the number
orders and which it inverts. A refusal is a successful measurement, not a failed
run: load_model returns None for a refused artifact so it can never reach a
board.
Known limits, printed by that report: within Bear Call and Short Put the ordering still inverts (~50 rows a side, about 1.5 standard errors). Those cells are reported, never gated on — choosing what to display from the same data that measured it fits the display to noise.
Predictive layer — experimental (do not treat as established).
The composite quality_score, the AI final_score, and the ranked order are models under evaluation, not proven edges. The out-of-sample evidence is surfaced live in the output as an evidence banner shaped like Ranking model: EXPERIMENTAL — OOS IC ±0.XX (p=0.XX, n=XX) | gate: <STATE> (n=X/50), followed by a second line giving the walk-forward artifact's own age (flagged once it's over 30 days stale — it only re-runs monthly) and the cohort's Pearson/rank IC side by side. It is read live from the validation artifacts, not hardcoded — it updates as evidence accumulates and will only read READY once the forward-cohort gate fires. For current numbers, see reports/TRACK_RECORD.md (refreshed weekly) and docs/VALIDATION_POWER.md for the power analysis behind the gate.
Audit trail. docs/TRUST_AUDIT_20260803.md walks every decision-feeding number back to the code that produces it, records what reproduced and what did not, and names the defects it found — each one a number that could not change when the thing it described changed. It also carries the CI history (red for weeks, and why), the mypy ratchet, and the module-by-module record of working that debt down, including how to read CI findings without admin rights.
Public track record. The running paper-trading record (win rate, returns, per-strategy breakdown, full closed-trade table, and gate status) is published to reports/TRACK_RECORD.md, refreshed weekly. It states the paper/delayed-data/friction caveats plainly.
Data sources (free, layered).
python -m src.cross_check TICKER joins the two chains per contract and reports IV/mid agreement (live check 2026-06-10: 99% mid / 90% IV agreement on AAPL, disagreements confined to near-expiry far-OTM strikes where IV is ill-defined).config.json → data_archive into data/chain_archive.db once per trading day. This compounds into real per-contract bid/ask/IV/Greeks history for backtesting, replacing model-priced premiums over time.python -m src.av_options --probe checks your key; --backfill SYMBOL --start DATE drains 15+ years of EOD option history into the same archive at ~25 symbol-days/day.src/dolt): real-marks backtester, scorer-slice validator, slippage model, and earnings/IV-crush studies. Honest result so far: on real fills the scorer slice shows no edge and the multi-symbol cohort is roughly breakeven — surfaced display-only via the per-pick Dolt verdict line. Not a live data source for scanning.Signal overlays (free, documented effects; deliberately NOT in scoring while the validation gate gathers).
python -m src.uoa ranks day-over-day OI jumps and volume spikes from the chain archive (Pan-Poteshman: informed trading leaks into option volume). Needs ≥2 archive days.python -m src.insider TICKER... | --archive-symbols scans SEC EDGAR Form 4 open-market purchases; ≥2 distinct insider buyers in 90 days is the documented multi-month signal (Cohen-Malloy-Pomorski). Sells are shown but never scored.python -m src.worldnews aggregates ~500 headlines/run from 200+ publishers (Google News/CNBC/MarketWatch RSS + StockTwits crowd tags) into a trust- and recency-weighted pulse with bull/bear split, confidence, per-theme breakdown, and the verified FOMC/CPI/NFP risk-event lookahead. A one-line summary prints under the regime dashboard at startup. Honest framing: news at retail speed times risk, not direction.Probability Lab — the market's implied distribution vs your view (descriptive, not gated).
python -m src.probability_lab AAPL --drift +3% --vol-mult 0.9 (or menu [12] PROB LAB) reads the risk-neutral density the option chain implies — the market's full probability distribution for where the stock lands at expiry — by taking the Breeden-Litzenberger second derivative (q(K) = e^{rT} ∂²C/∂K²) of the call curve reconstructed from the fitted SVI smile. You then tilt it into your own view with a directional drift and a vol multiplier; the tilt re-centers and re-scales the market's own density, preserving its skew rather than replacing it with a Gaussian. It prints: the market vs your P(S_T > level) at each key level (the edge column is where you disagree with what's priced), and the listed long call / long put / call- and put-debit verticals ranked by EV under your view, each with an EV(mkt) contrast column. This is arithmetic on the live chain plus a stated view — it sits in the descriptive layer alongside IV rank and Greeks, not the predictive gate. EV(mkt) ≈ 0 on a structure means you're buying consensus; a positive EV(view) with a negative or near-zero EV(mkt) is where your view is actually differentiated. Model caveats are printed with every run: the RND is smoothed off delayed data, and the view EV is only as good as your view.
Prerequisites: Python 3.11+, pip.
CI runs the suite on 3.11 and 3.12; it is also developed and run daily on 3.14. 3.10 is not tested — the code may well run on it, but nothing here verifies that.
git clone https://github.com/Ollie1o1/options.git
cd options
python3 -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements-lock.txt # pinned, reproducible — recommended
Install from requirements-lock.txt (frozen, exact versions) rather than the loose requirements.txt. yfinance and several other dependencies routinely break between minor/major versions, so the pinned lock file is the only reproducible install path; requirements.txt is a loose fallback for development only.
The technical screener works out of the box — no API keys needed (uses Yahoo Finance). The AI layer requires an OpenRouter API key (see Setup).
Optional — Polygon.io enrichment: Set POLYGON_API_KEY in your .env file to enable higher-quality ticker-filtered news, real-time VWAP, and unusual options flow detection. Free API keys are available at polygon.io/dashboard/signup. The screener runs identically without this key.
Optional — SEC EDGAR contact: the insider-buys overlay calls SEC EDGAR, which asks every client to identify itself with a contact address. Set SEC_EDGAR_CONTACT in your .env (e.g. [email protected]) to send yours. Unset, the request still identifies the software and the SEC may throttle or refuse it — the overlay fails soft and the screener is unaffected either way.
Important: Always run from the project root directory (
cd options).
python run.py
That is the front door. It finds the right interpreter, sets the working
directory, opens the launcher menu, and forwards every flag through to the
screener — so python run.py -ds and python run.py --ticker AAPL --auto
work the same way. Nothing else needs activating first.
If something misbehaves, ask the install to describe itself:
python run.py # then: [7] SETTINGS → [2] DOCTOR
python -m src.doctor # or run the same checks directly
The doctor is read-only. It checks your Python version, dependency drift
against requirements-lock.txt, network reachability, directory and database
writability, config.json validity, and whether the scheduler is actually
running — and prints a one-line fix beside anything that is not healthy.
These all still work; none of them is the one to learn first.
| Entry point | What it is for |
|---|---|
run.sh / run.bat | Platform wrappers around run.py (shell / Windows) |
python -m src.launcher | The launcher menu directly, skipping run.py's interpreter handling |
python -m src.options_screener | The screener directly, skipping the launcher menu |
start_all.py | Starts the API and bots, not the interactive screener |
python -m src.check_pnl | Portfolio viewer |
python -m src.catalyst | Pharma catalyst calendar (see below) |
streamlit run src/dashboard.py | Web dashboard |
Dated Phase 2/3 clinical events for US-listed biotechs between $50M and $10B, sorted by date. Each row carries cash runway (is the company funded through its own catalyst?), the trial's amendment history, the options-implied move for the expiry spanning the date, and a phase base rate.
PYTHONPATH=$PWD ~/.venvs/options/bin/python -m src.catalyst --window 6m
PYTHONPATH=$PWD ~/.venvs/options/bin/python -m src.catalyst --funded-only
PYTHONPATH=$PWD ~/.venvs/options/bin/python -m src.catalyst --limit 80
PYTHONPATH=$PWD ~/.venvs/options/bin/python -m src.catalyst ANNX
PYTHONPATH=$PWD ~/.venvs/options/bin/python -m src.catalyst --mark
Only the soonest --limit names (default 40) get the per-ticker network
lookups, because runway, amendments and chains each cost a request. A six-month
Ph2+Ph3 window resolves ~96 names, so the default truncates — and the board
says so explicitly (showing 40 of 96 names — 56 not fetched) rather than
leaving you to assume you are seeing everything.
Sources are free and keyless: ClinicalTrials.gov v2 for the sweep, its internal
history endpoint for amendment flags, EDGAR XBRL for cash and burn, Yahoo for
caps and chains. Set SEC_EDGAR_CONTACT to your email so SEC requests identify
themselves.
Read the board with three things in mind:
~2026-11 (est, month).Every event is written to data/catalysts.db with first_seen, and --mark
re-observes elapsed events, so the calendar can eventually be checked against
what actually happened rather than merely trusted.
Platform support: the screener itself runs on macOS, Linux and Windows.
Scheduled automation is macOS-only — it uses launchd, and the plists in
scripts/ install user LaunchAgents. On Linux and Windows nothing runs on a
timer: auto-logging, exit checks, the weekly checkpoint and the track-record
refresh only happen when you open the app, which is a supported way to run it
but means the cohort fills more slowly and exits are checked less often. See
the stop-overshoot note in reports/TRACK_RECORD.md for what that costs.
options shell command (optional)run.py already sets its own working directory, so you can wrap it in a one-line
shell function and launch the screener from any directory. Add to ~/.zshrc
(or ~/.bashrc):
options() { python3 "$HOME/Projects/options/run.py" "$@"; }
Then source ~/.zshrc once and run:
options # opens the menu (auto-activates venv)
options -ds # discovery scan, auto-log top 5
options --ticker AAPL --auto --no-ai # single-ticker, unattended
All run.py flags (including the -ds/-sps/-ics/-ss shortcuts and --logging-help) pass straight through.
Why does this matter? The project has ~30 dependencies (pandas, openai, rich, etc.). Invoking a module directly with a bare
python3uses your system interpreter, which does not have them — you get missing-module errors, no colors, and broken API calls.run.pyresolves the interpreter for you, which is why it is the documented front door. If you would rather manage that yourself, install fromrequirements-lock.txt(notrequirements.txt) and runpython -m src.doctorto confirm the versions match.
# Interactive technical screener (CLI)
python -m src.options_screener
# Streamlit web dashboard
streamlit run src/dashboard.py
# AI-enhanced ranked output for specific tickers
python ai_rank.py AAPL TSLA NVDA
# AI ranking with full reasoning shown per pick
python ai_rank.py AAPL --detail
# Skip AI entirely — technical scores only, no API key needed
python ai_rank.py AAPL --no-ai
# Paper trade portfolio viewer
python -m src.check_pnl
# Walk-forward backtest
python -m src.backtester AAPL SPY NVDA
# Morning briefing — market-wide quant morning note (self-contained HTML)
python -m src.morning --open
Commands in this section assume the venv is activated. If not, either activate it (
source venv/bin/activate) or use the auto-venv launchers above.
Running python3 run.py or python3 -m src with no arguments doesn't drop you straight into the equity screener — it opens a top-level menu (src/launcher.py) covering everything in this repo:
[1] STOCKS equity options — discover / spreads / iron / sell
[2] CRYPTO BTC/ETH options on Deribit + perp funding/basis
[3] LEVERAGE BTC/ETH perp futures strategy [no edge yet]
[4] RESEARCH breakout · vol-intelligence · equity-VRP [read-only]
[5] HOLDINGS long-term stock accumulation — buy zones · tranches · TFSA book
[6] STRATEGIES your setups — signal · account · friction · evidence · verdict [display-only]
[7] SETTINGS theme · preferences
[Q] QUIT
[1] STOCKS is the technical screener documented below — everything from Scan Modes through Weight-Profile Auto-Logging.[2] CRYPTO is the Crypto Strategist.[3] LEVERAGE is the Leverage Module — explicitly pre-validation, tagged no edge yet in the menu itself.[4] RESEARCH opens the Research Tools submenu (breakout / vol-intelligence / equity-VRP), all read-only, no scores or trades.[5] HOLDINGS is the Long-Term Holdings Desk.[6] STRATEGIES is the setup library: rules of the form "when these
conditions hold, sell this option", each marked with its hypothesis, signal
conditions, capital requirement, which account it can legally be traded in,
its evidence and its verdict. Every row carries a FRICTION column — the
round-trip crossing cost as a share of the credit, measured from the ledger's
own mid-vs-crossed fills and checked against auto_log.max_friction_to_credit
— because the cost wall, not the signal, is what has killed most of this
book's edge. Registered accounts cannot sell naked options, so every setup
declares its accounts and a test refuses to file a naked setup under TFSA. The
library includes deliberate controls — an unselected benchmark, two null
controls and a known-negative tripwire — because selectivity made this book
worse monotonically, so a signal has to beat doing nothing clever before it
is believed. Display-only. Dead setups are kept, not deleted.[7] SETTINGS picks a color theme (Quant Desk / Cyberpunk Neon / Matrix Terminal / Amber CRT) — persisted to and applied on every future run, interactive or scripted.If you pass any CLI flag (--ticker AAPL, --mode discover, --default-scoring, --enforce-exits, etc.), the launcher is bypassed entirely and the flag is forwarded straight to the equity screener — this is what keeps cron jobs and every historical command in this README working unchanged. The menu is purely an opt-in front door for interactive sessions; the python -m src.options_screener [OPTIONS] and python3 run.py [OPTIONS] forms shown throughout this doc always go directly to the equity screener, with or without the launcher.
Launch with python -m src.options_screener (venv active) or python3 run.py (auto-venv) and enter one of these commands at the prompt:
| Command | Mode | What it does |
|---|---|---|
AAPL (any ticker) | Single-stock | Deep analysis of one symbol across all expiries |
ALL | Budget scan | Multi-ticker scan filtered to a per-contract dollar budget |
DISCOVER | Discovery | Scans the top 100 most-liquid tickers, no budget limit |
SELL | Premium Selling | Short put candidates ranked by return-on-risk |
SPREADS | Credit Spreads | Bull Put and Bear Call spread opportunities — full detail cards with thesis, breakevens, top components, execution guidance + comparison table |
IRON | Iron Condors | Delta-neutral range-bound strategies — same detail-card pipeline as singles + spreads |
PORTFOLIO | Portfolio | View P/L on all open paper trades |
MY LIST | Watchlist | Scan your personal watchlist (type ADD AAPL to build it) |
LOTTERY | Lottery Ticket | Far-OTM long-shot board (bounce/crash/breakout/catalyst/squeeze) — experimental, negative-EV base rate, see Module Maturity |
INTEL | Intel Briefing | Market overview, single-ticker briefing, macro pulse, and the morning briefing |
SQUEEZE | Short Squeeze | Short-squeeze setups ranked by short interest + unusual call activity — display-only, not scored into ranking. Backtested 2018–2026 on point-in-time FINRA data: the signal is real and out-of-sample robust, but it lives in the short-interest level, not the multi-factor score — see docs/SQUEEZE_BACKTEST.md |
A market-wide quant morning note — regime, vol intelligence, macro calendar, signals, portfolio state, and gate status — as a self-contained HTML page (light/dark, reproducible offline from its JSON sidecar).
python -m src.morning --open # build and open
python -m src.morning --from reports/briefings/2026-07-10.json # offline re-render
Also available in the screener via INTEL → [d] morning briefing. The
maintenance heartbeat writes a fresh pair once per business day to
reports/briefings/YYYY-MM-DD.{html,json} when the headless LaunchAgent runs.
python -m src.options_screener [OPTIONS] # requires venv active
python3 run.py [OPTIONS] # auto-activates venv
python3 -m src [OPTIONS] # auto-activates venv
Options:
--no-color Disable ANSI color output (useful for piping to a log file)
--no-ai Skip AI analysis after scan
--close-trades Update the trade log with closing prices and realised P/L
--enforce-exits Run exit-rule enforcement on paper_trades.db and exit (for cron)
--ui Launch the Streamlit web dashboard
--mode MODE Skip mode menu: ticker | all | discover | sell | spreads | iron |
portfolio | mylist | lottery | squeeze
--ticker SYM Ticker symbol (implies --mode ticker)
--watchlist NAME Use named watchlist (liquid_large_cap, sector_etfs, high_iv, income)
--top N Cross-ticker top-N scan (default 10), grouped by DTE bucket
--export csv Export top scan results to scan_results_YYYYMMDD_HHMM.csv
--auto Skip interactive prompts, use config defaults
--compact Compact per-pick output (3 lines per pick)
--no-cache Disable all caching (requests, AI scores, IV history)
--tearsheet N Write a self-contained HTML tearsheet for pick #N after the scan
--no-tearsheet Suppress the post-scan tearsheet prompt
--weights NAME Weight profile name (in configs/weights/) or path to JSON; tags logged trades
--auto-log Auto-log top-N picks after scan (skips save-menu prompt)
--log-top N With --auto-log: how many top picks to log (default 5)
--min-dte N Override minimum days-to-expiration for this scan
--max-dte N Override maximum days-to-expiration for this scan
--list-profiles List available weight profiles and exit
--surface Show 3D P&L risk surface for the top pick (single-stock mode)
--surface-mode M Render mode: braille (default, hi-res Unicode) or ascii
--surface-greek G Show greek sensitivity surface: delta, gamma, vega, theta
--no-contours Disable contour lines on surface
--viz Open interactive 3D visualizer (Plotly, opens in browser)
-h, --help Show help and exit
--version Show version string and exit
The CLI renders a responsive, colour-coded interface that adapts to your terminal width (60–120 chars). The display follows a deliberate house style (src/ui.py + src/formatting.py semantic theme):
Startup
Report — per pick
━ #1/3 MSFT CALL $480 exp 2026-07-17 36d ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
★★★★★ 0.84 Prem $11.30 IV 42.0% OI 8,450 Vol 1,240 Δ +0.45 OTM
VERDICT POSITIVE EV +88/ct ← bright (green = +EV)
DO BUY 1 @ $4.24 · tgt $6.36 (+50%) · stop $3.18 (-25%)
Liquidity Vol: 1240 OI: 8450 Spread: 2.1% ($0.09) — tight Cost: $1,130.00
Greeks Δ +0.45 Γ 0.012 V 0.31 Θ -0.08 V$: $31
Valuation IV: ████████░░░░░░░░ 41%ile PoP 66.0% RR 2.1x EV $88.00 (+21.0%)
Flow PCR: 0.82 Momentum: RSI 58, 5d 2.3% Sentiment: Bullish
Quant read POSITIVE EV after cost: net EV +88/contract
▸ Thesis High probability (>65%) • Strong R/R (2.1x)
▸ Plan Entry ≤$4.24 → Target $6.36 (+50%) / Stop $3.18 (-25%)
Breakeven: $484.24 Max Loss: $424 Confidence: HIGH (100%)
░ depth Vanna/Charm · GEX flip · scenario grid (dimmed)
The VERDICT / DO decision zone consolidates the EV-after-cost read and the entry/target/stop plan into the two lines the eye lands on first; the detailed Thesis/Plan/Exec/scenario rows remain below for depth.
Comparison table — compact ranked summary after detailed output (EV sign-coloured, SVI cheap/rich; all other columns neutral):
─ QUICK COMPARISON — sorted: Score ──────────────────────────────────────────
# Ticker Strike Exp Score PoP R/R IV% Δ ν EV Sprd SVI
1 MSFT $480C 07/17 0.84 [HI] 66% 2.1x 41% +0.45 0.31 $ +88 2.1%
2 NVDA $190C 07/17 0.78 [HI] 62% 2.1x 41% +0.45 0.31 $ +31 2.1% CHEAP
3 AAPL $210P 07/17 0.55 [HI] 51% 2.1x 41% -0.38 0.31 $ -12 2.1%
Concentration panel — after the comparison table, a single panel aggregates direction-aware net Greeks and flags when the basket is really one bet (net delta / net vega / single underlying), folding in high price-correlation pairs.
3D Risk Surface — high-resolution braille or ASCII surface (activated with --surface):
3D P&L Risk Surface — AAPL $215 CALL
Price shock: -25% <- -> +25% | IV shock: -50% <- -> +50%
P&L range: $-4.50 to $+18.20
[Braille-rendered isometric 3D surface with truecolor gradient]
[Contour lines: white = breakeven, yellow = iso-value levels]
Legend: ████████████████████████████████ $-5 to $+18
Supports P&L and Greek sensitivity surfaces (--surface-greek delta|gamma|vega|theta).
Uses Unicode braille characters (2x4 dots per character) for ~8x pixel density vs ASCII.
Falls back to ASCII shading automatically if the terminal doesn't support Unicode.
Interactive 3D Visualizer — browser-based multi-view dashboard (activated with --viz or [V] at the post-scan menu):
Six tabbed views, all interactive with rotation, zoom, pan, and hover tooltips:
| Tab | What it shows |
|---|---|
| Contract Explorer | 3D scatter of all screened contracts. Four dropdown menus remap X/Y/Z axes and colour to any of 20 numeric fields (moneyness, DTE, score, IV, Greeks, EV, volume, etc.). Top picks highlighted as gold diamonds. |
| IV Surface | SVI-fitted implied volatility surface with market IVs overlaid as scatter points. Points coloured by residual: blue = CHEAP vs surface, red = RICH. |
| Greek Landscape | Delta/gamma/vega/theta sensitivity surfaces for the top pick. Toggle between Greeks with buttons. Full BS repricing across price and IV shocks. |
| P&L Scenarios | P&L surface for top 3 picks across ±25% price and ±50% IV shocks. Red = loss, green = profit. Breakeven plane at $0. Toggle individual contracts. |
| Score Decomposition | Parallel coordinates of 12 most-weighted score components. Drag on any axis to filter contracts. Lines coloured by quality score. |
| Risk Radar | Spider chart comparing top 5 contracts across 7 risk dimensions (PoP, EV, Liquidity, IV Edge, VRP, Momentum, Greeks). |
Dark financial theme. No extra dependencies — uses Plotly (already in requirements). Opens as a standalone HTML page in your default browser.
Stress test — 7×3 scenario matrix with full Black-Scholes repricing:
PORTFOLIO STRESS TEST — 3 open position(s) [full BS repricing]
IV Shock -20% -10% -5% +0% +5% +10% +20%
IV flat -1,245 -580 -267 +0 +215 +390 +640
IV +10% -1,400 -735 -422 -155 +60 +235 +485
IV +20% -1,555 -890 -577 -310 -95 +80 +330
ai_rank.py runs the technical screener then sends candidates through a two-pass AI analysis. The AI enriches each candidate with qualitative context that the quant model can't see — news sentiment, catalyst proximity, IV justification, and directional bias — then produces a combined final score.
Two-pass architecture:
What the AI scores:
ai_score (0–100), ai_confidence (0–10), catalyst_risk (low/medium/high), and reasoningSame-day cache: Scores are cached in a local SQLite database (.ai_score_cache.db) keyed on symbol, strike, expiry, and IV regime. Re-running on the same tickers within the same trading day skips the API for already-scored contracts.
The AI layer uses OpenRouter by default, which gives you access to dozens of models through a single API key. You can also point it directly at the Anthropic API.
These models are free with an OpenRouter account and no credit card required:
| Model | ID for config_ai.py | Role | Notes |
|---|---|---|---|
| Ling 3.0 Flash (primary) | inclusionai/ling-3.0-flash:free | Primary model | Fast, good reasoning |
| Nemotron 3 Ultra | nvidia/nemotron-3-ultra-550b-a55b:free | 1st fallback | Large model, strong analysis |
| Laguna S 2.1 | poolside/laguna-s-2.1:free | 2nd fallback | Strong structured output |
The free tier is rate-limited. If you hit 429 errors, the screener retries automatically with exponential backoff and switches to the fallback model. For production use, add a small OpenRouter credit balance to lift rate limits.
Adding credit to your OpenRouter account ($5–10 goes a long way) unlocks faster, higher-quality models:
| Model | ID for config_ai.py | Cost | Notes |
|---|---|---|---|
| Claude Sonnet 4.6 | anthropic/claude-sonnet-4-6 | ~$3/M tok | Best reasoning on options context |
| GPT-4o | openai/gpt-4o | ~$5/M tok | Strong; good at structured JSON output |
| Claude Haiku 4.5 | anthropic/claude-haiku-4-5 | ~$0.80/M tok | Fast and cheap; good enough for screening |
| DeepSeek V3 | deepseek/deepseek-chat | ~$0.27/M tok | Excellent value; strong analyst-style output |
If you prefer to use your Anthropic API key directly (bypassing OpenRouter):
# in src/config_ai.py
AI_CONFIG = {
"provider": "anthropic",
"model": "claude-sonnet-4-6",
"api_key_env": "ANTHROPIC_API_KEY",
...
}
To change model at any time, edit the model field in src/config_ai.py. The fallback_model field is used automatically after 2 failed retries.
Create a free account at openrouter.ai and copy your API key from the dashboard.
Create a .env file in the project root (it is gitignored — never committed):
OPENROUTER_API_KEY=sk-or-v1-your-key-here
# Optional: per-model keys for specialised models
# OPENROUTER_LING_KEY=sk-or-v1-...
# OPENROUTER_NVIDIA_KEY=sk-or-v1-...
# OPENROUTER_POOLSIDE_KEY=sk-or-v1-...
# Optional: Polygon.io for higher-quality news, VWAP, unusual flow
# POLYGON_API_KEY=your_polygon_key_here
See .env.example for the full template (also includes Discord/Telegram bot tokens).
That's it. The screener loads the key automatically from .env at startup (works with or without python-dotenv installed).
If you prefer Anthropic directly, set
ANTHROPIC_API_KEY=sk-ant-...in.envand updateprovider+api_key_envinsrc/config_ai.py.
# Score and rank candidates
python ai_rank.py AAPL
python ai_rank.py AAPL TSLA NVDA
# Show full AI reasoning for each pick
python ai_rank.py AAPL --detail
# Run AI portfolio coherence check on top 5 picks
python ai_rank.py AAPL MSFT TSLA --portfolio-check
# Change screener mode
python ai_rank.py --mode "Premium Selling" SPY QQQ
python ai_rank.py --mode "Credit Spreads" AAPL MSFT
# Control DTE window
python ai_rank.py --dte-min 14 --dte-max 45 AAPL
# Set a per-contract budget cap (Budget scan mode)
python ai_rank.py --mode "Budget scan" --budget 500 AAPL MSFT GOOG
# Change trader profile (adjusts scoring weights)
python ai_rank.py --profile position SPY # swing | day | position
# Adjust how much the AI score matters (default: 0.30)
python ai_rank.py --ai-weight 0.5 AAPL # 50% AI, 50% technical
# Skip AI entirely — rank by technical quality_score only (no API key needed)
python ai_rank.py --no-ai AAPL TSLA
# Show top 10 instead of default 20
python ai_rank.py --top 10 AAPL
# Save ranked results to a file
python ai_rank.py AAPL --output ranked.csv
python ai_rank.py AAPL --json ranked.json
# Quiet mode (suppress screener progress output)
python ai_rank.py AAPL --quiet
# Debug logging for the AI scorer
python ai_rank.py AAPL --verbose
python ai_rank.py [TICKERS ...] [OPTIONS]
Positional:
TICKERS One or more ticker symbols (e.g. AAPL TSLA SPY)
Screener:
--mode MODE Screener mode. Choices:
Single-stock (default), Budget scan, Discovery scan,
Premium Selling, Credit Spreads, Iron Condor
--dte-min DAYS Minimum days to expiration (default: 7)
--dte-max DAYS Maximum days to expiration (default: 45)
--budget $ Per-contract budget cap (Budget scan mode only)
--expiries N Max expiration dates fetched per ticker (default: 8)
--profile PROFILE Trader profile: swing (default) | day | position
AI scoring:
--no-ai Skip AI — rank by technical quality_score only
--ai-weight W AI contribution to final_score, 0–1 (default: 0.30)
--top N Candidates to display in ranked table (default: 20)
--detail Show full AI reasoning for each pick
--portfolio-check Run AI portfolio coherence check on top 5 picks
Output:
--output FILE Save ranked results to CSV
--json FILE Save ranked results to JSON
--quiet Suppress screener verbose output
--verbose Enable debug logging for the AI scorer
Tickers
│
▼
[Technical Screener]
│ src/options_screener.py
│ - Fetches options chains + 1-year daily history per ticker
│ - Calculates Greeks, PoP, EV, IV rank, HV, momentum, warnings
│ - SVI IV surface fitting → mispricing detection
│ - Scores each contract -> quality_score [0, 1]
│
▼
[Pass 1 — Ticker Context]
│ src/ai_scorer.py
│ - Per symbol: IV regime, earnings date, implied vs. historical move,
│ term structure, news sentiment, short interest, headlines
│ - Returns: regime, catalyst_risk, directional_bias, summary
│
▼
[Pass 2 — Contract Scoring]
│ src/ai_scorer.py
│ - Narrative context built per contract (IV rank narrative, seller/buyer
│ edge, PoP strength, RR quality, theta burn, score drivers, warnings)
│ - Ticker summary from Pass 1 prepended to each contract prompt
│ - Batches candidates (default 3 per API call)
│ - Model fallback chain: ling → nemotron-ultra → laguna-s
│ - Returns: ai_score (0-100), ai_confidence (0-10), reasoning, flags,
│ catalyst_risk, iv_justified
│ - Results cached in .ai_score_cache.db for the trading day
│
▼
[Ranking]
│ src/ranking.py
│ final_score = tw * quality_score + aw * (ai_score / 100)
│
│ ai_weight (aw) is dynamic per row:
│ base_aw (0.30) x VIX regime mult x AI confidence mult x liquidity adj
│ capped at 0.55
│
│ Divergence flagged when |ai_score/100 - quality_score| > 0.20
│
▼
[Rich Table]
- Summary panel: pick count, avg scores, divergence count, top pick
- Colour-coded table: scores, types, DTE, PoP, confidence, catalyst risk
- Alternating row stripes, top-3 highlighting
- Divergence detail panel for flagged picks
- Falls back to plain text if rich is not installed
| Column | Description |
|---|---|
| Tech | Technical quality score × 100 (0–100). Pure quant — Greeks, PoP, EV, liquidity, momentum |
| AI | AI score (0–100). Qualitative — catalyst, IV justification, narrative context |
| Conf | AI confidence in its own score (0–10). Higher = more AI weight applied |
| Final | Weighted blend: tw × Tech/100 + aw × AI/100. This is the ranking signal |
| Cat | Catalyst risk: LOW / MED / HI!. Earnings or macro events near expiry |
| Div | Divergence: AI>TECH (AI bullish vs quant) or TECH>AI (quant bullish vs AI). Highlighted in cyan/magenta |
| Reasoning | Short flags in normal mode; full 1–2 sentence AI rationale with --detail |
Divergence is the most actionable signal — when the two models strongly disagree, it's worth reading the reasoning before trading.
PoP is arguably the most important metric in the screener. Unlike simple "Delta" (which estimates the probability of the option being 1 cent In-the-Money), PoP calculates the probability that the trade will be profitable at expiration after accounting for the premium paid.
Why don't I see 50%+ PoP? If you are buying "Out-of-the-Money" (OTM) options, you are intentionally taking a low-probability, high-payoff bet. To see 50%+ PoP, switch the screener to "SELL" (Premium Selling) mode or buy Deep In-the-Money (ITM) options.
Each contract is scored across 27 weighted factors including:
| Factor | What it measures |
|---|---|
| Probability of Profit | Blended Black-Scholes + Monte Carlo PoP |
| Expected Move Realism | How achievable the breakeven is vs. the 1σ expected move |
| Risk/Reward | Payoff at 0.75× EM target vs. premium paid |
| Momentum | RSI, 5-day return, ATR trend, confluence scoring |
| IV Rank | IV percentile vs. 30-day range; buyers rewarded for low IV |
| IV vs. HV Edge | HV-adjusted EV — positive = options cheap vs. realised vol |
| IV Mispricing | SVI surface residual — contract rich or cheap vs fitted vol surface |
| Liquidity | Volume + open interest, rank-normalised |
| Catalyst | Crush-magnitude-adjusted earnings penalty |
| Theta Efficiency | Time decay pressure relative to delta |
| Skew Alignment | Put/call IV skew directional bias |
| Gamma/Theta Ratio | Explosive payoff potential per unit of daily bleed |
| Expected Value | HV-adjusted BS value minus market price minus spread cost |
Scores are further adjusted for trend alignment, decay risk, gamma squeeze setup, OI wall proximity, macro risk, seasonal win rate, and VIX regime multipliers.
VIX regime adaptation — scoring weights shift automatically based on the current VIX level (low / normal / high), emphasising different factors in different volatility environments. The AI weight also scales with VIX regime (×0.80 in low VIX, ×1.30 in high VIX).
All Greeks are computed analytically via Black-Scholes with continuous dividend yield, vectorised with NumPy across the full chain in a single batch: delta, gamma, vega, theta, rho, charm, vanna.
Probability of Profit is blended: 60% Monte Carlo (Merton Jump Diffusion GBM, captures path-dependency and tail risk) + 40% analytical PoP, using numpy.random.default_rng for reproducible, thread-safe results.
For each pick the screener generates:
exit_rules in config.jsonLog any pick directly from the CLI and track it going forward:
exit_reason, full multi-leg columns, paper_only, and era)long_strike, spread_width, net_credit) and iron condors (short_put_strike, long_put_strike, short_call_strike, long_call_strike, net_delta) round-trip through the same log/exit/portfolio path as singlescost_to_close = Σ(-qty × leg_price) drives TP/SL/Time exits (no more intrinsic-fraction approximation)python -m src.options_screener --close-tradespython3 run.py --enforce-exitspython -m src.check_pnl (or press 7 / PORTFOLIO at the mode menu) — viewer runs exit enforcement before displayFiltering the portfolio view. The book was restarted on 2026-08-05. The viewer splits on that frozen date rather than on the old era / calibration-cohort tags, which carved up trades scored under superseded models:
python -m src.check_pnl --current # current book: 2026-08-05 onward + every open position
python -m src.check_pnl --before # closed history from before 2026-08-05
python -m src.check_pnl --period current | --period before
python -m src.check_pnl --menu # interactive view chooser (Current / Before / All)
python -m src.check_pnl # whole book (default)
The interactive [7] PORTFOLIO menu offers the same choices, defaulting to the current book: [C] current · [B] before 2026-08-05 · [A] all trades.
--current deliberately includes every still-open position regardless of entry date — most open positions predate the restart, and filing them under history would hide live exposure. --before is therefore closed trades only. The cutoff is a frozen constant (check_pnl.BOOK_RESTART_DATE), not date.today(): a rolling date would empty the current book every morning.
The underlying era column is unchanged, and python -m src.portfolio_eras still reports the pre-data vs finalized process split — that's separate evidence from what the viewer shows.
Flat take_profit=50% / stop_loss=-25% doesn't work for options: it's too tight for short premium (whipsaws on normal IV noise) and misses obvious defensive signals (short strike going ITM). The enforcement layer uses strategy-aware rules from config.json → exit_rules:
Short single-leg (short calls / short puts) — first trigger wins:
| Priority | Trigger | Rule |
|---|---|---|
| 1 | Take profit (DTE-tiered) | 50% @ ≥21 DTE · 35% @ 7–21 DTE · 25% @ <7 DTE |
| 2 | Time exit | DTE ≤ 21 and days-held ≥ 3 |
| 3 | Stop — strike breach | Spot crosses the short strike (unambiguous defensive signal) |
| 4 | Stop — premium multiple | Current premium ≥ 2.0× entry |
| 5 | Stop — delta multiple | abs(current Δ) ≥ 2.5 × abs(entry Δ) (early warning; uses stored entry_delta + entry_iv) |
Credit spreads & iron condors (defined risk, real per-leg marks):
| Trigger | Rule |
|---|---|
| Take profit | 50% of credit received (raw P/L before friction) |
| Stop loss | Position worth 2× credit (i.e. raw loss = 1× credit) |
| Time exit | DTE ≤ 21 and days-held ≥ 3 |
Friction model: per-close costs are (2 × slippage + 2 × commission/100) × n_legs, plus currency conversion on the premium moved, subtracted from raw P/L before persisting pnl_pct. Stored exit_price = cost-to-close so closed multi-leg trades can be re-derived.
Every fee is documented in docs/BROKER_COSTS.md — what the broker charges, which costs are modelled, which are knowingly not, and how the bid-ask figures were measured rather than assumed. Configured in config.json → paper_trading; nothing is hardcoded. Run python scripts/cost_model_report.py to see the book re-priced on real costs.
Long single-leg:
| Trigger | Rule |
|---|---|
| Take profit | +100% premium or delta ≥ 0.80 (deep ITM) |
| Stop loss | −50% premium |
| Time exit | 21 DTE |
Which rule fired is persisted in the exit_reason column on close.
Collect optimization data for the 27 composite weights by running the screener under different weight configurations and auto-logging the best picks. Every logged trade is tagged with a weight_profile column so you can later compute per-profile win rate, IC, and P&L and pick the best weight set.
python3 run.py --default-scoring
(or the alias -ds, or the long form below — all three are equivalent.)
Change the top-N count inline — append --5, --10, --25 (any positive integer) to override --log-top:
python3 run.py --default-scoring --10 # log top 10 instead of the default 5
python3 run.py -ds --25 # log top 25
python3 run.py --5 --mode mylist --weights baseline --auto-log --auto # use --5 on its own
Under the hood this expands to, and auto-activates the venv for you:
python -m src.options_screener --mode discover --weights baseline --auto-log --log-top 5 --auto
It:
config.json's composite_weights — the control).paper_trades.db. Not by quality_score — that was removed as a sort key (its top quintile is the worst cell in the ledger: 31.6% win rate, −19.9% return on capital, against +5.2% for the [0.55, 0.65) bucket; Wilcoxon p=0.89 at the #1 slot). It survives only as a tie-break. Boards are ordered by EV net of trading cost, and the entries themselves are drawn at random among gate survivors so the resulting cohort can test the ordering — a cohort selected by rule X cannot test rule X.--auto): runs end-to-end unattended.Each auto-logged trade is identical to a manually-logged one — it appears in the portfolio view (python -m src.check_pnl or 7 at the mode menu), contributes to portfolio Greeks/stress test, and can be closed with --close-trades.
Live since 2026-08-24 (config.json → auto_log.allocation). Before it, the
auto-logger used allowed_strategies: ["Bull Put"], which is self-sealing:
nothing outside the list can enter the book, so nothing outside it can ever
accumulate the evidence that would let it in. Bear Call, Short Put and Iron
Condor last entered on 2026-07-30/31, Long Put on 2026-07-13 — and under that
list, never again, whatever the market did.
What replaces it is a share per structure: (1 − explore_rate) follows a
bootstrap posterior of mean return on capital at risk, and explore_rate is
spread uniformly so no door closes permanently. Evidence decays by age, so a
structure going stale widens rather than hardens, and if Bull Put degrades the
weight moves on its own with no config edit. The draw is deterministic per
contract (blake2b, never the builtin hash, which Python randomises per
process), so entries replay.
explore_rate is 0.15, chosen by a walk-forward policy replay over trades
that actually happened rather than by taste:
| explore | mean return on risk | 95% CI |
|---|---|---|
| 0% | +6.64% | [+2.60%, +11.01%] |
| 15% | +2.66% | [+0.47%, +7.59%] |
| 25% | +2.68% | [−1.55%, +6.70%] |
| 40% | +1.80% | [−4.21%, +6.41%] |
15% is the highest rate whose backtested return still excludes zero. Read the replay for what it is: it prices the cost of exploring and structurally cannot price the benefit — a fixed history has no regime change for the insurance to pay off against, so it is biased against exploration by construction. It is a price tag, not a verdict.
eligible_strategies is the hard rail and deliberately excludes Bear Call and
Iron Condor: those two are not unmeasured, they fail their own breakeven
(−7.4pp and −10.1pp). Exploration buys information where the edge is unknown;
it is not a licence to re-run a structure already measured negative. Live
admission: Bull Put 88%, Long Call 3.0%, Long Put 3.2%, Short Put 3.4%.
Nothing else changed. Every other gate — friction, EV, earnings, position
sizing, the long-premium DTE floor — still runs first and still refuses. Set
allocation.enabled: false to restore the old allowlist exactly.
Create a variant in configs/weights/ (copy baseline.json, tweak weights), then run both:
python -m src.options_screener --mode discover --weights baseline --auto-log --auto
python -m src.options_screener --mode discover --weights exp_high_pop --auto-log --auto
Same scan, different weights → different rankings → different logged picks. Over weeks, the DB builds up per-profile P&L that you can compare directly.
# List available profiles
python -m src.options_screener --list-profiles
# Inspect per-profile counts
sqlite3 paper_trades.db "SELECT weight_profile, COUNT(*) FROM trades GROUP BY weight_profile"
# Per-profile P&L on closed trades
sqlite3 paper_trades.db "SELECT weight_profile, COUNT(*), ROUND(AVG(pnl_pct)*100,1) AS avg_pct FROM trades WHERE status='CLOSED' GROUP BY weight_profile"
cp configs/weights/baseline.json configs/weights/my_experiment.json
# Edit the file — tweak any of the 27 weights under "composite_weights"
python -m src.options_screener --weights my_experiment --auto-log --auto --mode discover
Unknown keys log a warning; non-numeric values error out. If --weights is omitted, behaviour is unchanged from pre-profile runs (trades logged with weight_profile = NULL).
(calendar_day, ticker, strike, expiration, type, weight_profile) is the unique key. Meaning:
Once the paper-trade ledger has accumulated enough closed trades, the per-component IC analysis tells you which scoring weights are actually predictive. Full plan and current state in docs/LOGGING_PLAN.md.
| Tool | What it does |
|---|---|
scripts/calibration_status.sh | One-page dashboard: closed-trade counts, per-strategy progress, apply-gate checklist, top-5 IC components, data integrity. Run anytime, no side effects. |
python3 -m src.backtester --calibrate | Read-only IC report against paper_trades.db. Run anytime to preview recommended weight changes. |
python3 -m src.backtester --calibrate --apply | Writes recommended weights into config.json (only run once you've crossed the gates in docs/LOGGING_PLAN.md). Auto-backs up to config.bak.<timestamp>.json. |
docs/CALIBRATION_JOURNAL.md | Append-only changelog of every --calibrate --apply event: IC snapshot, weight Δs, interpretation, revert procedure, known issues at time of apply. |
scripts/enforce_exits.sh | Wrapper around --enforce-exits. Install in cron for automatic daily TP/stop/time-exit closes. |
scripts/calibrate_snapshot.sh | Weekly snapshot — runs --calibrate, appends per-component IC drift to logs/calibration_history.tsv, integrity-checks the DB for out-of-bounds PnL or NULL pnl_usd. |
logs/calibration_history.tsv | Time series of per-component IC. Plot to see how signal stability changes as the corpus grows. |
logs/calibration_<date>.warnings | Created only when integrity-check finds an anomaly. Empty / absent = data is clean. |
Pre / Post-calibration cohort view. From the equity screener main menu press [7] PORTFOLIO — you'll be prompted to choose:
[1] Pre-calibration — trades scored under the original hand-tuned weights (entry_id < first_post_entry_id)[2] Post-calibration — trades scored under the most recent IC-tuned weights[A] All trades (default) — full timeline with the calibration moment markedThe cutoff is encoded by entry_id (not by date — since multiple trades can be logged on the same calendar day as a calibration apply). See CALIBRATION_CUTOFFS in src/check_pnl.py and scripts/make_pnl_chart.py. Add a new tuple to both files when you apply a new calibration.
P&L visualization. Generate stand-alone SVG equity curves (no matplotlib dependency):
python scripts/make_pnl_chart.py # all-trade equity + crypto curves
python scripts/make_pnl_chart.py --equity-only --cohort post # post-calibration cohort only
python scripts/make_pnl_chart.py --equity-only --cohort pre # pre-calibration cohort only
Output lands in reports/<book>_curve_<cohort>_<date>.svg. The chart auto-refreshes when you use the menu cohort selector.
Auto-log filters that affect the calibration corpus (toggle in config.json):
auto_log_skip_long_puts: true — Long Puts are silently filtered from --auto-log because their PF is 0.69 in the historical ledger and they're now disabled going forward. Auto-log line shows filtered N Long Put(s) for visibility.PnL data is auto-sanitized at close time in paper_manager.py — pnl_pct is clamped to physically-possible bounds per strategy, exit_price to ≥0, and pnl_usd is always populated. This prevents IC pollution from anomalous closes (e.g. a credit spread storing pnl_pct=+3.58, which actually happened once and silently flipped the sign of skew_align IC).
Equity mode (the screener above) handles US equities via yfinance. A parallel crypto strategist lives in src/crypto/ and screens BTC / ETH options on Deribit, with funding-rate / basis / OI / stablecoin-flow signals layered on top. Build plan: docs/CRYPTO_BUILD_PLAN.md.
python -m src.crypto.screener # main crypto CLI (BTC / ETH scans, funding-basis, portfolio, calibration, backtest)
python -m src.crypto.check_pnl # crypto portfolio viewer (entry / live mark / P/L per position)
python -m src.crypto volcarry --currency BTC --dte 14 --freq 7 --cost-stress --oos # delta-hedged vol-carry backtester
| File | What it does |
|---|---|
src/crypto/screener.py | Main CLI menu (scan BTC, scan ETH, funding/basis dashboard, portfolio, calibration status, backtest) |
src/crypto/data_fetching.py | Deribit chain pulls + Binance/Bybit/OKX/dYdX funding aggregation + USDT/USDC stablecoin flow |
src/crypto/scoring.py | Crypto-specific composite score (IV rank, VRP, term structure, skew, funding-divergence, basis, OI z-score, stablecoin flow) |
src/crypto/strategy.py | Strategy builders: long single-leg, debit/credit spreads, calendars |
src/crypto/exit_enforcer.py | TP / stop / time-exit enforcement for crypto positions (per-leg Deribit mark price) |
src/crypto/auto_logger.py | Auto-log driver for crypto. Runs every 4 hours via cron, gated by regime + chain availability. |
src/crypto/backtester.py | Walk-forward backtest on accumulated chain snapshots |
src/crypto/volbacktest/ | Delta-hedged short-straddle vol-carry backtester. DVOL-anchored marks (real Deribit implied-vol index), real-trade-calibrated costs, daily delta-hedging, Newey-West/bootstrap significance, regime split, cost-stress, optional defined-risk wings (--wing). Finding (2.7yr, real costs): BTC vol-risk-premium is harvestable (Sharpe ~2.8 naked / ~3.2 with ±15% wings, NW t≈4–5, survives >5× quoted cost, regime-robust, OOS holds); ETH is not (Sharpe <0, dies at half the quoted cost). Research tool — not wired to real money. |
src/crypto/check_pnl.py | Standalone portfolio viewer — open positions with live Deribit marks + closed positions with entry / exit / P&L / reason / win-loss |
Automation (cron — installed alongside equity automation):
# Crypto auto-log driver — every 4 hours, both BTC and ETH
0 */4 * * * /path/to/options/scripts/auto_log_crypto.sh >> /path/to/options/logs/auto_log_crypto.log 2>&1
# Crypto exit enforcer — hourly (Deribit prices update faster than equity yfinance)
5 * * * * /path/to/options/scripts/enforce_exits_crypto.sh >> /path/to/options/logs/enforce_exits_crypto.log 2>&1
Crypto trades land in paper_trades_crypto.db (separate ledger from equity). The two books share schema layout but never mix — calibration runs independently per book.
A separate book from the options screener above: buy-zone / tranche-ladder tracking for long-term stock accumulation, not options trading. [5] HOLDINGS in the launcher menu, or directly:
python -m src.longterm # interactive holdings desk
python -m src.longterm --report # write & open the HTML holdings report, then exit
What it does — all descriptive, none of it predictive (see Module Maturity):
Tranches) and a cash pool to draw from. Each name is classified IN ZONE (spot at/below the next unfilled level), NEAR (within 1 daily σ or 2%, whichever is wider), WATCHING, or FILLED — pure zone-state math (src/longterm/zones.py), not a signal.[6] Find candidates (DISCOVER) scans a sector (SEMICONDUCTORS, TECH, BANKS, HEALTHCARE, ENERGY, CONSUMER, INDUSTRIALS, UTILITIES, REALESTATE, MATERIALS, COMMUNICATIONS) for beaten-down, liquid, US-listed names and gives each an explicit BUY NOW or WAIT for $X entry verdict — reusing the same distance/sigma widening rule as the zone tracker against the candidate's own suggested buy ladder, never a new weighted score. BUY NOW picks inside a 14-day earnings window carry a caution rather than a silent upgrade. Drill into any candidate for its full read: support/resistance ladder, bounce-odds after similar historical drops, insider cluster buys (SEC EDGAR Form 4), days to earnings, and lightweight fundamentals — then log it straight into your plan with the suggested ladder pre-filled.KO → COLA) where one exists — annotation only, no price is fetched for the CDR itself (src/longterm/cdr.py).--report or [7] from the menu) — self-contained, same house design system as the rest of this repo's reports (src/desk_kit).The plan lives in longterm_plan.json (git-ignored, same as watchlist.json); actual fills live in data/longterm.db.
[3] LEVERAGE in the launcher, or python -m src.leverage {signal,backtest,paper,status}. Explicitly pre-validation — the module's own banner reads PRE-VALIDATION — NO EDGE YET and every command is paper/dry-run unless --live, which is an inert stub until the validation gate passes. See Module Maturity. Do not trade this with real money.
[4] RESEARCH in the launcher opens a read-only submenu — no scores, no trades, no real money, ever:
| Tool | Command | Finding |
|---|---|---|
| Breakout/breakdown probabilities | python -m src.breakout | Honest no-edge result vs. implied vol |
| Vol-intelligence (IV movers + VRP) | python -m src.vol_intel | Monitor-only; equity VRP currently negative |
| Equity-VRP backtest (delta-hedged short straddle) | python -m src.equity_vol | No single-name edge; SPY/index level is positive |
These are kept in the repo — and in the menu — precisely because they're honest negative/no-edge results, in the same spirit as the Lottery sleeve and the leverage module's backtests.
{
"filters": {
"min_volume": 50,
"min_open_interest": 10,
"max_bid_ask_spread_pct": 0.40,
"delta_min": 0.15,
"delta_max": 0.35,
"min_days_to_expiration": 7,
"max_days_to_expiration": 45,
"min_iv_percentile": 20
},
"exit_rules": {
"time_exit_dte": 21,
"min_days_held": 3,
"short_premium": {
"take_profit_ge_21_dte": 0.50,
"take_profit_7_to_21_dte": 0.35,
"take_profit_lt_7_dte": 0.25,
"stop_loss_premium_multiple": 2.0,
"stop_loss_on_strike_breach": true,
"strike_breach_buffer": 0.0,
"stop_loss_delta_multiple": 2.5
},
"spread": {
"take_profit": 0.50,
"stop_loss": -1.0
},
"long_option": {
"take_profit": 1.00,
"take_profit_delta": 0.80,
"stop_loss": -0.50
}
},
"composite_weights": {
"pop": 0.22,
"em_realism": 0.10,
"rr": 0.15,
"momentum": 0.08,
"iv_rank": 0.06,
"liquidity": 0.12,
"ev": 0.14
},
"vix_regime_multipliers": {
"low": { "pop": 1.0, "ev": 1.2, "rr": 1.1 },
"normal": { "pop": 1.0, "ev": 1.0, "rr": 1.0 },
"high": { "pop": 1.2, "ev": 0.8, "rr": 1.3 }
}
}
AI_CONFIG = {
# API provider: "openrouter" (default) or "anthropic"
"provider": "openrouter",
"model": "inclusionai/ling-3.0-flash:free",
"fallback_model": "nvidia/nemotron-3-ultra-550b-a55b:free",
"second_fallback_model": "poolside/laguna-s-2.1:free",
"third_fallback_model": None,
"api_key_env": "OPENROUTER_API_KEY",
# Per-model API key overrides
"model_key_map": {
"inclusionai/ling-3.0-flash:free": "OPENROUTER_LING_KEY",
"nvidia/nemotron-3-ultra-550b-a55b:free": "OPENROUTER_NVIDIA_KEY",
"poolside/laguna-s-2.1:free": "OPENROUTER_POOLSIDE_KEY",
},
# Scoring weights
"ai_weight": 0.30,
"technical_weight": 0.70,
# Dynamic weight multipliers by VIX regime
"regime_weight_multipliers": {
"low": 0.80,
"normal": 1.00,
"high": 1.30,
},
# Feature flags
"cache_enabled": True,
"confidence_enabled": True,
"two_pass_enabled": True,
"news_enabled": True,
# API call settings
"batch_size": 3,
"max_tokens": 2048,
"temperature": 0.1,
"timeout": 60,
}
Key levers:
| Setting | Effect |
|---|---|
model / fallback_model | Swap to any OpenRouter model ID at any time |
ai_weight / technical_weight | Shift how much AI opinion matters vs. quant model |
two_pass_enabled | Disable to skip ticker context pass (faster, fewer API calls) |
batch_size | Smaller = easier to debug; larger = fewer API calls |
cache_enabled | Disable to force fresh AI scores on every run |
fields_to_include | Trim to reduce token cost; expand for richer AI context |
The screener can be exposed as a local HTTP API so that Discord and Telegram bots can query it from anywhere — live options data from your phone or in a chat app without opening a terminal.
Discord Bot ──┐
├──► FastAPI server :8000 ──► run_scan / get_market_context
Telegram Bot ──┘
# Install bot dependencies
pip install -r requirements_bots.txt
# Set up tokens
copy .env.example .env # Windows
# cp .env.example .env # macOS/Linux
# Edit .env with real values:
# DISCORD_BOT_TOKEN=...
# DISCORD_GUILD_ID=... (server ID for instant slash-command sync)
# TELEGRAM_BOT_TOKEN=...
# API_BASE_URL=http://127.0.0.1:8000
# Start everything at once (API + both bots)
python start_all.py
# Or start individually:
python -m uvicorn src.api:app --host 127.0.0.1 --port 8000
python -m src.bots.discord_bot
python -m src.bots.telegram_bot
| Endpoint | Description |
|---|---|
GET /health | Health check |
GET /market | VIX level, regime, trend, macro risk (cached 60s) |
GET /top?n=10 | Top N picks from liquid_large_cap watchlist |
GET /scan/{symbol} | Scan a single ticker |
GET /watchlist/{name} | Scan any named watchlist from config.json |
Both bots support the same four commands:
| Command | What it does |
|---|---|
/market | Current market context: trend, VIX, macro risk |
/top | Top picks from the liquid_large_cap watchlist |
/scan AAPL | Scan a single ticker |
/watchlist high_iv | Scan a named watchlist (liquid_large_cap, sector_etfs, high_iv, income) |
options/
├── run.py # Auto-venv launcher: python3 run.py (no activation needed)
├── ai_rank.py # AI-enhanced entry point — runs screener + AI scoring
├── options_screener.py # Thin wrapper for backward compatibility
├── backtest_screener.py # Backtester entry point
├── config.json # Screener thresholds, weights, and exit rules
├── configs/
│ └── weights/ # Weight profiles for A/B optimization (baseline.json + experiments)
├── watchlist.json # Personal ticker list (auto-created by ADD command)
├── requirements.txt
├── requirements_bots.txt # Bot/API dependencies (fastapi, discord.py, python-telegram-bot)
├── start_all.py # Launch API + both bots in one command
├── .env # Your API keys (gitignored — never committed)
├── .env.example # Template showing required env vars (screener + bots)
├── README.md
└── src/
├── __main__.py # Auto-venv launcher for python3 -m src
├── options_screener.py # Core scan engine, report printing, spread/condor finders
├── cli_display.py # Terminal display: per-pick detail, comparison table, report
├── data_fetching.py # yfinance single-fetch, technical indicators, chain cache
├── filters.py # Chain filtering, IV smile outlier removal, premium bucketing
├── scoring.py # Composite quality score re-exports
├── trade_analysis.py # Thesis, entry/exit levels, confidence, execution guidance
├── risk_engine.py # OI wall detection, gamma ramp flags, structural risk
├── utils.py # Vectorised BS Greeks + BAW American pricing (NumPy/SciPy)
├── iv_surface.py # SVI IV surface fitting, mispricing detection
├── simulation.py # Monte Carlo PoP / PoT (Merton Jump Diffusion GBM)
├── formatting.py # ANSI colours, box drawing, metric formatters
├── stress_test.py # Full BS repricing scenario P/L matrix
├── paper_manager.py # SQLite paper trade logging, schema migration (v10), multi-leg log/exit, dedup, weight_profile tagging
├── spread_scoring.py # Spread + iron condor composite scoring (credit_to_width, delta_neutral, etc.)
├── weight_profiles.py # Load/list weight profiles from configs/weights/
├── check_pnl.py # Standalone portfolio P/L viewer with Greeks
├── news_fetcher.py # Concurrent multi-source news (yfinance, Finviz, Polygon)
├── macro_analyzer.py # Macro risk gating (10Y yield, VIX, DXY, credit spreads)
├── portfolio_risk.py # Portfolio VaR, correlation analysis, position sizing
├── sector_analyzer.py # Sector-level analysis and rotation signals
├── polygon_client.py # Polygon.io integration (news, VWAP, unusual flow)
├── vol_analytics.py # Vol cone, concurrent IV surface, regime classification
├── regime_dashboard.py # VIX/PCR/SPY market regime dashboard at startup
├── backtester.py # Walk-forward backtester + paper-trade IC + per-structure weight optimizer (singles / spreads / iron condors)
├── backtest_optimizer.py # Weight optimizer for composite_weights via IC analysis
├── calc_expected_move.py # Implied expected move calculator
├── oi_snapshot.py # OI change tracking between runs
├── watchlist.py # Watchlist management (ADD/REMOVE commands)
├── visual_surface.py # 3D risk surface: braille/ASCII, P&L + Greek surfaces, contours
├── visualizer_3d.py # Interactive 3D Plotly visualizer (6-tab browser dashboard)
├── visualize_results.py # Matplotlib/Plotly charts for scan results
├── dashboard.py # Streamlit web interface
├── ai_scorer.py # Two-pass AI scoring with retry, fallback, narrative context
├── ai_cache.py # Same-day SQLite cache for AI scores
├── ranking.py # combine_scores(), Rich ranked table, divergence detection
├── prompts.py # AI system/scoring prompt templates
├── config_ai.py # AI layer configuration (models, weights, thresholds)
├── config_validator.py # Config.json validation at module load
├── types.py # Shared type definitions and data classes
├── api.py # FastAPI server (/market, /top, /scan, /watchlist)
├── bots/
│ ├── __init__.py
│ ├── discord_bot.py # Discord slash commands (/market, /top, /scan, /watchlist)
│ └── telegram_bot.py # Telegram command handlers (same 4 commands)
├── crypto/ # BTC/ETH options module (Deribit) — separate book from equity
│ ├── screener.py # Main crypto CLI: scans, funding-basis dashboard, portfolio, calibration, backtest
│ ├── data_fetching.py # Deribit chain + Binance/Bybit/OKX/dYdX funding + stablecoin flow
│ ├── scoring.py # Crypto-specific composite (IV rank, VRP, term, skew, funding/basis, OI, stableflow)
│ ├── strategy.py # Strategy builders: long single-leg, debit/credit spreads, calendars
│ ├── exit_enforcer.py # TP / stop / time-exit enforcement (per-leg Deribit marks)
│ ├── auto_logger.py # Auto-log driver (cron, every 4h, gated by regime + chain availability)
│ ├── backtester.py # Walk-forward backtest over accumulated chain snapshots
│ ├── chain_snapshot.py # Daily chain snapshotting for backtest data accumulation
│ ├── regime.py # Crypto regime classifier (BEAR / NEUTRAL / BULL based on rvol30d + spot vs 200dMA)
│ ├── cache.py # Disk-backed cache for Deribit responses
│ └── check_pnl.py # Standalone portfolio viewer (entry / live mark / P&L / reason)
├── launcher.py # Top-level [1-7] menu: STOCKS / CRYPTO / LEVERAGE / RESEARCH / HOLDINGS / STRATEGIES / SETTINGS
├── longterm/ # Long-term holdings desk — buy zones, tranches, DISCOVER entry verdict (see § Long-Term Holdings Desk)
├── leverage/ # BTC/ETH perp futures strategy — pre-validation, no edge yet
├── breakout/, vol_intel/, equity_vol/ # Research tools (read-only) — see § Research Tools
├── research.py / research/ # Research desk — multi-tab HTML report (market + optional ticker deep-dive)
├── morning/ # Morning briefing — self-contained HTML quant note (regime, vol, macro, portfolio, gate)
├── tearsheet/ # Self-contained HTML tearsheet per scanned pick (--tearsheet N)
├── squeeze/ # Short-squeeze grader — powers SQUEEZE mode, display-only
│ └── backtest/ # Point-in-time validation harness (FINRA + EDGAR + DoltHub)
├── lottery/ # Lottery Ticket board — powers LOTTERY mode; documented negative-EV base rate
├── outlook/ # Sector/asset outlook engine — relative-ranking edge only (see Module Maturity)
├── intel/ # Intel Briefing engine — powers INTEL mode
├── worldnews.py, insider.py, uoa.py # Signal overlays — see § What You Can Trust Today
├── cross_check.py, av_options.py, dolt_*.py # Second-source data + DoltHub backtest/validation tooling
├── core/, execution/, desk_kit/ # Shared internals: position sizing/costs/journal, exit/preflight pipeline, HTML design system
└── maintenance.py, maintenance_health.py # Headless cron/LaunchAgent heartbeat + staleness escalation
scripts/
├── auto_log_equity.sh # Equity auto-log driver (M-F 10:30 / 12:30 / 14:15 ET) — modes by clock window
├── auto_log_crypto.sh # Crypto auto-log driver (every 4h, BTC + ETH)
├── enforce_exits.sh # Equity exit enforcer (daily cron)
├── enforce_exits_crypto.sh # Crypto exit enforcer (hourly cron)
├── calibrate_snapshot.sh # Equity calibration snapshot (Sundays)
├── calibrate_snapshot_crypto.sh # Crypto calibration snapshot
├── calibration_status.sh # One-page calibration dashboard (read-only)
├── equity_stress_check.py # SAFE / UNSAFE gate based on -20%/+10pp scenario vs threshold
├── make_pnl_chart.py # Pure-stdlib SVG equity curve generator (equity + crypto, --cohort pre|post|all)
└── reclassify_legacy_trades.py # One-shot migration for legacy single-leg rows
docs/
├── LOGGING_PLAN.md # Calibration logging plan — what/when to log, current gate progress
├── CALIBRATION_JOURNAL.md # Append-only changelog of every --calibrate --apply event
├── QUANT_RESEARCH_MANUAL.md # Strategy/architecture reference manual
├── DECISION_MATRIX_AND_TROUBLESHOOTING.md # Tactical command reference + VIX-regime decision matrix
├── CRYPTO_BUILD_PLAN.md # Crypto module build plan + progress
├── VALIDATION_POWER.md # Power analysis behind the forward-cohort gate
├── OUTLOOK_FINDINGS.md # Sector/asset outlook engine backtest findings
└── LOTTERY_BACKTEST_FINDINGS.md # Lottery sleeve negative-EV backtest findings
exit_reason)--enforce-exits CLIrecommend_weights_for_structure() learns independent composite_weights / credit_spread_weights / iron_condor_weights maps from each structure's closed-trade subset using component-level IC + shrinkage + cap + budget renormalization--weights NAME --auto-log for A/B optimization with per-profile dedupFor educational and informational purposes only. Not financial advice. Options trading involves substantial risk of loss. Always do your own research.
Personal use only. Not licensed for commercial distribution.
[9]--mode lottery✦⚠python3 -m src.check_pnl✦| Frozen — reads out 2026-11-19. |
docs/PREREG_RANKER_TEST.md, one look at n=1,424 closed gate survivors (currently 312). Do not run early or route around it. |
settings.json--portfolio-check)entry_id cutoff, dedicated SVG equity curve per cohortscripts/make_pnl_chart.py) — pure stdlib, no matplotlib dependency, supports --cohort pre|post|all and crypto bookdocs/CALIBRATION_JOURNAL.md) — append-only changelog of every --calibrate --apply event with IC tables, weight Δs, interpretation, and revert procedurecomposite_weights shrunk POP from 0.13 → 0.03, boosted vrp / term_structure / vega_risk to 0.14 / 0.13 / 0.11 (Calibration #1; backup config.bak.20260511-111722.json)gamma_pin, max_pain, oi_change, option_rvol, pcr, sentiment) — removed self-reinforcing weight-gate short-circuits that pinned them to constants (2026-05-20); whether they carry real signal is still an open calibration question now that they store real valuessrc/crypto/volbacktest) — delta-hedged short-straddle harness on real Deribit DVOL marks; BTC VRP harvestable (Sharpe ~2.8–3.2, survives >5× quoted cost), ETH is notsrc/outlook) — relative-ranking edge (IC +0.05–0.08 at 2–3 months); absolute directional calls are unreliable, see docs/OUTLOOK_FINDINGS.mdpython -m src.research) and morning briefing (python -m src.morning) — self-contained HTML quant reports through a shared design system (src/desk_kit)--tearsheet N) — self-contained HTML per scanned pick, re-renderable offline from its JSON sidecar[11] SQUEEZE mode) — display-only grader on short interest + unusual call activitysrc/squeeze/backtest) — survivorship-free point-in-time study, 205 FINRA settlement dates × 480k graded observations. Signal confirmed (tail asymmetry +2.6pp, holds out-of-sample) but attributable to the short-interest level; the scoring adds nothing and two of its rules are backwards — see docs/SQUEEZE_BACKTEST.md[9] LOTTERY mode) — far-OTM long-shot ranking with BS-repriced hit-probability; built around a documented negative-EV base rate, see docs/LOTTERY_BACKTEST_FINDINGS.mdsrc/leverage, BTC/ETH perp futures) — pre-validation signal/backtest/paper harness, explicitly no edge yetsrc/longterm) — buy-zone/tranche accumulation tracking, DISCOVER sector scan with BUY NOW / WAIT entry verdict, CDR ticker markers, guided ACTIONS menu, HTML reportpython3 run.py / python3 -m src with no args) — top-level [1-7] menu across STOCKS / CRYPTO / LEVERAGE / RESEARCH / HOLDINGS / STRATEGIES / SETTINGSrecommend_weights_for_structure('spread') — gated by Bear Call ≥ 30)