feat: PEAD mid-cap strategy + pipeline hardening + README cleanup
- Implement PEAD 7% Long+Short strategy with mid-cap universe expansion - Add Stock Oracle screener/company clients, text sentiment features - Enhance backtest engine: short-side execution, walk-forward CV, MFE/MAE analysis - Harden pipeline: sequential Oracle API calls, scoring recalibration (event_quality 65%) - Add experiment configs for 60+ strategy variants and journal tracking - Add review/analysis CLI tools - Remove obsolete dev/phase0-4 design documents and analysis scripts - Clean README to reflect only implemented features (remove unbuilt adapters/engines) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>main
parent
764493dbe1
commit
2395a0c0c3
@ -0,0 +1,11 @@
|
||||
---
|
||||
name: feedback_stock_oracle_data
|
||||
description: All data must come through Stock Oracle API. If Oracle doesn't provide something, report what's missing instead of using alternatives.
|
||||
type: feedback
|
||||
---
|
||||
|
||||
All data must be fetched through the Stock Oracle API. Do not use alternative data sources (direct SEC EDGAR, Yahoo Finance, web scraping, etc.).
|
||||
|
||||
**Why:** Stock Oracle is the single source of truth for this project. Using other sources creates inconsistencies and bypasses the project's data infrastructure.
|
||||
|
||||
**How to apply:** When a data need arises, check Stock Oracle API first. If Oracle doesn't provide it, tell the user what's missing rather than silently using an alternative source.
|
||||
@ -0,0 +1,39 @@
|
||||
---
|
||||
name: PEAD Strategy Results
|
||||
description: Pure PEAD 7% strategy is the first configuration to show consistent OOS profitability across all temporal splits
|
||||
type: project
|
||||
---
|
||||
|
||||
## PEAD 7% Base — Production Candidate (2026-03-14)
|
||||
|
||||
First strategy to show positive expectancy out-of-sample on all splits.
|
||||
|
||||
### Signal
|
||||
- Entry: earnings reaction_day_return >= 7%, volume_ratio_20d >= 1.5x
|
||||
- Scoring model: `pead` in `libs/backtest/scoring.py` (`compute_pead_score()`)
|
||||
- Earnings-only, all other event types disabled
|
||||
- Filters disabled: veto_oneoff_penalty=1.0, veto_unknown_direction=false, veto_bearish_direction=false
|
||||
|
||||
### Combined Results (155 trades across train/test/valid)
|
||||
- Win Rate: 67.1% (stable 64.7-67.5% across all splits)
|
||||
- Avg Win: +5.45%, Avg Loss: -3.48%
|
||||
- Mean PnL per trade: +2.51%
|
||||
- Median PnL: +3.73%
|
||||
- W/L Ratio: 1.57
|
||||
- PF: 1.31 (test), 1.95 (valid), 1.68 (train)
|
||||
|
||||
### Key Files
|
||||
- Manifests: `configs/experiments/pead_pure_7pct.json`, `pead_7pct_drift_a.json`
|
||||
- Scoring: `libs/backtest/scoring.py` — `compute_pead_score()`
|
||||
- Domain: `libs/backtest/domain.py` — `SignalConfig.scoring_model`, `pead_reaction_threshold`, `pead_volume_threshold`
|
||||
- Store wiring: `apps/backtester/run.py` — `_build_store()` dispatches to PEAD scoring
|
||||
|
||||
### Drift A Variant (wider target 3.0 ATR, hold 25d)
|
||||
- Higher per-trade returns (+7.13% avg win) but lower win rate (56.1%)
|
||||
- Mean PnL nearly identical (+2.59%) but median much lower (+0.40%)
|
||||
- OOS less stable (valid WR dropped to 40%)
|
||||
- Not recommended for production
|
||||
|
||||
**Why:** Previous strategies (baseline 3-factor scoring, small/mid-cap variants, execution optimization) all failed OOS. PEAD 7% succeeds because it relies on a well-documented academic anomaly with a simple, hard-to-overfit signal.
|
||||
|
||||
**How to apply:** Use PEAD 7% Base as the foundation. Any future strategy modifications should be compared against this baseline. The signal is the edge — execution/risk parameters have limited room for improvement (5 execution variants tested, none beat baseline OOS).
|
||||
@ -0,0 +1,314 @@
|
||||
"""Coverage Check: Validate Stock Oracle data availability for symbol universe.
|
||||
|
||||
Checks each ticker for:
|
||||
1. Price data coverage (daily bars since start_date, require 90%+ trading days)
|
||||
2. Filing data (8-K filings, require 3+ filings)
|
||||
3. Company info (name, CIK, sector must exist)
|
||||
4. Market cap range ($500M - $10B for small/mid-cap)
|
||||
|
||||
Outputs CSV report + pass/fail summary. Optionally writes a filtered YAML
|
||||
with only passing tickers.
|
||||
|
||||
Usage:
|
||||
python -m dev.analysis.coverage_check \
|
||||
--symbols-file configs/symbols_smallmid.yaml \
|
||||
--start-date 2022-07-01 \
|
||||
[--output-dir ./data/analysis] \
|
||||
[--write-filtered]
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
import csv
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import yaml
|
||||
|
||||
from libs.oracle_client import FilingsService, FinancialService, PriceService, make_oracle_client
|
||||
|
||||
|
||||
# --- Constants ---
|
||||
MIN_MARKET_CAP = 5e8 # $500M
|
||||
MAX_MARKET_CAP = 1e10 # $10B
|
||||
MIN_FILINGS = 3
|
||||
MIN_PRICE_COVERAGE = 0.90 # 90% of expected trading days
|
||||
APPROX_TRADING_DAYS_PER_YEAR = 252
|
||||
|
||||
|
||||
def _load_symbols(path: str) -> list[str]:
|
||||
with open(path) as f:
|
||||
cfg = yaml.safe_load(f) or {}
|
||||
symbols = cfg.get("symbols", [])
|
||||
# Deduplicate while preserving order
|
||||
seen: set[str] = set()
|
||||
unique: list[str] = []
|
||||
for s in symbols:
|
||||
if s not in seen:
|
||||
seen.add(s)
|
||||
unique.append(s)
|
||||
return unique
|
||||
|
||||
|
||||
async def _check_ticker(
|
||||
ticker: str,
|
||||
price_svc: PriceService,
|
||||
filings_svc: FilingsService,
|
||||
financial_svc: FinancialService,
|
||||
start_date: str,
|
||||
expected_trading_days: int,
|
||||
) -> dict[str, Any]:
|
||||
"""Run all coverage checks for a single ticker."""
|
||||
result: dict[str, Any] = {
|
||||
"ticker": ticker,
|
||||
"price_bars": 0,
|
||||
"price_coverage": 0.0,
|
||||
"price_pass": False,
|
||||
"filing_count": 0,
|
||||
"filing_pass": False,
|
||||
"has_name": False,
|
||||
"has_cik": False,
|
||||
"has_sector": False,
|
||||
"sector": None,
|
||||
"info_pass": False,
|
||||
"market_cap": None,
|
||||
"market_cap_pass": False,
|
||||
"overall_pass": False,
|
||||
"error": None,
|
||||
}
|
||||
|
||||
# 1. Price data
|
||||
try:
|
||||
end_date = datetime.now().strftime("%Y-%m-%d")
|
||||
price_resp = await price_svc.get_daily_bars(ticker, start=start_date, end=end_date)
|
||||
n_bars = len(price_resp.bars)
|
||||
result["price_bars"] = n_bars
|
||||
coverage = n_bars / expected_trading_days if expected_trading_days > 0 else 0
|
||||
result["price_coverage"] = round(coverage, 3)
|
||||
result["price_pass"] = coverage >= MIN_PRICE_COVERAGE
|
||||
except Exception as e:
|
||||
result["error"] = f"price: {e}"
|
||||
|
||||
# 2. Filings (8-K)
|
||||
try:
|
||||
filings_resp = await filings_svc.search_filings(
|
||||
ticker, form_type="8-K", start_date=start_date
|
||||
)
|
||||
n_filings = len(filings_resp.filings)
|
||||
result["filing_count"] = n_filings
|
||||
result["filing_pass"] = n_filings >= MIN_FILINGS
|
||||
except Exception as e:
|
||||
err = f"filings: {e}"
|
||||
result["error"] = err if not result["error"] else f"{result['error']}; {err}"
|
||||
|
||||
# 3. Company info + market cap
|
||||
try:
|
||||
info = await financial_svc.get_company_info(ticker)
|
||||
result["has_name"] = bool(info.name)
|
||||
result["has_cik"] = bool(info.cik)
|
||||
result["has_sector"] = bool(info.sector)
|
||||
result["sector"] = info.sector
|
||||
result["info_pass"] = all([info.name, info.cik, info.sector])
|
||||
if info.market_cap is not None:
|
||||
result["market_cap"] = info.market_cap
|
||||
result["market_cap_pass"] = MIN_MARKET_CAP <= info.market_cap <= MAX_MARKET_CAP
|
||||
else:
|
||||
# If market_cap not available, pass this check (can't verify)
|
||||
result["market_cap_pass"] = True
|
||||
except Exception as e:
|
||||
err = f"info: {e}"
|
||||
result["error"] = err if not result["error"] else f"{result['error']}; {err}"
|
||||
# If company info fails entirely, market_cap check is inconclusive
|
||||
result["market_cap_pass"] = True
|
||||
|
||||
# Overall
|
||||
result["overall_pass"] = all([
|
||||
result["price_pass"],
|
||||
result["filing_pass"],
|
||||
result["info_pass"],
|
||||
result["market_cap_pass"],
|
||||
])
|
||||
|
||||
return result
|
||||
|
||||
|
||||
async def run_coverage_check(
|
||||
symbols: list[str],
|
||||
start_date: str,
|
||||
output_dir: Path,
|
||||
write_filtered: bool = False,
|
||||
symbols_file: str | None = None,
|
||||
concurrency: int = 5,
|
||||
) -> None:
|
||||
"""Run coverage check for all symbols."""
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Calculate expected trading days
|
||||
start_dt = datetime.strptime(start_date, "%Y-%m-%d")
|
||||
years_elapsed = (datetime.now() - start_dt).days / 365.25
|
||||
expected_trading_days = int(years_elapsed * APPROX_TRADING_DAYS_PER_YEAR)
|
||||
|
||||
print(f"\n{'='*80}")
|
||||
print(f"Coverage Check — {len(symbols)} tickers, start_date={start_date}")
|
||||
print(f"Expected trading days: ~{expected_trading_days}, concurrency={concurrency}")
|
||||
print(f"{'='*80}\n")
|
||||
|
||||
results: list[dict[str, Any]] = []
|
||||
sem = asyncio.Semaphore(concurrency)
|
||||
counter = {"done": 0}
|
||||
|
||||
async def _check_with_sem(ticker: str) -> dict[str, Any]:
|
||||
async with sem:
|
||||
result = await _check_ticker(
|
||||
ticker, price_svc, filings_svc, financial_svc,
|
||||
start_date, expected_trading_days,
|
||||
)
|
||||
counter["done"] += 1
|
||||
status = "PASS" if result["overall_pass"] else "FAIL"
|
||||
mcap_str = f"${result['market_cap']/1e6:.0f}M" if result["market_cap"] else "N/A"
|
||||
print(
|
||||
f" [{counter['done']:3d}/{len(symbols)}] {ticker:<6s} "
|
||||
f"price={result['price_bars']:>4d} bars ({result['price_coverage']:.0%}) "
|
||||
f"filings={result['filing_count']:>3d} "
|
||||
f"mcap={mcap_str:>8s} "
|
||||
f"-> {status}"
|
||||
)
|
||||
if result["error"]:
|
||||
print(f" ! {result['error']}")
|
||||
return result
|
||||
|
||||
async with make_oracle_client() as client:
|
||||
price_svc = PriceService(client)
|
||||
filings_svc = FilingsService(client)
|
||||
financial_svc = FinancialService(client)
|
||||
|
||||
tasks = [_check_with_sem(ticker) for ticker in symbols]
|
||||
results_unordered = await asyncio.gather(*tasks)
|
||||
|
||||
# Preserve original symbol order
|
||||
ticker_to_result = {r["ticker"]: r for r in results_unordered}
|
||||
results = [ticker_to_result[s] for s in symbols]
|
||||
|
||||
# Write CSV
|
||||
csv_path = output_dir / "coverage_check.csv"
|
||||
if results:
|
||||
fieldnames = list(results[0].keys())
|
||||
with open(csv_path, "w", newline="") as f:
|
||||
writer = csv.DictWriter(f, fieldnames=fieldnames)
|
||||
writer.writeheader()
|
||||
writer.writerows(results)
|
||||
|
||||
# Summary
|
||||
passed = [r for r in results if r["overall_pass"]]
|
||||
failed = [r for r in results if not r["overall_pass"]]
|
||||
|
||||
print(f"\n{'='*80}")
|
||||
print(f"Summary")
|
||||
print(f"{'='*80}")
|
||||
print(f" Total: {len(results)}")
|
||||
print(f" Passed: {len(passed)}")
|
||||
print(f" Failed: {len(failed)}")
|
||||
|
||||
if failed:
|
||||
# Breakdown of failure reasons
|
||||
price_fail = sum(1 for r in failed if not r["price_pass"])
|
||||
filing_fail = sum(1 for r in failed if not r["filing_pass"])
|
||||
info_fail = sum(1 for r in failed if not r["info_pass"])
|
||||
mcap_fail = sum(1 for r in failed if not r["market_cap_pass"])
|
||||
print(f"\n Failure breakdown:")
|
||||
print(f" Price coverage < {MIN_PRICE_COVERAGE:.0%}: {price_fail}")
|
||||
print(f" Filings < {MIN_FILINGS}: {filing_fail}")
|
||||
print(f" Missing company info: {info_fail}")
|
||||
print(f" Market cap out of range: {mcap_fail}")
|
||||
print(f"\n Failed tickers: {', '.join(r['ticker'] for r in failed)}")
|
||||
|
||||
# Sector distribution of passed tickers
|
||||
sector_counts: dict[str, int] = {}
|
||||
for r in passed:
|
||||
sector = r.get("sector") or "Unknown"
|
||||
sector_counts[sector] = sector_counts.get(sector, 0) + 1
|
||||
if sector_counts:
|
||||
print(f"\n Sector distribution (passed):")
|
||||
for sector, count in sorted(sector_counts.items(), key=lambda x: -x[1]):
|
||||
print(f" {sector:<30s} {count:>3d}")
|
||||
|
||||
print(f"\n CSV report: {csv_path}")
|
||||
|
||||
# Write filtered YAML
|
||||
if write_filtered and passed:
|
||||
filtered_path = output_dir / "symbols_smallmid_filtered.yaml"
|
||||
filtered_symbols = [r["ticker"] for r in passed]
|
||||
with open(filtered_path, "w") as f:
|
||||
yaml.dump(
|
||||
{"symbols": filtered_symbols},
|
||||
f,
|
||||
default_flow_style=False,
|
||||
sort_keys=False,
|
||||
)
|
||||
print(f" Filtered YAML ({len(filtered_symbols)} tickers): {filtered_path}")
|
||||
if symbols_file:
|
||||
print(f"\n To use filtered list:")
|
||||
print(f" cp {filtered_path} {symbols_file}")
|
||||
|
||||
# Go/No-Go
|
||||
print(f"\n{'='*80}")
|
||||
if len(passed) >= 150:
|
||||
print(f"GO: {len(passed)} tickers passed (>= 150 threshold)")
|
||||
elif len(passed) >= 100:
|
||||
print(f"MARGINAL: {len(passed)} tickers passed (100-150 range, consider adding more)")
|
||||
else:
|
||||
print(f"NO-GO: Only {len(passed)} tickers passed (< 100, need more tickers)")
|
||||
print(f"{'='*80}\n")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="Coverage Check for Symbol Universe")
|
||||
parser.add_argument(
|
||||
"--symbols-file",
|
||||
required=True,
|
||||
help="Path to symbols YAML file",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--start-date",
|
||||
default="2022-07-01",
|
||||
help="Start date for data coverage check (YYYY-MM-DD)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output-dir",
|
||||
default="./data/analysis",
|
||||
help="Output directory for CSV report",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--write-filtered",
|
||||
action="store_true",
|
||||
help="Write filtered YAML with only passing tickers",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--concurrency",
|
||||
type=int,
|
||||
default=5,
|
||||
help="Max concurrent API calls (default: 5)",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
symbols = _load_symbols(args.symbols_file)
|
||||
if not symbols:
|
||||
print(f"No symbols found in {args.symbols_file}")
|
||||
return
|
||||
|
||||
asyncio.run(
|
||||
run_coverage_check(
|
||||
symbols=symbols,
|
||||
start_date=args.start_date,
|
||||
output_dir=Path(args.output_dir),
|
||||
write_filtered=args.write_filtered,
|
||||
symbols_file=args.symbols_file,
|
||||
concurrency=args.concurrency,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -0,0 +1,391 @@
|
||||
"""Diagnostic Experiments: Fixed-holding & entry-timing analysis.
|
||||
|
||||
Experiment 1 — Stop-off + fixed holding period exit (3/5/10/20d)
|
||||
Purpose: Does the raw signal carry time-based alpha?
|
||||
Method: Use pre-computed fwd_return_Xd (no stop, no target, pure hold).
|
||||
Report: Mean return, median, win rate, by score bucket and event type.
|
||||
|
||||
Experiment 2 — Reaction-day close entry vs next-open entry
|
||||
Purpose: Is the overnight gap eating the edge?
|
||||
Method: Compare fwd_return_Xd (next-open base) with
|
||||
close_entry_return = (entry_price / event_close) * (1 + fwd_return_Xd) - 1
|
||||
Constraint: Only pre-market/intraday filings (reaction_date == event_date)
|
||||
are eligible for close entry; after-close filings shown separately.
|
||||
|
||||
Usage:
|
||||
python -m dev.analysis.entry_timing_experiment \
|
||||
--snapshot-dir data/datasets/snapshots/a6687401-afdd-4bb7-9bb1-4cd32c5190bb \
|
||||
--split train
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import statistics
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import pyarrow.parquet as pq
|
||||
|
||||
from libs.backtest.scoring import compute_entry_score
|
||||
|
||||
|
||||
FWD_HORIZONS = ["fwd_return_3d", "fwd_return_5d", "fwd_return_10d", "fwd_return_20d"]
|
||||
HORIZON_LABELS = ["3d", "5d", "10d", "20d"]
|
||||
|
||||
|
||||
def _load_rows(snapshot_dir: Path, split: str) -> list[dict[str, Any]]:
|
||||
table = pq.read_table(snapshot_dir / f"{split}.parquet")
|
||||
return table.to_pylist()
|
||||
|
||||
|
||||
def _stats(values: list[float]) -> dict[str, float | None]:
|
||||
if not values:
|
||||
return {"mean": None, "median": None, "win_rate": None, "n": 0}
|
||||
return {
|
||||
"mean": statistics.mean(values),
|
||||
"median": statistics.median(values),
|
||||
"win_rate": sum(1 for v in values if v > 0) / len(values),
|
||||
"n": len(values),
|
||||
}
|
||||
|
||||
|
||||
def _fmt(val: float | None, pct: bool = True) -> str:
|
||||
if val is None:
|
||||
return " N/A"
|
||||
if pct:
|
||||
return f"{val:+.2%}"
|
||||
return f"{val:.1%}"
|
||||
|
||||
|
||||
def _score_bucket(score: float) -> str:
|
||||
if score < 0.3:
|
||||
return "low(<0.3)"
|
||||
if score < 0.5:
|
||||
return "mid(0.3-0.5)"
|
||||
if score < 0.7:
|
||||
return "high(0.5-0.7)"
|
||||
return "top(>=0.7)"
|
||||
|
||||
|
||||
BUCKET_ORDER = ["low(<0.3)", "mid(0.3-0.5)", "high(0.5-0.7)", "top(>=0.7)"]
|
||||
|
||||
|
||||
def _is_intraday_filing(row: dict) -> bool:
|
||||
"""Pre-market / regular-hours filing: reaction_date == event_date."""
|
||||
return row.get("reaction_date") == row.get("event_date")
|
||||
|
||||
|
||||
# ── Experiment 1 ──────────────────────────────────────────────────────
|
||||
|
||||
def experiment_1(rows: list[dict[str, Any]]) -> None:
|
||||
"""Stop-off + fixed holding period: raw forward returns."""
|
||||
print(f"\n{'='*100}")
|
||||
print("EXPERIMENT 1: Stop-off + Fixed Holding Period Exit")
|
||||
print(f"{'='*100}")
|
||||
|
||||
# Enrich with score
|
||||
for r in rows:
|
||||
r["_score"] = compute_entry_score(r)
|
||||
r["_bucket"] = _score_bucket(r["_score"])
|
||||
|
||||
# ── 1a. ALL events ──
|
||||
print(f"\n--- 1a. ALL events (N={len(rows)}) ---")
|
||||
_print_horizon_table("ALL", rows)
|
||||
|
||||
# ── 1b. By score bucket ──
|
||||
print(f"\n--- 1b. By score bucket ---")
|
||||
by_bucket: dict[str, list] = {b: [] for b in BUCKET_ORDER}
|
||||
for r in rows:
|
||||
by_bucket[r["_bucket"]].append(r)
|
||||
|
||||
header = f"{'Bucket':<16} {'N':>5}"
|
||||
for lbl in HORIZON_LABELS:
|
||||
header += f" {lbl+'_mean':>8} {lbl+'_med':>8} {lbl+'_wr':>7}"
|
||||
print(header)
|
||||
print("-" * len(header))
|
||||
|
||||
for bucket in BUCKET_ORDER:
|
||||
items = by_bucket[bucket]
|
||||
line = f"{bucket:<16} {len(items):>5}"
|
||||
for h in FWD_HORIZONS:
|
||||
vals = [float(r[h]) for r in items if r.get(h) is not None]
|
||||
s = _stats(vals)
|
||||
line += f" {_fmt(s['mean']):>8} {_fmt(s['median']):>8} {_fmt(s['win_rate'], pct=False):>7}"
|
||||
print(line)
|
||||
|
||||
# ── 1c. By event_type ──
|
||||
print(f"\n--- 1c. By event_type ---")
|
||||
by_type: dict[str, list] = {}
|
||||
for r in rows:
|
||||
et = r.get("event_type", "unknown")
|
||||
by_type.setdefault(et, []).append(r)
|
||||
|
||||
header = f"{'EventType':<25} {'N':>5} {'AvgScr':>7}"
|
||||
for lbl in HORIZON_LABELS:
|
||||
header += f" {lbl+'_mean':>8} {lbl+'_wr':>7}"
|
||||
print(header)
|
||||
print("-" * len(header))
|
||||
|
||||
for et in sorted(by_type, key=lambda k: -len(by_type[k])):
|
||||
items = by_type[et]
|
||||
avg_score = statistics.mean(r["_score"] for r in items)
|
||||
line = f"{et:<25} {len(items):>5} {avg_score:>7.3f}"
|
||||
for h in FWD_HORIZONS:
|
||||
vals = [float(r[h]) for r in items if r.get(h) is not None]
|
||||
s = _stats(vals)
|
||||
line += f" {_fmt(s['mean']):>8} {_fmt(s['win_rate'], pct=False):>7}"
|
||||
print(line)
|
||||
|
||||
# ── 1d. MFE/MAE analysis (reward:risk) ──
|
||||
print(f"\n--- 1d. MFE/MAE analysis (max favorable / max adverse excursion) ---")
|
||||
mfe_mae_horizons = [("3d", "mfe_3d", "mae_3d"),
|
||||
("5d", "mfe_5d", "mae_5d"),
|
||||
("10d", "mfe_10d", "mae_10d"),
|
||||
("20d", "mfe_20d", "mae_20d")]
|
||||
|
||||
header = f"{'Horizon':<10} {'MFE_mean':>10} {'MAE_mean':>10} {'R:R':>8} {'MFE_med':>10} {'MAE_med':>10}"
|
||||
print(header)
|
||||
print("-" * len(header))
|
||||
|
||||
for lbl, mfe_col, mae_col in mfe_mae_horizons:
|
||||
mfe_vals = [float(r[mfe_col]) for r in rows if r.get(mfe_col) is not None]
|
||||
mae_vals = [float(r[mae_col]) for r in rows if r.get(mae_col) is not None]
|
||||
if mfe_vals and mae_vals:
|
||||
mfe_mean = statistics.mean(mfe_vals)
|
||||
mae_mean = statistics.mean(mae_vals)
|
||||
rr = mfe_mean / abs(mae_mean) if mae_mean != 0 else 0
|
||||
mfe_med = statistics.median(mfe_vals)
|
||||
mae_med = statistics.median(mae_vals)
|
||||
print(f"{lbl:<10} {mfe_mean:>+10.2%} {mae_mean:>+10.2%} {rr:>8.2f} {mfe_med:>+10.2%} {mae_med:>+10.2%}")
|
||||
|
||||
# ── 1e. Score monotonicity per horizon ──
|
||||
print(f"\n--- 1e. Score monotonicity (does higher score → better return?) ---")
|
||||
for h, lbl in zip(FWD_HORIZONS, HORIZON_LABELS):
|
||||
bucket_means = []
|
||||
for bucket in BUCKET_ORDER:
|
||||
vals = [float(r[h]) for r in by_bucket[bucket] if r.get(h) is not None]
|
||||
bucket_means.append(statistics.mean(vals) if vals else None)
|
||||
|
||||
valid_means = [m for m in bucket_means if m is not None]
|
||||
monotonic = all(a <= b for a, b in zip(valid_means, valid_means[1:])) if len(valid_means) >= 2 else False
|
||||
direction = "MONOTONIC" if monotonic else "NOT monotonic"
|
||||
means_str = " → ".join(f"{m:+.2%}" if m is not None else "N/A" for m in bucket_means)
|
||||
print(f" {lbl}: {means_str} [{direction}]")
|
||||
|
||||
|
||||
# ── Experiment 2 ──────────────────────────────────────────────────────
|
||||
|
||||
def experiment_2(rows: list[dict[str, Any]]) -> None:
|
||||
"""Reaction-close entry vs next-open entry."""
|
||||
print(f"\n{'='*100}")
|
||||
print("EXPERIMENT 2: Reaction-day Close Entry vs Next-Open Entry")
|
||||
print(f"{'='*100}")
|
||||
|
||||
# Split by filing timing
|
||||
intraday = [r for r in rows if _is_intraday_filing(r)]
|
||||
after_close = [r for r in rows if not _is_intraday_filing(r)]
|
||||
|
||||
print(f"\nFiling timing split:")
|
||||
print(f" Pre-market / intraday (reaction_date == event_date): {len(intraday)}")
|
||||
print(f" After-close (reaction_date > event_date): {len(after_close)}")
|
||||
|
||||
# ── 2a. Overnight gap analysis (intraday filings only) ──
|
||||
print(f"\n--- 2a. Overnight gap cost (intraday filings, N={len(intraday)}) ---")
|
||||
gaps = []
|
||||
for r in intraday:
|
||||
ep = r.get("entry_price")
|
||||
ec = r.get("event_close")
|
||||
if ep and ec and ec > 0:
|
||||
gap = ep / ec - 1.0
|
||||
gaps.append(gap)
|
||||
|
||||
if gaps:
|
||||
s = _stats(gaps)
|
||||
print(f" Overnight gap (next_open / reaction_close - 1):")
|
||||
print(f" Mean: {_fmt(s['mean'])}")
|
||||
print(f" Median: {_fmt(s['median'])}")
|
||||
print(f" Gap up rate (open > close): {_fmt(s['win_rate'], pct=False)}")
|
||||
print(f" Std: {statistics.stdev(gaps):+.2%}" if len(gaps) > 1 else "")
|
||||
|
||||
# Distribution
|
||||
gap_buckets = {"< -2%": 0, "-2% to 0%": 0, "0% to +1%": 0,
|
||||
"+1% to +3%": 0, "+3% to +5%": 0, "> +5%": 0}
|
||||
for g in gaps:
|
||||
if g < -0.02:
|
||||
gap_buckets["< -2%"] += 1
|
||||
elif g < 0:
|
||||
gap_buckets["-2% to 0%"] += 1
|
||||
elif g < 0.01:
|
||||
gap_buckets["0% to +1%"] += 1
|
||||
elif g < 0.03:
|
||||
gap_buckets["+1% to +3%"] += 1
|
||||
elif g < 0.05:
|
||||
gap_buckets["+3% to +5%"] += 1
|
||||
else:
|
||||
gap_buckets["> +5%"] += 1
|
||||
|
||||
print(f"\n Gap distribution:")
|
||||
for label, count in gap_buckets.items():
|
||||
pct = count / len(gaps) * 100
|
||||
bar = "#" * int(pct / 2)
|
||||
print(f" {label:>12}: {count:>4} ({pct:5.1f}%) {bar}")
|
||||
|
||||
# ── 2b. Close entry vs next-open returns (intraday only) ──
|
||||
print(f"\n--- 2b. Close entry vs Next-open entry returns (intraday, N={len(intraday)}) ---")
|
||||
|
||||
header = f"{'Horizon':<10} {'NextOpen_mean':>13} {'NextOpen_wr':>11} {'CloseEntry_mean':>15} {'CloseEntry_wr':>13} {'Diff':>8}"
|
||||
print(header)
|
||||
print("-" * len(header))
|
||||
|
||||
for h, lbl in zip(FWD_HORIZONS, HORIZON_LABELS):
|
||||
next_open_vals = []
|
||||
close_entry_vals = []
|
||||
|
||||
for r in intraday:
|
||||
fwd = r.get(h)
|
||||
ep = r.get("entry_price")
|
||||
ec = r.get("event_close")
|
||||
if fwd is not None and ep and ec and ec > 0:
|
||||
fwd_f = float(fwd)
|
||||
next_open_vals.append(fwd_f)
|
||||
# close_entry_return = (entry_price / event_close) * (1 + fwd) - 1
|
||||
close_ret = (ep / ec) * (1.0 + fwd_f) - 1.0
|
||||
close_entry_vals.append(close_ret)
|
||||
|
||||
no_s = _stats(next_open_vals)
|
||||
ce_s = _stats(close_entry_vals)
|
||||
diff = (ce_s["mean"] - no_s["mean"]) if ce_s["mean"] is not None and no_s["mean"] is not None else None
|
||||
diff_str = _fmt(diff) if diff is not None else " N/A"
|
||||
|
||||
print(f"{lbl:<10} {_fmt(no_s['mean']):>13} {_fmt(no_s['win_rate'], pct=False):>11}"
|
||||
f" {_fmt(ce_s['mean']):>15} {_fmt(ce_s['win_rate'], pct=False):>13} {diff_str:>8}")
|
||||
|
||||
# ── 2c. After-close filings (separate analysis) ──
|
||||
if after_close:
|
||||
print(f"\n--- 2c. After-close filings (N={len(after_close)}) ---")
|
||||
print(f" (These events filed after market close; reaction_date = next trading day)")
|
||||
|
||||
header = f"{'Horizon':<10} {'NextOpen_mean':>13} {'NextOpen_wr':>11}"
|
||||
print(f" {header}")
|
||||
print(f" {'-' * len(header)}")
|
||||
|
||||
for h, lbl in zip(FWD_HORIZONS, HORIZON_LABELS):
|
||||
vals = [float(r[h]) for r in after_close if r.get(h) is not None]
|
||||
s = _stats(vals)
|
||||
print(f" {lbl:<10} {_fmt(s['mean']):>13} {_fmt(s['win_rate'], pct=False):>11}")
|
||||
|
||||
# ── 2d. Close entry by event type (intraday only) ──
|
||||
print(f"\n--- 2d. Close-entry returns by event type (intraday only) ---")
|
||||
by_type: dict[str, list] = {}
|
||||
for r in intraday:
|
||||
et = r.get("event_type", "unknown")
|
||||
by_type.setdefault(et, []).append(r)
|
||||
|
||||
header = f"{'EventType':<25} {'N':>5} {'Gap_mean':>9}"
|
||||
for lbl in HORIZON_LABELS:
|
||||
header += f" {lbl+'_CE':>8} {lbl+'_NO':>8}"
|
||||
print(header)
|
||||
print("-" * len(header))
|
||||
|
||||
for et in sorted(by_type, key=lambda k: -len(by_type[k])):
|
||||
items = by_type[et]
|
||||
# Gap
|
||||
type_gaps = []
|
||||
for r in items:
|
||||
ep = r.get("entry_price")
|
||||
ec = r.get("event_close")
|
||||
if ep and ec and ec > 0:
|
||||
type_gaps.append(ep / ec - 1.0)
|
||||
|
||||
gap_mean = statistics.mean(type_gaps) if type_gaps else None
|
||||
line = f"{et:<25} {len(items):>5} {_fmt(gap_mean):>9}"
|
||||
|
||||
for h in FWD_HORIZONS:
|
||||
ce_vals = []
|
||||
no_vals = []
|
||||
for r in items:
|
||||
fwd = r.get(h)
|
||||
ep = r.get("entry_price")
|
||||
ec = r.get("event_close")
|
||||
if fwd is not None and ep and ec and ec > 0:
|
||||
fwd_f = float(fwd)
|
||||
no_vals.append(fwd_f)
|
||||
ce_vals.append((ep / ec) * (1.0 + fwd_f) - 1.0)
|
||||
ce_s = _stats(ce_vals)
|
||||
no_s = _stats(no_vals)
|
||||
line += f" {_fmt(ce_s['mean']):>8} {_fmt(no_s['mean']):>8}"
|
||||
print(line)
|
||||
|
||||
# ── 2e. Score-filtered close entry (score >= 0.5, intraday) ──
|
||||
filtered = [r for r in intraday if r.get("_score", 0) >= 0.5]
|
||||
if filtered:
|
||||
print(f"\n--- 2e. Score-filtered (>= 0.5) close entry (intraday, N={len(filtered)}) ---")
|
||||
|
||||
header = f"{'Horizon':<10} {'CloseEntry_mean':>15} {'CE_wr':>8} {'NextOpen_mean':>14} {'NO_wr':>8}"
|
||||
print(header)
|
||||
print("-" * len(header))
|
||||
|
||||
for h, lbl in zip(FWD_HORIZONS, HORIZON_LABELS):
|
||||
ce_vals = []
|
||||
no_vals = []
|
||||
for r in filtered:
|
||||
fwd = r.get(h)
|
||||
ep = r.get("entry_price")
|
||||
ec = r.get("event_close")
|
||||
if fwd is not None and ep and ec and ec > 0:
|
||||
fwd_f = float(fwd)
|
||||
no_vals.append(fwd_f)
|
||||
ce_vals.append((ep / ec) * (1.0 + fwd_f) - 1.0)
|
||||
ce_s = _stats(ce_vals)
|
||||
no_s = _stats(no_vals)
|
||||
print(f"{lbl:<10} {_fmt(ce_s['mean']):>15} {_fmt(ce_s['win_rate'], pct=False):>8}"
|
||||
f" {_fmt(no_s['mean']):>14} {_fmt(no_s['win_rate'], pct=False):>8}")
|
||||
|
||||
|
||||
def _print_horizon_table(label: str, items: list[dict]) -> None:
|
||||
header = f"{'Horizon':<10} {'Mean':>10} {'Median':>10} {'WinRate':>8} {'N':>6} {'StdDev':>10}"
|
||||
print(header)
|
||||
print("-" * len(header))
|
||||
for h, lbl in zip(FWD_HORIZONS, HORIZON_LABELS):
|
||||
vals = [float(r[h]) for r in items if r.get(h) is not None]
|
||||
s = _stats(vals)
|
||||
std = statistics.stdev(vals) if len(vals) > 1 else 0
|
||||
print(f"{lbl:<10} {_fmt(s['mean']):>10} {_fmt(s['median']):>10}"
|
||||
f" {_fmt(s['win_rate'], pct=False):>8} {s['n']:>6} {std:>10.2%}")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="Entry Timing Experiments")
|
||||
parser.add_argument("--snapshot-dir", required=True)
|
||||
parser.add_argument("--split", default="train")
|
||||
args = parser.parse_args()
|
||||
|
||||
rows = _load_rows(Path(args.snapshot_dir), args.split)
|
||||
if not rows:
|
||||
print("No data found.")
|
||||
return
|
||||
|
||||
print(f"Loaded {len(rows)} events from {args.split} split")
|
||||
|
||||
experiment_1(rows)
|
||||
experiment_2(rows)
|
||||
|
||||
# ── Summary ──
|
||||
print(f"\n{'='*100}")
|
||||
print("KEY DIAGNOSTIC QUESTIONS")
|
||||
print(f"{'='*100}")
|
||||
print("""
|
||||
Exp 1 — Time-based alpha:
|
||||
• Are any fixed-horizon returns consistently positive?
|
||||
• Does score monotonicity hold at any horizon?
|
||||
• Which event types carry actual alpha (mean > 0, win rate > 50%)?
|
||||
|
||||
Exp 2 — Entry timing:
|
||||
• Is the overnight gap systematically positive (eating the edge)?
|
||||
• Does close-entry improve returns vs next-open?
|
||||
• Is the gap effect uniform across event types, or concentrated?
|
||||
""")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -0,0 +1,141 @@
|
||||
"""Fix documents and events with NULL symbol_id / issuer_id.
|
||||
|
||||
The filing_poller stores ticker in document_id as TICKER::{ticker}.
|
||||
When issuer_sync hadn't run before filing_poller, the lookup maps were
|
||||
empty and symbol_id / issuer_id were inserted as NULL.
|
||||
|
||||
This script:
|
||||
1. Extracts ticker from document_id for NULL-symbol docs
|
||||
2. Looks up correct symbol_id / issuer_id from symbol_master / issuer_master
|
||||
3. Updates documents and events
|
||||
|
||||
Usage:
|
||||
python -m apps.tools.fix_null_symbols [--dry-run]
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
|
||||
from sqlalchemy import select, text, update
|
||||
|
||||
from libs.common.config import get_settings
|
||||
from libs.common.logging import configure_logging, get_logger
|
||||
from libs.db.models import Document, Event, IssuerMaster, SymbolMaster
|
||||
from libs.db.session import get_session
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
async def fix_null_symbols(dry_run: bool = False) -> dict[str, int]:
|
||||
stats = {"docs_total_null": 0, "docs_fixed": 0, "docs_no_match": 0,
|
||||
"events_fixed": 0, "events_no_match": 0}
|
||||
|
||||
async with get_session() as session:
|
||||
# 1. Build ticker lookup maps from current DB
|
||||
issuer_rows = await session.execute(select(IssuerMaster))
|
||||
ticker_to_issuer: dict[str, str] = {}
|
||||
for im in issuer_rows.scalars().all():
|
||||
if im.ticker:
|
||||
ticker_to_issuer[im.ticker.upper()] = im.issuer_id
|
||||
|
||||
symbol_rows = await session.execute(
|
||||
select(SymbolMaster).where(SymbolMaster.is_primary == True) # noqa: E712
|
||||
)
|
||||
ticker_to_symbol: dict[str, str] = {}
|
||||
for sm in symbol_rows.scalars().all():
|
||||
if sm.ticker:
|
||||
ticker_to_symbol[sm.ticker.upper()] = sm.symbol_id
|
||||
|
||||
print(f"Lookup maps: {len(ticker_to_issuer)} issuers, {len(ticker_to_symbol)} symbols")
|
||||
|
||||
# 2. Find documents with NULL symbol_id
|
||||
null_docs = await session.execute(
|
||||
select(Document).where(Document.symbol_id.is_(None))
|
||||
)
|
||||
docs = null_docs.scalars().all()
|
||||
stats["docs_total_null"] = len(docs)
|
||||
print(f"Documents with NULL symbol_id: {len(docs)}")
|
||||
|
||||
# 3. Extract ticker from document_id and update
|
||||
no_match_tickers: set[str] = set()
|
||||
for doc in docs:
|
||||
# document_id format: DOC::sec::TICKER::{ticker}::{date}::{accession}
|
||||
parts = doc.document_id.split("::")
|
||||
if len(parts) >= 4 and parts[2] == "TICKER":
|
||||
ticker = parts[3].upper()
|
||||
else:
|
||||
logger.warning("unexpected_doc_id_format", document_id=doc.document_id)
|
||||
stats["docs_no_match"] += 1
|
||||
continue
|
||||
|
||||
sym_id = ticker_to_symbol.get(ticker)
|
||||
iss_id = ticker_to_issuer.get(ticker)
|
||||
|
||||
if sym_id:
|
||||
doc.symbol_id = sym_id
|
||||
doc.issuer_id = iss_id # may still be None if issuer doesn't exist
|
||||
stats["docs_fixed"] += 1
|
||||
else:
|
||||
no_match_tickers.add(ticker)
|
||||
stats["docs_no_match"] += 1
|
||||
|
||||
if no_match_tickers:
|
||||
print(f"No symbol_master match for {len(no_match_tickers)} tickers: "
|
||||
f"{sorted(no_match_tickers)[:20]}{'...' if len(no_match_tickers) > 20 else ''}")
|
||||
|
||||
# 4. Fix events with NULL symbol_id (join via primary_document_id)
|
||||
null_events = await session.execute(
|
||||
select(Event).where(Event.symbol_id.is_(None))
|
||||
)
|
||||
events = null_events.scalars().all()
|
||||
print(f"Events with NULL symbol_id: {len(events)}")
|
||||
|
||||
for evt in events:
|
||||
# Look up the parent document
|
||||
doc_result = await session.execute(
|
||||
select(Document).where(Document.document_id == evt.primary_document_id)
|
||||
)
|
||||
parent_doc = doc_result.scalar_one_or_none()
|
||||
if parent_doc and parent_doc.symbol_id:
|
||||
evt.symbol_id = parent_doc.symbol_id
|
||||
evt.issuer_id = parent_doc.issuer_id
|
||||
stats["events_fixed"] += 1
|
||||
else:
|
||||
stats["events_no_match"] += 1
|
||||
|
||||
# 5. Commit or rollback
|
||||
if dry_run:
|
||||
print("\n[DRY RUN] Rolling back — no changes saved.")
|
||||
await session.rollback()
|
||||
else:
|
||||
await session.flush()
|
||||
print("\nChanges committed.")
|
||||
|
||||
# Report
|
||||
print(f"\n{'='*50}")
|
||||
print("Fix NULL Symbols Report")
|
||||
print(f"{'='*50}")
|
||||
print(f" Documents with NULL symbol_id: {stats['docs_total_null']}")
|
||||
print(f" Documents fixed: {stats['docs_fixed']}")
|
||||
print(f" Documents no match: {stats['docs_no_match']}")
|
||||
print(f" Events fixed: {stats['events_fixed']}")
|
||||
print(f" Events no match: {stats['events_no_match']}")
|
||||
print(f"{'='*50}")
|
||||
|
||||
return stats
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="Fix NULL symbol_id in documents and events")
|
||||
parser.add_argument("--dry-run", action="store_true", help="Preview changes without saving")
|
||||
args = parser.parse_args()
|
||||
|
||||
settings = get_settings()
|
||||
configure_logging(settings.log_level)
|
||||
|
||||
asyncio.run(fix_null_symbols(dry_run=args.dry_run))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -0,0 +1,155 @@
|
||||
"""Universe Builder: expand symbol universe via Stock Oracle screener API.
|
||||
|
||||
Fetches large-cap liquid equities from screener, merges with existing
|
||||
symbols.yaml, and writes the union back.
|
||||
|
||||
Usage:
|
||||
python -m apps.tools.universe_builder [--keep-existing] [--output configs/symbols.yaml]
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
|
||||
from libs.common.config import get_settings
|
||||
from libs.common.logging import configure_logging, get_logger
|
||||
from libs.oracle_client import ScreenerService, make_oracle_client
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
# ── Screener defaults ──────────────────────────────────────────────
|
||||
SCREENER_DEFAULTS = {
|
||||
"market_cap_min": 10_000_000_000, # $10B
|
||||
"min_avg_volume": 1_000_000, # 1M shares/day
|
||||
"exchange": "NYSE,NASDAQ",
|
||||
"exclude_types": "ETF,FUND",
|
||||
"price_min": 5,
|
||||
}
|
||||
|
||||
|
||||
def load_existing_symbols(path: Path) -> list[str]:
|
||||
"""Load current symbols.yaml and return list of tickers."""
|
||||
if not path.exists():
|
||||
return []
|
||||
with open(path) as f:
|
||||
cfg = yaml.safe_load(f) or {}
|
||||
return cfg.get("symbols", [])
|
||||
|
||||
|
||||
def build_symbols_yaml(
|
||||
existing: list[str],
|
||||
screener_results: list[dict],
|
||||
) -> str:
|
||||
"""Build YAML content: existing-only tickers first, then screener (sorted)."""
|
||||
screener_set: set[str] = set()
|
||||
for item in screener_results:
|
||||
ticker = item.get("symbol", "")
|
||||
if ticker:
|
||||
screener_set.add(ticker.upper())
|
||||
|
||||
existing_set = {s.upper() for s in existing}
|
||||
existing_only = sorted(existing_set - screener_set)
|
||||
screener_sorted = sorted(screener_set)
|
||||
|
||||
lines = ["symbols:"]
|
||||
if existing_only:
|
||||
lines.append(" # --- Existing-only (not in screener, kept) ---")
|
||||
for t in existing_only:
|
||||
lines.append(f" - {t}")
|
||||
lines.append(f" # --- Screener ($10B+ mcap, 1M+ vol) — {len(screener_sorted)} symbols ---")
|
||||
for t in screener_sorted:
|
||||
lines.append(f" - {t}")
|
||||
lines.append("") # trailing newline
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
async def run_universe_builder(
|
||||
keep_existing: bool,
|
||||
output_path: Path,
|
||||
) -> dict[str, int]:
|
||||
existing = load_existing_symbols(output_path) if keep_existing else []
|
||||
existing_set = {s.upper() for s in existing}
|
||||
|
||||
async with make_oracle_client() as client:
|
||||
svc = ScreenerService(client)
|
||||
screener_stocks = await svc.search_all_stocks(**SCREENER_DEFAULTS)
|
||||
|
||||
screener_results = [{"symbol": s.symbol, "name": s.name} for s in screener_stocks]
|
||||
screener_tickers = {s.symbol.upper() for s in screener_stocks if s.symbol}
|
||||
|
||||
overlap = existing_set & screener_tickers
|
||||
new_only = screener_tickers - existing_set
|
||||
existing_only = existing_set - screener_tickers
|
||||
union = existing_set | screener_tickers
|
||||
|
||||
stats = {
|
||||
"screener": len(screener_tickers),
|
||||
"existing": len(existing_set),
|
||||
"overlap": len(overlap),
|
||||
"existing_only": len(existing_only),
|
||||
"new": len(new_only),
|
||||
"total": len(union),
|
||||
}
|
||||
|
||||
yaml_content = build_symbols_yaml(existing, screener_results)
|
||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
output_path.write_text(yaml_content)
|
||||
|
||||
# Report
|
||||
logger.info("universe_report", **stats)
|
||||
print(f"\n{'='*50}")
|
||||
print("Universe Builder Report")
|
||||
print(f"{'='*50}")
|
||||
print(f" Screener results: {stats['screener']:>6}")
|
||||
print(f" Existing symbols: {stats['existing']:>6}")
|
||||
print(f" Overlap: {stats['overlap']:>6}")
|
||||
print(f" Existing-only (kept): {stats['existing_only']:>6}")
|
||||
print(f" New additions: {stats['new']:>6}")
|
||||
print(f" Total (union): {stats['total']:>6}")
|
||||
print(f"{'='*50}")
|
||||
print(f" Written to: {output_path}")
|
||||
|
||||
if existing_only:
|
||||
print(f"\n Existing-only tickers (kept): {sorted(existing_only)}")
|
||||
|
||||
return stats
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Build symbol universe from Stock Oracle screener",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--keep-existing",
|
||||
action="store_true",
|
||||
default=True,
|
||||
help="Keep all existing symbols (default: True)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-keep-existing",
|
||||
action="store_false",
|
||||
dest="keep_existing",
|
||||
help="Replace existing symbols entirely with screener results",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output",
|
||||
default="configs/symbols.yaml",
|
||||
help="Output YAML path (default: configs/symbols.yaml)",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
settings = get_settings()
|
||||
configure_logging(settings.log_level)
|
||||
|
||||
asyncio.run(run_universe_builder(
|
||||
keep_existing=args.keep_existing,
|
||||
output_path=Path(args.output),
|
||||
))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -0,0 +1,86 @@
|
||||
{
|
||||
"strategy_name": "smallmid_swing_v1",
|
||||
"dataset_snapshot_id": "pending_phase7",
|
||||
"universe": {
|
||||
"min_price": 5.0,
|
||||
"min_avg_dollar_volume": 300000.0,
|
||||
"exclude_asset_types": ["ETF", "FUND"]
|
||||
},
|
||||
"signal": {
|
||||
"score_threshold": 0.55,
|
||||
"max_candidates_per_day": 3,
|
||||
"execution_timing": "next_open",
|
||||
"decision_timing": "reaction_close",
|
||||
"ranking_fields": ["score", "avg_dollar_volume"]
|
||||
},
|
||||
"risk": {
|
||||
"per_trade_risk_pct": 0.005,
|
||||
"max_daily_new_risk_pct": 0.015,
|
||||
"max_positions": 4,
|
||||
"max_positions_per_sector": 2,
|
||||
"max_position_value_pct": 0.10,
|
||||
"max_adv_fraction": 0.02,
|
||||
"cooldown_after_loss_streak": 3,
|
||||
"cooldown_days": 2,
|
||||
"macro_regime_enabled": false,
|
||||
"macro_sma_period": 20,
|
||||
"stop_atr_multiplier": 3.0,
|
||||
"backtest_mode": "research",
|
||||
"kill_switch_cooldown_days": 20,
|
||||
"kill_switch_log_only": true,
|
||||
"veto_oneoff_penalty": 0.7,
|
||||
"veto_parse_confidence_min": 0.4,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": true
|
||||
},
|
||||
"execution": {
|
||||
"entry_fill_model": "next_open",
|
||||
"exit_fill_model": "daily_bar_approximation",
|
||||
"slippage_bps_base": 25.0,
|
||||
"commission_per_share": 0.005,
|
||||
"same_bar_priority": "stop_first_conservative",
|
||||
"target_model": "atr_multiple",
|
||||
"target_1_r": 2.0,
|
||||
"target_atr_multiplier": 1.5,
|
||||
"target_1_fraction": 0.5,
|
||||
"trailing_model": "pct_5",
|
||||
"trailing_warmup_days": 2,
|
||||
"max_holding_days": 15,
|
||||
"no_follow_through_exit": false
|
||||
},
|
||||
"reporting": {
|
||||
"write_trade_blotter": true,
|
||||
"write_equity_curve": true,
|
||||
"write_metrics_summary": true,
|
||||
"generate_plots": false,
|
||||
"attribution_buckets": ["event_type", "sector", "score_bucket"]
|
||||
},
|
||||
"event_type_profiles": {
|
||||
"earnings_release": {
|
||||
"enabled": true,
|
||||
"max_holding_days_override": 15,
|
||||
"direction_filter": "any"
|
||||
},
|
||||
"guidance_update": {
|
||||
"enabled": true,
|
||||
"max_holding_days_override": 15,
|
||||
"direction_filter": "any"
|
||||
},
|
||||
"management_change": {
|
||||
"enabled": true,
|
||||
"direction_filter": "any"
|
||||
},
|
||||
"material_contract": {
|
||||
"enabled": true,
|
||||
"max_holding_days_override": 10,
|
||||
"direction_filter": "any"
|
||||
},
|
||||
"unknown": {
|
||||
"enabled": true,
|
||||
"direction_filter": "any"
|
||||
},
|
||||
"other_material_event": {
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
{
|
||||
"experiment_name": "earnings_smallmid_v1",
|
||||
"dataset_snapshot_id": "smallmid-only-8ef47ee5",
|
||||
"description": "PEAD alpha test on small/mid-cap earnings. Tests hypothesis that PEAD is stronger in less efficient market segments.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": {
|
||||
"enabled": true,
|
||||
"direction_filter": "bullish_only"
|
||||
},
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": { "score_threshold": 0.45 }
|
||||
},
|
||||
"tags": ["earnings-only", "smallmid", "pead"]
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
{
|
||||
"experiment_name": "earnings_smallmid_v2",
|
||||
"dataset_snapshot_id": "smallmid-only-8ef47ee5",
|
||||
"description": "PEAD small/mid-cap v2: relaxed oneoff filter (0.9), more positions (6), more candidates/day (5). Tests if conservative filters were destroying alpha.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": {
|
||||
"enabled": true,
|
||||
"direction_filter": "bullish_only"
|
||||
},
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"score_threshold": 0.45,
|
||||
"max_candidates_per_day": 5
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 6,
|
||||
"max_positions_per_sector": 3,
|
||||
"veto_oneoff_penalty": 0.9
|
||||
}
|
||||
},
|
||||
"tags": ["earnings-only", "smallmid", "pead", "relaxed-filters"]
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
{
|
||||
"experiment_name": "earnings_smallmid_v3",
|
||||
"dataset_snapshot_id": "smallmid-only-8ef47ee5",
|
||||
"description": "PEAD small/mid-cap v3: oneoff filter effectively disabled, 8 max positions, 8 candidates/day. Maximum exposure to test raw PEAD signal.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": {
|
||||
"enabled": true,
|
||||
"direction_filter": "bullish_only"
|
||||
},
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"score_threshold": 0.45,
|
||||
"max_candidates_per_day": 8
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 4,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"per_trade_risk_pct": 0.005,
|
||||
"max_daily_new_risk_pct": 0.025
|
||||
}
|
||||
},
|
||||
"tags": ["earnings-only", "smallmid", "pead", "max-exposure"]
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
{
|
||||
"experiment_name": "exec_opt_a_wider",
|
||||
"dataset_snapshot_id": "cd664594-6a6c-4e16-afef-9104ce900f33",
|
||||
"description": "Execution opt A: wider stops (3.5 ATR), higher targets (2.0 ATR), wider trailing (7%), longer hold (20d). Let winners run.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"signal": { "score_threshold": 0.5 },
|
||||
"risk": { "stop_atr_multiplier": 3.5 },
|
||||
"execution": {
|
||||
"target_atr_multiplier": 2.0,
|
||||
"trailing_model": "pct_7",
|
||||
"max_holding_days": 20
|
||||
}
|
||||
},
|
||||
"tags": ["exec-opt", "wider"]
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
{
|
||||
"experiment_name": "exec_opt_b_tight",
|
||||
"dataset_snapshot_id": "cd664594-6a6c-4e16-afef-9104ce900f33",
|
||||
"description": "Execution opt B: tighter stops (2.5 ATR), quick targets (1.5 ATR), tight trailing (3%), short hold (10d). Cut losses fast.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"signal": { "score_threshold": 0.5 },
|
||||
"risk": { "stop_atr_multiplier": 2.5 },
|
||||
"execution": {
|
||||
"target_atr_multiplier": 1.5,
|
||||
"trailing_model": "pct_3",
|
||||
"max_holding_days": 10
|
||||
}
|
||||
},
|
||||
"tags": ["exec-opt", "tight"]
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
{
|
||||
"experiment_name": "exec_opt_c_no_partial",
|
||||
"dataset_snapshot_id": "cd664594-6a6c-4e16-afef-9104ce900f33",
|
||||
"description": "Execution opt C: no partial exit (full position rides to trailing/stop/max_hold). Tests if partial exit is killing winners.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"signal": { "score_threshold": 0.5 },
|
||||
"execution": {
|
||||
"target_1_fraction": 0.0,
|
||||
"trailing_model": "pct_5",
|
||||
"max_holding_days": 15
|
||||
}
|
||||
},
|
||||
"tags": ["exec-opt", "no-partial"]
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
{
|
||||
"experiment_name": "exec_opt_d_wide_trail",
|
||||
"dataset_snapshot_id": "cd664594-6a6c-4e16-afef-9104ce900f33",
|
||||
"description": "Execution opt D: default stops/targets but much wider trailing (10%). Tests if trailing stop is choking winners prematurely.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"signal": { "score_threshold": 0.5 },
|
||||
"execution": {
|
||||
"trailing_model": "pct_10",
|
||||
"max_holding_days": 15
|
||||
}
|
||||
},
|
||||
"tags": ["exec-opt", "wide-trail"]
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
{
|
||||
"experiment_name": "exec_opt_e_big_winners",
|
||||
"dataset_snapshot_id": "cd664594-6a6c-4e16-afef-9104ce900f33",
|
||||
"description": "Execution opt E: big winner focus. Wider stops (3.5 ATR), high targets (2.5 ATR), wide trailing (10%), long hold (20d), small partial (33%).",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"signal": { "score_threshold": 0.5 },
|
||||
"risk": { "stop_atr_multiplier": 3.5 },
|
||||
"execution": {
|
||||
"target_atr_multiplier": 2.5,
|
||||
"target_1_fraction": 0.33,
|
||||
"trailing_model": "pct_10",
|
||||
"max_holding_days": 20
|
||||
}
|
||||
},
|
||||
"tags": ["exec-opt", "big-winners"]
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
{
|
||||
"experiment_name": "pead_10pct",
|
||||
"dataset_snapshot_id": "cd664594-6a6c-4e16-afef-9104ce900f33",
|
||||
"description": "PEAD 10% threshold: ultra-selective, only biggest earnings reactions. Maximum signal strength.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "bullish_only" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.10,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 5
|
||||
},
|
||||
"risk": {
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "10pct"]
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
{
|
||||
"experiment_name": "pead_10pct_expanded",
|
||||
"dataset_snapshot_id": "5f3c99d9-693c-48d6-88bf-644666559ca9",
|
||||
"description": "Expanded universe with 10% reaction threshold. Large-cap 7% reactions include noise (sector rotation, short covering). 10%+ reactions are more likely genuine PEAD drift. Quality > quantity approach.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "any" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.10,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 8
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 4,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "10pct", "longshort", "expanded", "universe-741"]
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
{
|
||||
"experiment_name": "pead_10pct_smallmid",
|
||||
"dataset_snapshot_id": "smallmid-only-8ef47ee5",
|
||||
"description": "PEAD 10% L+S on small/mid-cap universe. Higher bar for reaction strength on smaller stocks where 7% reactions are more common.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "any" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.10,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 8
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 8,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "10pct", "longshort", "smallmid", "nosectorlimit"]
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
{
|
||||
"experiment_name": "pead_5pct_longshort_v2",
|
||||
"dataset_snapshot_id": "cd664594-6a6c-4e16-afef-9104ce900f33",
|
||||
"description": "Long+Short PEAD with 5% threshold: 8 max positions, 4 per sector, higher daily risk budget. Tests lower threshold for more trades.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "any" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.05,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 8
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 4,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "5pct", "longshort", "expanded"]
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
{
|
||||
"experiment_name": "pead_7pct_best_combo",
|
||||
"dataset_snapshot_id": "cd664594-6a6c-4e16-afef-9104ce900f33",
|
||||
"description": "PEAD 7% best combo: tighter stop (2.0 ATR) + more positions (6) + no cooldown. Higher capital deployment with tighter risk control.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "bullish_only" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 5
|
||||
},
|
||||
"risk": {
|
||||
"stop_atr_multiplier": 2.0,
|
||||
"max_positions": 6,
|
||||
"max_positions_per_sector": 3,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"max_daily_new_risk_pct": 0.03,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "7pct", "combo", "stop2", "uncapped"]
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
{
|
||||
"experiment_name": "pead_7pct_drift_a",
|
||||
"dataset_snapshot_id": "cd664594-6a6c-4e16-afef-9104ce900f33",
|
||||
"description": "PEAD 7% with extended hold (25d) and wider target (3.0 ATR). Same stop. Tests if longer hold captures more drift.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": {
|
||||
"enabled": true,
|
||||
"direction_filter": "bullish_only",
|
||||
"max_holding_days_override": 25
|
||||
},
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 5
|
||||
},
|
||||
"execution": {
|
||||
"max_holding_days": 25,
|
||||
"target_atr_multiplier": 3.0
|
||||
},
|
||||
"risk": {
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "7pct", "drift", "hold25", "wide_target"]
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
{
|
||||
"experiment_name": "pead_7pct_drift_a_phase6",
|
||||
"dataset_snapshot_id": "46950fc5-4155-4eb0-ab1c-018774e444cc",
|
||||
"description": "PEAD 7% Drift A (hold25, wide target 3.0 ATR) on Phase 6 dataset.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": {
|
||||
"enabled": true,
|
||||
"direction_filter": "bullish_only",
|
||||
"max_holding_days_override": 25
|
||||
},
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 5
|
||||
},
|
||||
"execution": {
|
||||
"max_holding_days": 25,
|
||||
"target_atr_multiplier": 3.0
|
||||
},
|
||||
"risk": {
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "7pct", "drift", "hold25", "phase6"]
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
{
|
||||
"experiment_name": "pead_7pct_drift_b",
|
||||
"dataset_snapshot_id": "cd664594-6a6c-4e16-afef-9104ce900f33",
|
||||
"description": "PEAD 7% with extended hold (25d), no partial exit, wider target (3.0 ATR). Full position rides the drift.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": {
|
||||
"enabled": true,
|
||||
"direction_filter": "bullish_only",
|
||||
"max_holding_days_override": 25
|
||||
},
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 5
|
||||
},
|
||||
"execution": {
|
||||
"max_holding_days": 25,
|
||||
"target_atr_multiplier": 3.0,
|
||||
"target_1_fraction": null
|
||||
},
|
||||
"risk": {
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "7pct", "drift", "hold25", "no_partial"]
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
{
|
||||
"experiment_name": "pead_7pct_drift_c",
|
||||
"dataset_snapshot_id": "cd664594-6a6c-4e16-afef-9104ce900f33",
|
||||
"description": "PEAD 7% aggressive: 2x risk (1%), 6 max positions, hold 25d, no partial exit, wide target. Maximizes capital deployment on high-conviction PEAD trades.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": {
|
||||
"enabled": true,
|
||||
"direction_filter": "bullish_only",
|
||||
"max_holding_days_override": 25
|
||||
},
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 5
|
||||
},
|
||||
"execution": {
|
||||
"max_holding_days": 25,
|
||||
"target_atr_multiplier": 3.0,
|
||||
"target_1_fraction": null
|
||||
},
|
||||
"risk": {
|
||||
"per_trade_risk_pct": 0.01,
|
||||
"max_daily_new_risk_pct": 0.03,
|
||||
"max_positions": 6,
|
||||
"max_positions_per_sector": 3,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "7pct", "drift", "hold25", "aggressive"]
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
{
|
||||
"experiment_name": "pead_7pct_expanded_combo_a",
|
||||
"dataset_snapshot_id": "5f3c99d9-693c-48d6-88bf-644666559ca9",
|
||||
"description": "Combo A: nosectorlimit + 10pct threshold. Removes sector gate bug AND raises reaction bar to filter large-cap noise. Expects fewer but higher-quality trades.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "any" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.10,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 8
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 8,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "10pct", "longshort", "expanded", "universe-741", "nosectorlimit", "combo"]
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
{
|
||||
"experiment_name": "pead_7pct_expanded_combo_b",
|
||||
"dataset_snapshot_id": "5f3c99d9-693c-48d6-88bf-644666559ca9",
|
||||
"description": "Combo B: nosectorlimit + 10pct threshold + stop ATR 2.0. Triple fix: remove sector bug, raise quality bar, widen stop for post-earnings volatility.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "any" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.10,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 8
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 8,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"stop_atr_multiplier": 2.0,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "10pct", "longshort", "expanded", "universe-741", "nosectorlimit", "stop2", "combo"]
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
{
|
||||
"experiment_name": "pead_7pct_expanded_combo_c",
|
||||
"dataset_snapshot_id": "5f3c99d9-693c-48d6-88bf-644666559ca9",
|
||||
"description": "Combo C: ALL fixes combined. nosectorlimit + 10pct + maxcand4 + continuous scoring. Maximum selectivity on expanded universe — only top 4 candidates per day with 10%+ reaction.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "any" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.10,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 4
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 8,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "10pct", "longshort", "expanded", "universe-741", "nosectorlimit", "maxcand4", "combo"]
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
{
|
||||
"experiment_name": "pead_7pct_expanded_continuous",
|
||||
"dataset_snapshot_id": "5f3c99d9-693c-48d6-88bf-644666559ca9",
|
||||
"description": "Expanded universe with continuous PEAD scoring. Multi-factor score (reaction 60%, volume 25%, gap 15%) replaces binary pass/fail. Combined with max_candidates_per_day=4 for synergy — top 4 by continuous score instead of volume ranking.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "any" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 4
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 4,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "7pct", "longshort", "expanded", "universe-741", "continuous-score"]
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
{
|
||||
"experiment_name": "pead_7pct_expanded_maxcand4",
|
||||
"dataset_snapshot_id": "5f3c99d9-693c-48d6-88bf-644666559ca9",
|
||||
"description": "Expanded universe with max 4 candidates per day (down from 8). With 741 symbols, earnings season produces 10+ qualifying events per day — limiting to top 4 reduces concentration risk and improves signal quality.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "any" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 4
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 4,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "7pct", "longshort", "expanded", "universe-741", "maxcand4"]
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
{
|
||||
"experiment_name": "pead_7pct_expanded_nosectorlimit",
|
||||
"dataset_snapshot_id": "5f3c99d9-693c-48d6-88bf-644666559ca9",
|
||||
"description": "Expanded universe with sector gate disabled. All symbols have sector=UNKNOWN, so max_positions_per_sector=4 was randomly blocking entries after 4 positions. Setting equal to max_positions effectively disables the gate.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "any" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 8
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 8,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "7pct", "longshort", "expanded", "universe-741", "nosectorlimit"]
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
{
|
||||
"experiment_name": "pead_7pct_expanded_stop2",
|
||||
"dataset_snapshot_id": "5f3c99d9-693c-48d6-88bf-644666559ca9",
|
||||
"description": "Expanded universe with wider 2.0 ATR stop. Test split showed 75% stop exit rate — post-earnings volatility triggers 1.5 ATR stops prematurely. Wider stop allows trades to reach profit target.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "any" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 8
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 4,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"stop_atr_multiplier": 2.0,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "7pct", "longshort", "expanded", "universe-741", "stop2"]
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
{
|
||||
"experiment_name": "pead_7pct_longshort",
|
||||
"dataset_snapshot_id": "cd664594-6a6c-4e16-afef-9104ce900f33",
|
||||
"description": "Long+Short PEAD: buy after +7% earnings reaction, short after -7% reaction. Both directions with volume >= 1.5x.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": {
|
||||
"enabled": true,
|
||||
"direction_filter": "any"
|
||||
},
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 5
|
||||
},
|
||||
"risk": {
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "7pct", "longshort"]
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
{
|
||||
"experiment_name": "pead_7pct_longshort_v2",
|
||||
"dataset_snapshot_id": "cd664594-6a6c-4e16-afef-9104ce900f33",
|
||||
"description": "Long+Short PEAD with expanded capacity: 8 max positions, 4 per sector, higher daily risk budget. Allows both sides to coexist.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "any" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 8
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 4,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "7pct", "longshort", "expanded"]
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
{
|
||||
"experiment_name": "pead_7pct_longshort_v2_expanded",
|
||||
"dataset_snapshot_id": "5f3c99d9-693c-48d6-88bf-644666559ca9",
|
||||
"description": "PEAD 7% L+S v2 on expanded universe (~741 symbols via screener $10B+ mcap, 1M+ vol). Same signal/risk params as v2, testing trade frequency scaling.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "any" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 8
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 4,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "7pct", "longshort", "expanded", "universe-741"]
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
{
|
||||
"experiment_name": "pead_7pct_lowliq",
|
||||
"dataset_snapshot_id": "cd664594-6a6c-4e16-afef-9104ce900f33",
|
||||
"description": "PEAD 7% with relaxed universe filter: min_avg_dollar_volume 200K (from 1M) and min_price 3 (from 5). Captures more events in same dataset.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "bullish_only" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"universe": {
|
||||
"min_price": 3.0,
|
||||
"min_avg_dollar_volume": 200000.0
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 5
|
||||
},
|
||||
"risk": {
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "7pct", "lowliq", "universe_expansion"]
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
{
|
||||
"experiment_name": "pead_7pct_macro",
|
||||
"dataset_snapshot_id": "cd664594-6a6c-4e16-afef-9104ce900f33",
|
||||
"description": "PEAD 7% + macro regime filter: only enter when SPY > SMA(20). Avoid PEAD trades in bear market.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "bullish_only" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 5
|
||||
},
|
||||
"risk": {
|
||||
"macro_regime_enabled": true,
|
||||
"macro_sma_period": 20,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "7pct", "macro"]
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
{
|
||||
"experiment_name": "pead_7pct_midcap",
|
||||
"dataset_snapshot_id": "midcap-filtered",
|
||||
"description": "PEAD 7% L+S on mid-cap universe ($2B-$10B, ~104 symbols). Academic PEAD strongest in less-followed stocks.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "any" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 8
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 8,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "7pct", "longshort", "midcap", "2b-10b"]
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
{
|
||||
"experiment_name": "pead_7pct_nft",
|
||||
"dataset_snapshot_id": "cd664594-6a6c-4e16-afef-9104ce900f33",
|
||||
"description": "PEAD 7% + No-Follow-Through exit: if D+1 close < entry price, exit immediately. Quick cut of non-drifters.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "bullish_only" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 5
|
||||
},
|
||||
"execution": {
|
||||
"no_follow_through_exit": true
|
||||
},
|
||||
"risk": {
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "7pct", "nft"]
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
{
|
||||
"experiment_name": "pead_7pct_nft_macro",
|
||||
"dataset_snapshot_id": "cd664594-6a6c-4e16-afef-9104ce900f33",
|
||||
"description": "PEAD 7% combined: no-follow-through exit + macro regime filter. Double filter for quality.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "bullish_only" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 5
|
||||
},
|
||||
"execution": {
|
||||
"no_follow_through_exit": true
|
||||
},
|
||||
"risk": {
|
||||
"macro_regime_enabled": true,
|
||||
"macro_sma_period": 20,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "7pct", "nft", "macro", "combined"]
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
{
|
||||
"experiment_name": "pead_7pct_notrail",
|
||||
"dataset_snapshot_id": "cd664594-6a6c-4e16-afef-9104ce900f33",
|
||||
"description": "PEAD 7% with no trailing stop. Only fixed stop + target + time exit. Test if trailing is hurting by cutting winners early.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "bullish_only" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 5
|
||||
},
|
||||
"execution": {
|
||||
"trailing_model": null
|
||||
},
|
||||
"risk": {
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "7pct", "notrail"]
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
{
|
||||
"experiment_name": "pead_7pct_phase6",
|
||||
"dataset_snapshot_id": "46950fc5-4155-4eb0-ab1c-018774e444cc",
|
||||
"description": "PEAD 7% base strategy on Phase 6 dataset for cross-validation.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": {
|
||||
"enabled": true,
|
||||
"direction_filter": "bullish_only"
|
||||
},
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 5
|
||||
},
|
||||
"risk": {
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "7pct", "phase6", "cross_validation"]
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
{
|
||||
"experiment_name": "pead_7pct_short",
|
||||
"dataset_snapshot_id": "cd664594-6a6c-4e16-afef-9104ce900f33",
|
||||
"description": "Short-side PEAD strategy: short after earnings with reaction <= -7% and volume >= 1.5x. Negative drift after bad earnings reactions.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": {
|
||||
"enabled": true,
|
||||
"direction_filter": "bearish_only"
|
||||
},
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 5
|
||||
},
|
||||
"risk": {
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "short", "7pct"]
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
{
|
||||
"experiment_name": "pead_7pct_smallmid",
|
||||
"dataset_snapshot_id": "smallmid-only-8ef47ee5",
|
||||
"description": "PEAD 7% on small/mid-cap dataset. Tests if PEAD signal works on smaller stocks (previously failed with default scoring).",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "bullish_only" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 5
|
||||
},
|
||||
"risk": {
|
||||
"stop_atr_multiplier": 3.0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "7pct", "smallmid"]
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
{
|
||||
"experiment_name": "pead_7pct_smallmid_v2",
|
||||
"dataset_snapshot_id": "smallmid-only-8ef47ee5",
|
||||
"description": "PEAD 7% L+S on small/mid-cap universe (~185 symbols). v2 capacity settings (8 max pos, no sector limit). Academic PEAD strongest in less-followed stocks.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "any" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 8
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 8,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "7pct", "longshort", "smallmid", "nosectorlimit"]
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
{
|
||||
"experiment_name": "pead_7pct_stop2",
|
||||
"dataset_snapshot_id": "cd664594-6a6c-4e16-afef-9104ce900f33",
|
||||
"description": "PEAD 7% with tighter stop (2.0 ATR instead of 3.0). Reduce avg loss per trade.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "bullish_only" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 5
|
||||
},
|
||||
"risk": {
|
||||
"stop_atr_multiplier": 2.0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "7pct", "stop2"]
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
{
|
||||
"experiment_name": "pead_7pct_stop4",
|
||||
"dataset_snapshot_id": "cd664594-6a6c-4e16-afef-9104ce900f33",
|
||||
"description": "PEAD 7% with wider stop (4.0 ATR instead of 3.0). Give trades more room to breathe.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "bullish_only" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 5
|
||||
},
|
||||
"risk": {
|
||||
"stop_atr_multiplier": 4.0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "7pct", "stop4"]
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
{
|
||||
"experiment_name": "pead_7pct_uncapped",
|
||||
"dataset_snapshot_id": "cd664594-6a6c-4e16-afef-9104ce900f33",
|
||||
"description": "PEAD 7% with more capacity: 6 max positions, no cooldown, 3% daily risk budget. More capital deployed.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "bullish_only" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 5
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 6,
|
||||
"max_positions_per_sector": 3,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"max_daily_new_risk_pct": 0.03,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "7pct", "uncapped"]
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
{
|
||||
"experiment_name": "pead_7pct_vol1x",
|
||||
"dataset_snapshot_id": "cd664594-6a6c-4e16-afef-9104ce900f33",
|
||||
"description": "PEAD 7% with lower volume threshold (1.0x instead of 1.5x). More trades by relaxing volume filter.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "bullish_only" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 1.0,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 5
|
||||
},
|
||||
"risk": {
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "7pct", "vol1x"]
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
{
|
||||
"experiment_name": "pead_midcap_combo_10pct_maxcand3",
|
||||
"dataset_snapshot_id": "midcap-filtered",
|
||||
"description": "Combo: Step3 (10% threshold) + Step5 (max 3 candidates/day). Both are signal quality filters — strongest midcap PEAD signals only.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "any" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.10,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 3
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 8,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "10pct", "longshort", "midcap", "combo", "maxcand3"]
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
{
|
||||
"experiment_name": "pead_midcap_step10_short",
|
||||
"dataset_snapshot_id": "midcap-filtered",
|
||||
"description": "Step 10: Combo base (10% + maxcand3) + shorter hold (7 days). Avg holding is 3.4 days, so 15-day max is unnecessary exposure. 7 days captures PEAD drift without mean reversion.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": {"enabled": true, "direction_filter": "any", "max_holding_days_override": 7},
|
||||
"guidance_update": {"enabled": false},
|
||||
"management_change": {"enabled": false},
|
||||
"material_contract": {"enabled": false},
|
||||
"unknown": {"enabled": false},
|
||||
"other_material_event": {"enabled": false}
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.10,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 3
|
||||
},
|
||||
"execution": {
|
||||
"max_holding_days": 7
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 8,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"splits": [],
|
||||
"tags": ["pead", "midcap", "step10", "short", "combo"],
|
||||
"notes": null
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
{
|
||||
"experiment_name": "pead_midcap_step11_score60",
|
||||
"dataset_snapshot_id": "midcap-filtered",
|
||||
"description": "Step 11: Combo base + higher score threshold (0.60). All prior experiments used 0.50 (below default 0.55). Raising to 0.60 should filter weak signals and improve trade quality.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": {"enabled": true, "direction_filter": "any"},
|
||||
"guidance_update": {"enabled": false},
|
||||
"management_change": {"enabled": false},
|
||||
"material_contract": {"enabled": false},
|
||||
"unknown": {"enabled": false},
|
||||
"other_material_event": {"enabled": false}
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.10,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.60,
|
||||
"max_candidates_per_day": 3
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 8,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"splits": [],
|
||||
"tags": ["pead", "midcap", "step11", "score60", "combo"],
|
||||
"notes": null
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
{
|
||||
"experiment_name": "pead_midcap_step12_vol2x",
|
||||
"dataset_snapshot_id": "midcap-filtered",
|
||||
"description": "Step 12: Combo base + volume threshold 2.0x (from 1.5x). Require stronger volume conviction on reaction day. Higher volume = more institutional participation = stronger drift.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": {"enabled": true, "direction_filter": "any"},
|
||||
"guidance_update": {"enabled": false},
|
||||
"management_change": {"enabled": false},
|
||||
"material_contract": {"enabled": false},
|
||||
"unknown": {"enabled": false},
|
||||
"other_material_event": {"enabled": false}
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.10,
|
||||
"pead_volume_threshold": 2.0,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 3
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 8,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"splits": [],
|
||||
"tags": ["pead", "midcap", "step12", "vol2x", "combo"],
|
||||
"notes": null
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
{
|
||||
"experiment_name": "pead_midcap_step13_best",
|
||||
"dataset_snapshot_id": "midcap-filtered",
|
||||
"description": "Step 13: Best combo. 10% reaction + maxcand3 + score 0.60 + short hold 7d + vol 2.0x. Combines all positive findings from steps 10-12.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": {"enabled": true, "direction_filter": "any", "max_holding_days_override": 7},
|
||||
"guidance_update": {"enabled": false},
|
||||
"management_change": {"enabled": false},
|
||||
"material_contract": {"enabled": false},
|
||||
"unknown": {"enabled": false},
|
||||
"other_material_event": {"enabled": false}
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.10,
|
||||
"pead_volume_threshold": 2.0,
|
||||
"score_threshold": 0.60,
|
||||
"max_candidates_per_day": 3
|
||||
},
|
||||
"execution": {
|
||||
"max_holding_days": 7
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 8,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"splits": [],
|
||||
"tags": ["pead", "midcap", "step13", "best", "combo"],
|
||||
"notes": null
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
{
|
||||
"experiment_name": "pead_midcap_step14_score65",
|
||||
"dataset_snapshot_id": "midcap-filtered",
|
||||
"description": "Step 14: Higher score threshold 0.65 (from 0.60). Stronger signal filter to improve WR/PF with fewer trades.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": {"enabled": true, "direction_filter": "any", "max_holding_days_override": 7},
|
||||
"guidance_update": {"enabled": false},
|
||||
"management_change": {"enabled": false},
|
||||
"material_contract": {"enabled": false},
|
||||
"unknown": {"enabled": false},
|
||||
"other_material_event": {"enabled": false}
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.10,
|
||||
"pead_volume_threshold": 2.0,
|
||||
"score_threshold": 0.65,
|
||||
"max_candidates_per_day": 3
|
||||
},
|
||||
"execution": {
|
||||
"max_holding_days": 7
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 8,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"splits": [],
|
||||
"tags": ["pead", "midcap", "step14", "score65"],
|
||||
"notes": null
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
{
|
||||
"experiment_name": "pead_midcap_step15_react7",
|
||||
"dataset_snapshot_id": "midcap-filtered",
|
||||
"description": "Step 15: Lower reaction threshold 0.07 (from 0.10). Wider funnel to increase trade count for statistical significance.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": {"enabled": true, "direction_filter": "any", "max_holding_days_override": 7},
|
||||
"guidance_update": {"enabled": false},
|
||||
"management_change": {"enabled": false},
|
||||
"material_contract": {"enabled": false},
|
||||
"unknown": {"enabled": false},
|
||||
"other_material_event": {"enabled": false}
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 2.0,
|
||||
"score_threshold": 0.60,
|
||||
"max_candidates_per_day": 3
|
||||
},
|
||||
"execution": {
|
||||
"max_holding_days": 7
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 8,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"splits": [],
|
||||
"tags": ["pead", "midcap", "step15", "react7"],
|
||||
"notes": null
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
{
|
||||
"experiment_name": "pead_midcap_step16_react7_score65",
|
||||
"dataset_snapshot_id": "midcap-filtered",
|
||||
"description": "Step 16: Combined lower reaction 0.07 + higher score 0.65. Wider funnel filtered by stricter quality gate.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": {"enabled": true, "direction_filter": "any", "max_holding_days_override": 7},
|
||||
"guidance_update": {"enabled": false},
|
||||
"management_change": {"enabled": false},
|
||||
"material_contract": {"enabled": false},
|
||||
"unknown": {"enabled": false},
|
||||
"other_material_event": {"enabled": false}
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 2.0,
|
||||
"score_threshold": 0.65,
|
||||
"max_candidates_per_day": 3
|
||||
},
|
||||
"execution": {
|
||||
"max_holding_days": 7
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 8,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"splits": [],
|
||||
"tags": ["pead", "midcap", "step16", "react7", "score65"],
|
||||
"notes": null
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
{
|
||||
"experiment_name": "pead_midcap_step17_target2",
|
||||
"dataset_snapshot_id": "midcap-filtered",
|
||||
"description": "Step 17: Target ATR 2.0 (from 1.5). Based on step14 (score 0.65). Improves R:R from 3:1.5 to 3:2.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": {"enabled": true, "direction_filter": "any", "max_holding_days_override": 7},
|
||||
"guidance_update": {"enabled": false},
|
||||
"management_change": {"enabled": false},
|
||||
"material_contract": {"enabled": false},
|
||||
"unknown": {"enabled": false},
|
||||
"other_material_event": {"enabled": false}
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.10,
|
||||
"pead_volume_threshold": 2.0,
|
||||
"score_threshold": 0.65,
|
||||
"max_candidates_per_day": 3
|
||||
},
|
||||
"execution": {
|
||||
"max_holding_days": 7,
|
||||
"target_atr_multiplier": 2.0
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 8,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"splits": [],
|
||||
"tags": ["pead", "midcap", "step17", "target2"],
|
||||
"notes": null
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
{
|
||||
"experiment_name": "pead_midcap_step18_nofrac",
|
||||
"dataset_snapshot_id": "midcap-filtered",
|
||||
"description": "Step 18: Full exit at target (fraction 1.0 from 0.5). Based on step14 (score 0.65). Prevents trailing stop eating profits.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": {"enabled": true, "direction_filter": "any", "max_holding_days_override": 7},
|
||||
"guidance_update": {"enabled": false},
|
||||
"management_change": {"enabled": false},
|
||||
"material_contract": {"enabled": false},
|
||||
"unknown": {"enabled": false},
|
||||
"other_material_event": {"enabled": false}
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.10,
|
||||
"pead_volume_threshold": 2.0,
|
||||
"score_threshold": 0.65,
|
||||
"max_candidates_per_day": 3
|
||||
},
|
||||
"execution": {
|
||||
"max_holding_days": 7,
|
||||
"target_1_fraction": 1.0
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 8,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"splits": [],
|
||||
"tags": ["pead", "midcap", "step18", "nofrac"],
|
||||
"notes": null
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
{
|
||||
"experiment_name": "pead_midcap_step19_hold5",
|
||||
"dataset_snapshot_id": "midcap-filtered",
|
||||
"description": "Step 19: Shorter hold 5d (from 7d). Based on step14 (score 0.65). Avg hold is 3.28d so 5d should be sufficient.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": {"enabled": true, "direction_filter": "any", "max_holding_days_override": 5},
|
||||
"guidance_update": {"enabled": false},
|
||||
"management_change": {"enabled": false},
|
||||
"material_contract": {"enabled": false},
|
||||
"unknown": {"enabled": false},
|
||||
"other_material_event": {"enabled": false}
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.10,
|
||||
"pead_volume_threshold": 2.0,
|
||||
"score_threshold": 0.65,
|
||||
"max_candidates_per_day": 3
|
||||
},
|
||||
"execution": {
|
||||
"max_holding_days": 5
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 8,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"splits": [],
|
||||
"tags": ["pead", "midcap", "step19", "hold5"],
|
||||
"notes": null
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
{
|
||||
"experiment_name": "pead_midcap_step1_fixedr",
|
||||
"dataset_snapshot_id": "midcap-filtered",
|
||||
"description": "Step 1: R:R fix. Switch to fixed_r target_1_r=2.0 (target=2x stop). Breakeven WR drops from 67% to 33%. Baseline uses atr_multiple 1.5 ATR target vs 3.0 ATR stop.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "any" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 8
|
||||
},
|
||||
"execution": {
|
||||
"target_model": "fixed_r",
|
||||
"target_1_r": 2.0
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 8,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "7pct", "longshort", "midcap", "step1", "fixedr"]
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
{
|
||||
"experiment_name": "pead_midcap_step20_best3",
|
||||
"dataset_snapshot_id": "midcap-filtered",
|
||||
"description": "Step 20: Best combo. score 0.65 (step14) + nofrac 1.0 (step18) + hold 5d (step19). Combines all marginally positive Phase A+B findings.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": {"enabled": true, "direction_filter": "any", "max_holding_days_override": 5},
|
||||
"guidance_update": {"enabled": false},
|
||||
"management_change": {"enabled": false},
|
||||
"material_contract": {"enabled": false},
|
||||
"unknown": {"enabled": false},
|
||||
"other_material_event": {"enabled": false}
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.10,
|
||||
"pead_volume_threshold": 2.0,
|
||||
"score_threshold": 0.65,
|
||||
"max_candidates_per_day": 3
|
||||
},
|
||||
"execution": {
|
||||
"max_holding_days": 5,
|
||||
"target_1_fraction": 1.0
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 8,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"splits": [],
|
||||
"tags": ["pead", "midcap", "step20", "best3", "combo"],
|
||||
"notes": null
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
{
|
||||
"experiment_name": "pead_midcap_step2_notrail",
|
||||
"dataset_snapshot_id": "midcap-filtered",
|
||||
"description": "Step 2: Disable trailing stop. Baseline pct_5 trail may be too tight for midcap volatility, cutting PEAD drift early.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "any" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 8
|
||||
},
|
||||
"execution": {
|
||||
"trailing_model": null
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 8,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "7pct", "longshort", "midcap", "step2", "notrail"]
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
{
|
||||
"experiment_name": "pead_midcap_step3_10pct",
|
||||
"dataset_snapshot_id": "midcap-filtered",
|
||||
"description": "Step 3: Raise reaction threshold to 10%. Stronger signal filter, fewer trades, higher quality expected.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "any" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.10,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 8
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 8,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "10pct", "longshort", "midcap", "step3", "threshold"]
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
{
|
||||
"experiment_name": "pead_midcap_step4_longonly",
|
||||
"dataset_snapshot_id": "midcap-filtered",
|
||||
"description": "Step 4: Long-only. Disable short side to test if midcap short PEAD mean-reverts instead of drifting.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "bullish_only" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 8
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 8,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "7pct", "longonly", "midcap", "step4", "bullish"]
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
{
|
||||
"experiment_name": "pead_midcap_step5_maxcand3",
|
||||
"dataset_snapshot_id": "midcap-filtered",
|
||||
"description": "Step 5: Limit to 3 candidates per day. Remove lower-ranked signals, keep only top entries.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "any" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 3
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 8,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "7pct", "longshort", "midcap", "step5", "maxcand3"]
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
{
|
||||
"experiment_name": "pead_midcap_step6_drift",
|
||||
"dataset_snapshot_id": "midcap-filtered",
|
||||
"description": "Step 6: Extended hold 25 days + disable partial exit. Test if PEAD drift needs more time than 15 days in midcap.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": { "enabled": true, "direction_filter": "any" },
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 8
|
||||
},
|
||||
"execution": {
|
||||
"max_holding_days": 25,
|
||||
"target_1_fraction": null
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 8,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "7pct", "longshort", "midcap", "step6", "drift25"]
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
{
|
||||
"experiment_name": "pead_midcap_step7_fixedr",
|
||||
"dataset_snapshot_id": "midcap-filtered",
|
||||
"description": "Step 7: Combo base (10% threshold + max 3 cand) + fixed_r target 2.0. Fixes inverted R:R from atr_multiple (1.5 ATR target vs 3.0 ATR stop). With proper R:R, need only 33% WR to breakeven.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": {"enabled": true, "direction_filter": "any"},
|
||||
"guidance_update": {"enabled": false},
|
||||
"management_change": {"enabled": false},
|
||||
"material_contract": {"enabled": false},
|
||||
"unknown": {"enabled": false},
|
||||
"other_material_event": {"enabled": false}
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.10,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 3
|
||||
},
|
||||
"execution": {
|
||||
"target_model": "fixed_r",
|
||||
"target_1_r": 2.0
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 8,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"splits": [],
|
||||
"tags": ["pead", "midcap", "step7", "fixedr", "combo"],
|
||||
"notes": null
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
{
|
||||
"experiment_name": "pead_midcap_step8_nft",
|
||||
"dataset_snapshot_id": "midcap-filtered",
|
||||
"description": "Step 8: Step 7 base + no_follow_through_exit. Exit at D+1 close if price < entry. Cuts dead trades early before stop is hit, reducing 85% stop exit rate.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": {"enabled": true, "direction_filter": "any"},
|
||||
"guidance_update": {"enabled": false},
|
||||
"management_change": {"enabled": false},
|
||||
"material_contract": {"enabled": false},
|
||||
"unknown": {"enabled": false},
|
||||
"other_material_event": {"enabled": false}
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.10,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 3
|
||||
},
|
||||
"execution": {
|
||||
"target_model": "fixed_r",
|
||||
"target_1_r": 2.0,
|
||||
"no_follow_through_exit": true
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 8,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"splits": [],
|
||||
"tags": ["pead", "midcap", "step8", "nft", "fixedr", "combo"],
|
||||
"notes": null
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
{
|
||||
"experiment_name": "pead_midcap_step9_stop2",
|
||||
"dataset_snapshot_id": "midcap-filtered",
|
||||
"description": "Step 9: Step 7 base + tighter stop (2.0 ATR instead of 3.0). With fixed_r target 2.0, target = 4 ATR. Tighter stop reduces avg loss, but may increase stop exit rate.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": {"enabled": true, "direction_filter": "any"},
|
||||
"guidance_update": {"enabled": false},
|
||||
"management_change": {"enabled": false},
|
||||
"material_contract": {"enabled": false},
|
||||
"unknown": {"enabled": false},
|
||||
"other_material_event": {"enabled": false}
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.10,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 3
|
||||
},
|
||||
"execution": {
|
||||
"target_model": "fixed_r",
|
||||
"target_1_r": 2.0
|
||||
},
|
||||
"risk": {
|
||||
"max_positions": 8,
|
||||
"max_positions_per_sector": 8,
|
||||
"max_daily_new_risk_pct": 0.04,
|
||||
"cooldown_after_loss_streak": 0,
|
||||
"cooldown_days": 0,
|
||||
"stop_atr_multiplier": 2.0,
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"splits": [],
|
||||
"tags": ["pead", "midcap", "step9", "stop2", "fixedr", "combo"],
|
||||
"notes": null
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
{
|
||||
"experiment_name": "pead_pure_5pct",
|
||||
"dataset_snapshot_id": "cd664594-6a6c-4e16-afef-9104ce900f33",
|
||||
"description": "Pure PEAD strategy: buy after earnings with reaction >= 5% and volume >= 1.5x. No complex scoring, just price/volume confirmation.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": {
|
||||
"enabled": true,
|
||||
"direction_filter": "bullish_only"
|
||||
},
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.05,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 5
|
||||
},
|
||||
"risk": {
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "pure", "5pct"]
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
{
|
||||
"experiment_name": "pead_pure_7pct",
|
||||
"dataset_snapshot_id": "cd664594-6a6c-4e16-afef-9104ce900f33",
|
||||
"description": "Pure PEAD strategy: buy after earnings with reaction >= 7% and volume >= 1.5x. Higher threshold = stronger signal but fewer trades.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"event_type_profiles": {
|
||||
"earnings_release": {
|
||||
"enabled": true,
|
||||
"direction_filter": "bullish_only"
|
||||
},
|
||||
"guidance_update": { "enabled": false },
|
||||
"management_change": { "enabled": false },
|
||||
"material_contract": { "enabled": false },
|
||||
"unknown": { "enabled": false },
|
||||
"other_material_event": { "enabled": false }
|
||||
},
|
||||
"signal": {
|
||||
"scoring_model": "pead",
|
||||
"pead_reaction_threshold": 0.07,
|
||||
"pead_volume_threshold": 1.5,
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 5
|
||||
},
|
||||
"risk": {
|
||||
"veto_oneoff_penalty": 1.0,
|
||||
"veto_unknown_direction": false,
|
||||
"veto_bearish_direction": false
|
||||
}
|
||||
},
|
||||
"tags": ["pead", "pure", "7pct"]
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
{
|
||||
"experiment_name": "phase5_no_trail_v1",
|
||||
"dataset_snapshot_id": "e684ab2c-cfd9-4d72-94de-f7cc497211b8",
|
||||
"description": "Phase 5 without trailing stop to isolate ATR target + wider stop effect.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"signal": {
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 10
|
||||
},
|
||||
"risk": {
|
||||
"per_trade_risk_pct": 0.01,
|
||||
"max_daily_new_risk_pct": 0.05,
|
||||
"max_positions": 10,
|
||||
"max_positions_per_sector": 5
|
||||
},
|
||||
"execution": {
|
||||
"trailing_model": null,
|
||||
"target_1_fraction": 1.0,
|
||||
"max_holding_days": 15
|
||||
}
|
||||
},
|
||||
"splits": [],
|
||||
"tags": ["phase5", "no-trailing", "atr-targets"]
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
{
|
||||
"experiment_name": "phase5_v1",
|
||||
"dataset_snapshot_id": "cd664594-6a6c-4e16-afef-9104ce900f33",
|
||||
"description": "Phase 5 strategy improvements: ATR targets, partial exits, wider stops, event-type filtering, SUE gate, direction filter.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"signal": {
|
||||
"score_threshold": 0.5,
|
||||
"max_candidates_per_day": 10
|
||||
},
|
||||
"risk": {
|
||||
"per_trade_risk_pct": 0.01,
|
||||
"max_daily_new_risk_pct": 0.05,
|
||||
"max_positions": 10,
|
||||
"max_positions_per_sector": 5
|
||||
}
|
||||
},
|
||||
"splits": [],
|
||||
"tags": ["phase5", "atr-targets", "partial-exits", "event-type-filter", "sue-gate"],
|
||||
"notes": "Phase 5 improvements over expanded_scored_v1. Uses new defaults: stop_atr_multiplier=3.0, target_model=atr_multiple, target_1_fraction=0.5, trailing pct_3 with warmup=2, event_type_profiles active."
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
{
|
||||
"experiment_name": "phase6_v1",
|
||||
"dataset_snapshot_id": "46950fc5-4155-4eb0-ab1c-018774e444cc",
|
||||
"description": "Phase 6: Open filtering gates, disable no-follow-through, kill_switch log_only, wider trailing, fix attribution.",
|
||||
"base_config": "configs/backtest/defaults.json",
|
||||
"overrides": {
|
||||
"signal": {
|
||||
"score_threshold": 0.55,
|
||||
"max_candidates_per_day": 10
|
||||
},
|
||||
"risk": {
|
||||
"per_trade_risk_pct": 0.01,
|
||||
"max_daily_new_risk_pct": 0.05,
|
||||
"max_positions": 10,
|
||||
"max_positions_per_sector": 5
|
||||
}
|
||||
},
|
||||
"splits": [],
|
||||
"tags": ["phase6", "gate-fix", "no-nft", "wider-trail", "attribution-fix"]
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
{
|
||||
"experiment_name": "phase7_smallmid_v1",
|
||||
"dataset_snapshot_id": "9bb74bb1-fefc-412b-b234-f25087634ad1",
|
||||
"description": "Phase 7: Small/mid-cap universe. Hypothesis: less coverage = more alpha from 8-K filings.",
|
||||
"base_config": "configs/backtest/defaults_smallmid.json",
|
||||
"overrides": {
|
||||
"signal": {
|
||||
"score_threshold": 0.55,
|
||||
"max_candidates_per_day": 10
|
||||
},
|
||||
"risk": {
|
||||
"per_trade_risk_pct": 0.01,
|
||||
"max_daily_new_risk_pct": 0.05,
|
||||
"max_positions": 10,
|
||||
"max_positions_per_sector": 5
|
||||
}
|
||||
},
|
||||
"splits": [],
|
||||
"tags": ["phase7", "smallmid", "universe-swap", "slippage-25bps"]
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
{
|
||||
"experiment_name": "phase7_smallmid_v2",
|
||||
"dataset_snapshot_id": "smallmid-only-8ef47ee5",
|
||||
"description": "Phase 7 v2: Small/mid-cap only dataset (largecap filtered out). Clean isolation test.",
|
||||
"base_config": "configs/backtest/defaults_smallmid.json",
|
||||
"overrides": {
|
||||
"signal": {
|
||||
"score_threshold": 0.55,
|
||||
"max_candidates_per_day": 10
|
||||
},
|
||||
"risk": {
|
||||
"per_trade_risk_pct": 0.01,
|
||||
"max_daily_new_risk_pct": 0.05,
|
||||
"max_positions": 10,
|
||||
"max_positions_per_sector": 5
|
||||
}
|
||||
},
|
||||
"splits": [],
|
||||
"tags": ["phase7", "smallmid", "isolated", "slippage-25bps"]
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
{
|
||||
"experiment_name": "phase7_smallmid_v3",
|
||||
"dataset_snapshot_id": "smallmid-only-8ef47ee5",
|
||||
"description": "Phase 7 v3: Earnings-focused. Disable unknown/material_contract/guidance_update event types. Conservative risk.",
|
||||
"base_config": "configs/backtest/defaults_smallmid.json",
|
||||
"overrides": {
|
||||
"signal": {
|
||||
"score_threshold": 0.55,
|
||||
"max_candidates_per_day": 5
|
||||
},
|
||||
"risk": {
|
||||
"per_trade_risk_pct": 0.01,
|
||||
"max_daily_new_risk_pct": 0.03,
|
||||
"max_positions": 6,
|
||||
"max_positions_per_sector": 3
|
||||
},
|
||||
"event_type_profiles": {
|
||||
"earnings_release": {
|
||||
"enabled": true,
|
||||
"max_holding_days_override": 15,
|
||||
"direction_filter": "any"
|
||||
},
|
||||
"guidance_update": {
|
||||
"enabled": false
|
||||
},
|
||||
"management_change": {
|
||||
"enabled": true,
|
||||
"direction_filter": "any"
|
||||
},
|
||||
"material_contract": {
|
||||
"enabled": false
|
||||
},
|
||||
"unknown": {
|
||||
"enabled": false
|
||||
},
|
||||
"other_material_event": {
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"splits": [],
|
||||
"tags": ["phase7", "smallmid", "earnings-focused", "slippage-25bps"]
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
{
|
||||
"experiment_name": "phase7_smallmid_v3_slip10",
|
||||
"dataset_snapshot_id": "smallmid-only-8ef47ee5",
|
||||
"description": "Phase 7 v3: Earnings-focused, slippage=10bps",
|
||||
"base_config": "configs/backtest/defaults_smallmid.json",
|
||||
"overrides": {
|
||||
"signal": {
|
||||
"score_threshold": 0.55,
|
||||
"max_candidates_per_day": 5
|
||||
},
|
||||
"risk": {
|
||||
"per_trade_risk_pct": 0.01,
|
||||
"max_daily_new_risk_pct": 0.03,
|
||||
"max_positions": 6,
|
||||
"max_positions_per_sector": 3
|
||||
},
|
||||
"event_type_profiles": {
|
||||
"earnings_release": {
|
||||
"enabled": true,
|
||||
"max_holding_days_override": 15,
|
||||
"direction_filter": "any"
|
||||
},
|
||||
"guidance_update": {
|
||||
"enabled": false
|
||||
},
|
||||
"management_change": {
|
||||
"enabled": true,
|
||||
"direction_filter": "any"
|
||||
},
|
||||
"material_contract": {
|
||||
"enabled": false
|
||||
},
|
||||
"unknown": {
|
||||
"enabled": false
|
||||
},
|
||||
"other_material_event": {
|
||||
"enabled": false
|
||||
}
|
||||
},
|
||||
"execution": {
|
||||
"slippage_bps_base": 10.0
|
||||
}
|
||||
},
|
||||
"splits": [],
|
||||
"tags": [
|
||||
"phase7",
|
||||
"smallmid",
|
||||
"earnings-focused",
|
||||
"slippage-10bps"
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
{
|
||||
"experiment_name": "phase7_smallmid_v3_slip15",
|
||||
"dataset_snapshot_id": "smallmid-only-8ef47ee5",
|
||||
"description": "Phase 7 v3: Earnings-focused, slippage=15bps",
|
||||
"base_config": "configs/backtest/defaults_smallmid.json",
|
||||
"overrides": {
|
||||
"signal": {
|
||||
"score_threshold": 0.55,
|
||||
"max_candidates_per_day": 5
|
||||
},
|
||||
"risk": {
|
||||
"per_trade_risk_pct": 0.01,
|
||||
"max_daily_new_risk_pct": 0.03,
|
||||
"max_positions": 6,
|
||||
"max_positions_per_sector": 3
|
||||
},
|
||||
"event_type_profiles": {
|
||||
"earnings_release": {
|
||||
"enabled": true,
|
||||
"max_holding_days_override": 15,
|
||||
"direction_filter": "any"
|
||||
},
|
||||
"guidance_update": {
|
||||
"enabled": false
|
||||
},
|
||||
"management_change": {
|
||||
"enabled": true,
|
||||
"direction_filter": "any"
|
||||
},
|
||||
"material_contract": {
|
||||
"enabled": false
|
||||
},
|
||||
"unknown": {
|
||||
"enabled": false
|
||||
},
|
||||
"other_material_event": {
|
||||
"enabled": false
|
||||
}
|
||||
},
|
||||
"execution": {
|
||||
"slippage_bps_base": 15.0
|
||||
}
|
||||
},
|
||||
"splits": [],
|
||||
"tags": [
|
||||
"phase7",
|
||||
"smallmid",
|
||||
"earnings-focused",
|
||||
"slippage-15bps"
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
{
|
||||
"experiment_name": "phase7_smallmid_v3_slip20",
|
||||
"dataset_snapshot_id": "smallmid-only-8ef47ee5",
|
||||
"description": "Phase 7 v3: Earnings-focused, slippage=20bps",
|
||||
"base_config": "configs/backtest/defaults_smallmid.json",
|
||||
"overrides": {
|
||||
"signal": {
|
||||
"score_threshold": 0.55,
|
||||
"max_candidates_per_day": 5
|
||||
},
|
||||
"risk": {
|
||||
"per_trade_risk_pct": 0.01,
|
||||
"max_daily_new_risk_pct": 0.03,
|
||||
"max_positions": 6,
|
||||
"max_positions_per_sector": 3
|
||||
},
|
||||
"event_type_profiles": {
|
||||
"earnings_release": {
|
||||
"enabled": true,
|
||||
"max_holding_days_override": 15,
|
||||
"direction_filter": "any"
|
||||
},
|
||||
"guidance_update": {
|
||||
"enabled": false
|
||||
},
|
||||
"management_change": {
|
||||
"enabled": true,
|
||||
"direction_filter": "any"
|
||||
},
|
||||
"material_contract": {
|
||||
"enabled": false
|
||||
},
|
||||
"unknown": {
|
||||
"enabled": false
|
||||
},
|
||||
"other_material_event": {
|
||||
"enabled": false
|
||||
}
|
||||
},
|
||||
"execution": {
|
||||
"slippage_bps_base": 20.0
|
||||
}
|
||||
},
|
||||
"splits": [],
|
||||
"tags": [
|
||||
"phase7",
|
||||
"smallmid",
|
||||
"earnings-focused",
|
||||
"slippage-20bps"
|
||||
]
|
||||
}
|
||||
@ -1,102 +1,744 @@
|
||||
symbols:
|
||||
# --- Mega-cap tech (original 15) ---
|
||||
- AAPL
|
||||
- MSFT
|
||||
- GOOGL
|
||||
- AMZN
|
||||
- META
|
||||
- NVDA
|
||||
- TSLA
|
||||
- AMD
|
||||
- NFLX
|
||||
- CRM
|
||||
- SNOW
|
||||
- NET
|
||||
- DDOG
|
||||
- ZS
|
||||
- CRWD
|
||||
# --- Large-cap tech additions ---
|
||||
- ADBE
|
||||
- ORCL
|
||||
- INTC
|
||||
- QCOM
|
||||
- AVGO
|
||||
- MU
|
||||
- PANW
|
||||
- FTNT
|
||||
- NOW
|
||||
- WDAY
|
||||
- SHOP
|
||||
- SQ
|
||||
- MELI
|
||||
- UBER
|
||||
- DASH
|
||||
# --- Mid-cap tech / growth ($5B-50B — stronger PEAD) ---
|
||||
# --- Existing-only (not in screener, kept) ---
|
||||
- AXON
|
||||
- BILL
|
||||
- HUBS
|
||||
- PCOR
|
||||
- CFLT
|
||||
- MNDY
|
||||
- BLK
|
||||
- BRZE
|
||||
- DOCN
|
||||
- DPZ
|
||||
- GTLB
|
||||
- HIMS
|
||||
- MELI
|
||||
- MNDY
|
||||
- PCOR
|
||||
- S
|
||||
- IOT
|
||||
- DOCN
|
||||
- BRZE
|
||||
# --- Healthcare ---
|
||||
- UNH
|
||||
- JNJ
|
||||
- LLY
|
||||
- SQ
|
||||
- URI
|
||||
# --- Screener ($10B+ mcap, 1M+ vol) — 727 symbols ---
|
||||
- A
|
||||
- AA
|
||||
- AAPL
|
||||
- ABBV
|
||||
- PFE
|
||||
- MRK
|
||||
- TMO
|
||||
- ABNB
|
||||
- ABT
|
||||
- ISRG
|
||||
- DXCM
|
||||
- VEEV
|
||||
- HIMS
|
||||
# --- Industrials ---
|
||||
- ACGL
|
||||
- ACM
|
||||
- ACN
|
||||
- ADBE
|
||||
- ADI
|
||||
- ADM
|
||||
- ADP
|
||||
- ADSK
|
||||
- AEE
|
||||
- AEG
|
||||
- AEM
|
||||
- AEP
|
||||
- AER
|
||||
- AES
|
||||
- AFL
|
||||
- AFRM
|
||||
- AG
|
||||
- AGI
|
||||
- AGNC
|
||||
- AIG
|
||||
- AJG
|
||||
- AKAM
|
||||
- ALAB
|
||||
- ALB
|
||||
- ALC
|
||||
- ALGN
|
||||
- ALL
|
||||
- ALLY
|
||||
- ALNY
|
||||
- AM
|
||||
- AMAT
|
||||
- AMCR
|
||||
- AMD
|
||||
- AME
|
||||
- AMGN
|
||||
- AMH
|
||||
- AMKR
|
||||
- AMRZ
|
||||
- AMT
|
||||
- AMX
|
||||
- AMZN
|
||||
- ANET
|
||||
- AON
|
||||
- APA
|
||||
- APD
|
||||
- APG
|
||||
- APH
|
||||
- APO
|
||||
- APP
|
||||
- APTV
|
||||
- AR
|
||||
- ARCC
|
||||
- ARES
|
||||
- ARM
|
||||
- ARMK
|
||||
- AS
|
||||
- ASML
|
||||
- ASTS
|
||||
- ASX
|
||||
- ATI
|
||||
- ATO
|
||||
- AU
|
||||
- AVAV
|
||||
- AVB
|
||||
- AVGO
|
||||
- AWK
|
||||
- AXIA
|
||||
- AXP
|
||||
- AZN
|
||||
- B
|
||||
- BA
|
||||
- BABA
|
||||
- BAC
|
||||
- BALL
|
||||
- BAM
|
||||
- BBIO
|
||||
- BBVA
|
||||
- BBY
|
||||
- BCE
|
||||
- BCS
|
||||
- BDX
|
||||
- BE
|
||||
- BEKE
|
||||
- BEN
|
||||
- BF-B
|
||||
- BG
|
||||
- BHP
|
||||
- BIDU
|
||||
- BIIB
|
||||
- BILI
|
||||
- BJ
|
||||
- BK
|
||||
- BKR
|
||||
- BMRN
|
||||
- BMY
|
||||
- BN
|
||||
- BNS
|
||||
- BNTX
|
||||
- BP
|
||||
- BR
|
||||
- BRK-B
|
||||
- BRO
|
||||
- BSX
|
||||
- BSY
|
||||
- BTI
|
||||
- BUD
|
||||
- BWA
|
||||
- BX
|
||||
- C
|
||||
- CAH
|
||||
- CARR
|
||||
- CART
|
||||
- CAT
|
||||
- CB
|
||||
- CBRE
|
||||
- CCEP
|
||||
- CCI
|
||||
- CCJ
|
||||
- CCK
|
||||
- CCL
|
||||
- CDE
|
||||
- CDNS
|
||||
- CDW
|
||||
- CEG
|
||||
- CELH
|
||||
- CF
|
||||
- CFG
|
||||
- CFLT
|
||||
- CG
|
||||
- CHD
|
||||
- CHKP
|
||||
- CHRW
|
||||
- CHTR
|
||||
- CHWY
|
||||
- CI
|
||||
- CIEN
|
||||
- CL
|
||||
- CLS
|
||||
- CLX
|
||||
- CM
|
||||
- CMCSA
|
||||
- CME
|
||||
- CMG
|
||||
- CMS
|
||||
- CNC
|
||||
- CNH
|
||||
- CNI
|
||||
- CNP
|
||||
- CNQ
|
||||
- COF
|
||||
- COHR
|
||||
- COIN
|
||||
- COO
|
||||
- COP
|
||||
- COR
|
||||
- COST
|
||||
- CP
|
||||
- CPNG
|
||||
- CPRT
|
||||
- CPT
|
||||
- CRBG
|
||||
- CRCL
|
||||
- CRDO
|
||||
- CRH
|
||||
- CRM
|
||||
- CRWD
|
||||
- CRWV
|
||||
- CSCO
|
||||
- CSGP
|
||||
- CSX
|
||||
- CTAS
|
||||
- CTRA
|
||||
- CTSH
|
||||
- CTVA
|
||||
- CUK
|
||||
- CVE
|
||||
- CVNA
|
||||
- CVS
|
||||
- CVX
|
||||
- CX
|
||||
- D
|
||||
- DAL
|
||||
- DASH
|
||||
- DB
|
||||
- DD
|
||||
- DDOG
|
||||
- DE
|
||||
- GE
|
||||
- HON
|
||||
- RTX
|
||||
- LMT
|
||||
- DECK
|
||||
- DELL
|
||||
- DEO
|
||||
- DG
|
||||
- DHI
|
||||
- DHR
|
||||
- DINO
|
||||
- DIS
|
||||
- DKNG
|
||||
- DKS
|
||||
- DLR
|
||||
- DLTR
|
||||
- DOC
|
||||
- DOV
|
||||
- DOW
|
||||
- DRI
|
||||
- DRS
|
||||
- DT
|
||||
- DTE
|
||||
- DUK
|
||||
- DVA
|
||||
- DVN
|
||||
- DXCM
|
||||
- EA
|
||||
- EBAY
|
||||
- EC
|
||||
- ECL
|
||||
- ED
|
||||
- EFX
|
||||
- EHC
|
||||
- EIX
|
||||
- EL
|
||||
- ELAN
|
||||
- ELS
|
||||
- ELV
|
||||
- EMBJ
|
||||
- EMR
|
||||
- ENB
|
||||
- ENTG
|
||||
- EOG
|
||||
- EPD
|
||||
- EQH
|
||||
- EQNR
|
||||
- EQR
|
||||
- EQT
|
||||
- ERIC
|
||||
- ES
|
||||
- ET
|
||||
- ETN
|
||||
- URI
|
||||
- AXON
|
||||
# --- Consumer ---
|
||||
- COST
|
||||
- WMT
|
||||
- MCD
|
||||
- SBUX
|
||||
- NKE
|
||||
- TGT
|
||||
- ETR
|
||||
- EVRG
|
||||
- EW
|
||||
- EWBC
|
||||
- EXAS
|
||||
- EXC
|
||||
- EXE
|
||||
- EXEL
|
||||
- EXPD
|
||||
- EXPE
|
||||
- EXR
|
||||
- F
|
||||
- FANG
|
||||
- FAST
|
||||
- FCX
|
||||
- FDX
|
||||
- FE
|
||||
- FER
|
||||
- FERG
|
||||
- FHN
|
||||
- FIG
|
||||
- FIS
|
||||
- FISV
|
||||
- FITB
|
||||
- FIVE
|
||||
- FLEX
|
||||
- FLUT
|
||||
- FNF
|
||||
- FOX
|
||||
- FOXA
|
||||
- FSLR
|
||||
- FTAI
|
||||
- FTI
|
||||
- FTNT
|
||||
- FTV
|
||||
- FUTU
|
||||
- FWONK
|
||||
- GD
|
||||
- GDDY
|
||||
- GE
|
||||
- GEHC
|
||||
- GEN
|
||||
- GEV
|
||||
- GFI
|
||||
- GFL
|
||||
- GFS
|
||||
- GH
|
||||
- GIL
|
||||
- GILD
|
||||
- GIS
|
||||
- GLPI
|
||||
- GLW
|
||||
- GM
|
||||
- GMAB
|
||||
- GME
|
||||
- GMED
|
||||
- GNRC
|
||||
- GOOG
|
||||
- GOOGL
|
||||
- GPC
|
||||
- GPN
|
||||
- GRMN
|
||||
- GS
|
||||
- GSK
|
||||
- GWRE
|
||||
- HAL
|
||||
- HAS
|
||||
- HBAN
|
||||
- HCA
|
||||
- HD
|
||||
- HDB
|
||||
- HIG
|
||||
- HL
|
||||
- HLN
|
||||
- HLT
|
||||
- HMC
|
||||
- HOLX
|
||||
- HON
|
||||
- HOOD
|
||||
- HPE
|
||||
- HPQ
|
||||
- HRL
|
||||
- HSBC
|
||||
- HST
|
||||
- HSY
|
||||
- HTHT
|
||||
- HUBS
|
||||
- HUM
|
||||
- HWM
|
||||
- IAG
|
||||
- IBKR
|
||||
- IBM
|
||||
- IBN
|
||||
- ICE
|
||||
- IFF
|
||||
- ILMN
|
||||
- INCY
|
||||
- INFY
|
||||
- ING
|
||||
- INSM
|
||||
- INTC
|
||||
- INTU
|
||||
- INVH
|
||||
- IONQ
|
||||
- IONS
|
||||
- IOT
|
||||
- IP
|
||||
- IQV
|
||||
- IR
|
||||
- IREN
|
||||
- IRM
|
||||
- ISRG
|
||||
- IT
|
||||
- ITUB
|
||||
- ITW
|
||||
- IVZ
|
||||
- JAZZ
|
||||
- JBHT
|
||||
- JBL
|
||||
- JBS
|
||||
- JCI
|
||||
- JD
|
||||
- JHX
|
||||
- JNJ
|
||||
- JPM
|
||||
- KDP
|
||||
- KEY
|
||||
- KEYS
|
||||
- KGC
|
||||
- KHC
|
||||
- KIM
|
||||
- KKR
|
||||
- KLAC
|
||||
- KMB
|
||||
- KMI
|
||||
- KO
|
||||
- KR
|
||||
- KRMN
|
||||
- KT
|
||||
- KTOS
|
||||
- KVUE
|
||||
- LDOS
|
||||
- LEN
|
||||
- LHX
|
||||
- LI
|
||||
- LIN
|
||||
- LITE
|
||||
- LLY
|
||||
- LMT
|
||||
- LNG
|
||||
- LNT
|
||||
- LOGI
|
||||
- LOW
|
||||
- LRCX
|
||||
- LSCC
|
||||
- LTM
|
||||
- LULU
|
||||
- DPZ
|
||||
# --- Financials ---
|
||||
- JPM
|
||||
- GS
|
||||
- V
|
||||
- LUV
|
||||
- LVS
|
||||
- LYB
|
||||
- LYG
|
||||
- LYV
|
||||
- MA
|
||||
- AXP
|
||||
- BLK
|
||||
- COIN
|
||||
- HOOD
|
||||
- SOFI
|
||||
- NU
|
||||
# --- Energy ---
|
||||
- XOM
|
||||
- CVX
|
||||
- COP
|
||||
- EOG
|
||||
- FANG
|
||||
# --- Communication / Media ---
|
||||
- DIS
|
||||
- CMCSA
|
||||
- MAA
|
||||
- MAR
|
||||
- MAS
|
||||
- MCD
|
||||
- MCHP
|
||||
- MCO
|
||||
- MDB
|
||||
- MDLN
|
||||
- MDLZ
|
||||
- MDT
|
||||
- MET
|
||||
- META
|
||||
- MFC
|
||||
- MFG
|
||||
- MGA
|
||||
- MGM
|
||||
- MKC
|
||||
- MKSI
|
||||
- MMM
|
||||
- MNST
|
||||
- MO
|
||||
- MP
|
||||
- MPC
|
||||
- MPLX
|
||||
- MRK
|
||||
- MRNA
|
||||
- MRSH
|
||||
- MRVL
|
||||
- MS
|
||||
- MSFT
|
||||
- MSI
|
||||
- MSTR
|
||||
- MT
|
||||
- MTB
|
||||
- MTSI
|
||||
- MU
|
||||
- MUFG
|
||||
- NBIS
|
||||
- NBIX
|
||||
- NDAQ
|
||||
- NEE
|
||||
- NEM
|
||||
- NET
|
||||
- NFLX
|
||||
- SPOT
|
||||
- NI
|
||||
- NIO
|
||||
- NKE
|
||||
- NLY
|
||||
- NMR
|
||||
- NOK
|
||||
- NOW
|
||||
- NRG
|
||||
- NSC
|
||||
- NTAP
|
||||
- NTNX
|
||||
- NTR
|
||||
- NTRA
|
||||
- NTRS
|
||||
- NU
|
||||
- NUE
|
||||
- NVDA
|
||||
- NVO
|
||||
- NVS
|
||||
- NVT
|
||||
- NWG
|
||||
- NWS
|
||||
- NWSA
|
||||
- NXPI
|
||||
- NXT
|
||||
- NYT
|
||||
- O
|
||||
- ODFL
|
||||
- OHI
|
||||
- OKE
|
||||
- OKTA
|
||||
- OMC
|
||||
- ON
|
||||
- ONON
|
||||
- ORCL
|
||||
- ORLY
|
||||
- OTIS
|
||||
- OVV
|
||||
- OWL
|
||||
- OXY
|
||||
- PAA
|
||||
- PAAS
|
||||
- PANW
|
||||
- PAYX
|
||||
- PBA
|
||||
- PBR
|
||||
- PBR-A
|
||||
- PCAR
|
||||
- PCG
|
||||
- PDD
|
||||
- PEG
|
||||
- PEN
|
||||
- PEP
|
||||
- PFE
|
||||
- PFG
|
||||
- PFGC
|
||||
- PG
|
||||
- PGR
|
||||
- PHM
|
||||
- PINS
|
||||
- PLD
|
||||
- PLTR
|
||||
- PM
|
||||
- PNC
|
||||
- PNFP
|
||||
- PNR
|
||||
- PNW
|
||||
- PPG
|
||||
- PPL
|
||||
- PR
|
||||
- PRU
|
||||
- PSA
|
||||
- PSKY
|
||||
- PSTG
|
||||
- PSX
|
||||
- PTC
|
||||
- PWR
|
||||
- PYPL
|
||||
- Q
|
||||
- QCOM
|
||||
- QSR
|
||||
- QXO
|
||||
- RBA
|
||||
- RBLX
|
||||
- RBRK
|
||||
- RCI
|
||||
- RCL
|
||||
- RDDT
|
||||
- RDY
|
||||
- REG
|
||||
- RELX
|
||||
- RF
|
||||
- RIO
|
||||
- RIVN
|
||||
- RJF
|
||||
- RKLB
|
||||
- RKT
|
||||
- RMBS
|
||||
- RMD
|
||||
- ROIV
|
||||
- ROKU
|
||||
- ROL
|
||||
- ROP
|
||||
- ROST
|
||||
- RPM
|
||||
- RPRX
|
||||
- RRC
|
||||
- RRX
|
||||
- RSG
|
||||
- RTO
|
||||
- RTX
|
||||
- RVMD
|
||||
- RY
|
||||
- RYAAY
|
||||
- SAN
|
||||
- SAP
|
||||
- SATS
|
||||
- SBS
|
||||
- SBUX
|
||||
- SCCO
|
||||
- SCHW
|
||||
- SCI
|
||||
- SE
|
||||
- SF
|
||||
- SGI
|
||||
- SHEL
|
||||
- SHOP
|
||||
- SHW
|
||||
- SJM
|
||||
- SKM
|
||||
- SLB
|
||||
- SMCI
|
||||
- SMFG
|
||||
- SMMT
|
||||
- SN
|
||||
- SNDK
|
||||
- SNOW
|
||||
- SNPS
|
||||
- SNY
|
||||
- SO
|
||||
- SOFI
|
||||
- SOLS
|
||||
- SOLV
|
||||
- SONY
|
||||
- SPG
|
||||
- SPGI
|
||||
- SPOT
|
||||
- SQM
|
||||
- SRE
|
||||
- SSNC
|
||||
- STLA
|
||||
- STLD
|
||||
- STM
|
||||
- STT
|
||||
- STX
|
||||
- STZ
|
||||
- SU
|
||||
- SUNB
|
||||
- SUZ
|
||||
- SW
|
||||
- SWK
|
||||
- SYF
|
||||
- SYK
|
||||
- SYM
|
||||
- SYY
|
||||
- T
|
||||
- TAK
|
||||
- TCOM
|
||||
- TD
|
||||
- TEAM
|
||||
- TECK
|
||||
- TEL
|
||||
- TER
|
||||
- TEVA
|
||||
- TFC
|
||||
- TGT
|
||||
- TIGO
|
||||
- TJX
|
||||
- TKO
|
||||
- TME
|
||||
- TMO
|
||||
- TMUS
|
||||
- TOL
|
||||
- TOST
|
||||
- TPG
|
||||
- TPR
|
||||
- TRGP
|
||||
- TRI
|
||||
- TRMB
|
||||
- TROW
|
||||
- TRP
|
||||
- TRU
|
||||
- TRV
|
||||
- TS
|
||||
- TSCO
|
||||
- TSEM
|
||||
- TSLA
|
||||
- TSM
|
||||
- TSN
|
||||
- TT
|
||||
- TTD
|
||||
- TTE
|
||||
- TTWO
|
||||
- TU
|
||||
- TW
|
||||
- TWLO
|
||||
- TXN
|
||||
- TXT
|
||||
- UAL
|
||||
- UBER
|
||||
- UBS
|
||||
- UDR
|
||||
- UL
|
||||
- ULS
|
||||
- UMC
|
||||
- UNH
|
||||
- UNM
|
||||
- UNP
|
||||
- UPS
|
||||
- USB
|
||||
- USFD
|
||||
- V
|
||||
- VALE
|
||||
- VEEV
|
||||
- VG
|
||||
- VICI
|
||||
- VIK
|
||||
- VLO
|
||||
- VLTO
|
||||
- VMC
|
||||
- VNOM
|
||||
- VOD
|
||||
- VRSK
|
||||
- VRT
|
||||
- VRTX
|
||||
- VST
|
||||
- VTR
|
||||
- VTRS
|
||||
- VZ
|
||||
- WAT
|
||||
- WBD
|
||||
- WBS
|
||||
- WCN
|
||||
- WDAY
|
||||
- WDC
|
||||
- WDS
|
||||
- WEC
|
||||
- WELL
|
||||
- WES
|
||||
- WFC
|
||||
- WLK
|
||||
- WM
|
||||
- WMB
|
||||
- WMG
|
||||
- WMT
|
||||
- WPC
|
||||
- WPM
|
||||
- WRB
|
||||
- WSM
|
||||
- WTRG
|
||||
- WY
|
||||
- WYNN
|
||||
- XEL
|
||||
- XOM
|
||||
- XPEV
|
||||
- XPO
|
||||
- XYL
|
||||
- XYZ
|
||||
- YPF
|
||||
- YUM
|
||||
- YUMC
|
||||
- Z
|
||||
- ZBH
|
||||
- ZG
|
||||
- ZM
|
||||
- ZS
|
||||
- ZTO
|
||||
- ZTS
|
||||
|
||||
@ -0,0 +1,744 @@
|
||||
symbols:
|
||||
# --- Existing-only (not in screener, kept) ---
|
||||
- AXON
|
||||
- BILL
|
||||
- BLK
|
||||
- BRZE
|
||||
- DOCN
|
||||
- DPZ
|
||||
- GTLB
|
||||
- HIMS
|
||||
- MELI
|
||||
- MNDY
|
||||
- PCOR
|
||||
- S
|
||||
- SQ
|
||||
- URI
|
||||
# --- Screener ($10B+ mcap, 1M+ vol) — 727 symbols ---
|
||||
- A
|
||||
- AA
|
||||
- AAPL
|
||||
- ABBV
|
||||
- ABNB
|
||||
- ABT
|
||||
- ACGL
|
||||
- ACM
|
||||
- ACN
|
||||
- ADBE
|
||||
- ADI
|
||||
- ADM
|
||||
- ADP
|
||||
- ADSK
|
||||
- AEE
|
||||
- AEG
|
||||
- AEM
|
||||
- AEP
|
||||
- AER
|
||||
- AES
|
||||
- AFL
|
||||
- AFRM
|
||||
- AG
|
||||
- AGI
|
||||
- AGNC
|
||||
- AIG
|
||||
- AJG
|
||||
- AKAM
|
||||
- ALAB
|
||||
- ALB
|
||||
- ALC
|
||||
- ALGN
|
||||
- ALL
|
||||
- ALLY
|
||||
- ALNY
|
||||
- AM
|
||||
- AMAT
|
||||
- AMCR
|
||||
- AMD
|
||||
- AME
|
||||
- AMGN
|
||||
- AMH
|
||||
- AMKR
|
||||
- AMRZ
|
||||
- AMT
|
||||
- AMX
|
||||
- AMZN
|
||||
- ANET
|
||||
- AON
|
||||
- APA
|
||||
- APD
|
||||
- APG
|
||||
- APH
|
||||
- APO
|
||||
- APP
|
||||
- APTV
|
||||
- AR
|
||||
- ARCC
|
||||
- ARES
|
||||
- ARM
|
||||
- ARMK
|
||||
- AS
|
||||
- ASML
|
||||
- ASTS
|
||||
- ASX
|
||||
- ATI
|
||||
- ATO
|
||||
- AU
|
||||
- AVAV
|
||||
- AVB
|
||||
- AVGO
|
||||
- AWK
|
||||
- AXIA
|
||||
- AXP
|
||||
- AZN
|
||||
- B
|
||||
- BA
|
||||
- BABA
|
||||
- BAC
|
||||
- BALL
|
||||
- BAM
|
||||
- BBIO
|
||||
- BBVA
|
||||
- BBY
|
||||
- BCE
|
||||
- BCS
|
||||
- BDX
|
||||
- BE
|
||||
- BEKE
|
||||
- BEN
|
||||
- BF-B
|
||||
- BG
|
||||
- BHP
|
||||
- BIDU
|
||||
- BIIB
|
||||
- BILI
|
||||
- BJ
|
||||
- BK
|
||||
- BKR
|
||||
- BMRN
|
||||
- BMY
|
||||
- BN
|
||||
- BNS
|
||||
- BNTX
|
||||
- BP
|
||||
- BR
|
||||
- BRK-B
|
||||
- BRO
|
||||
- BSX
|
||||
- BSY
|
||||
- BTI
|
||||
- BUD
|
||||
- BWA
|
||||
- BX
|
||||
- C
|
||||
- CAH
|
||||
- CARR
|
||||
- CART
|
||||
- CAT
|
||||
- CB
|
||||
- CBRE
|
||||
- CCEP
|
||||
- CCI
|
||||
- CCJ
|
||||
- CCK
|
||||
- CCL
|
||||
- CDE
|
||||
- CDNS
|
||||
- CDW
|
||||
- CEG
|
||||
- CELH
|
||||
- CF
|
||||
- CFG
|
||||
- CFLT
|
||||
- CG
|
||||
- CHD
|
||||
- CHKP
|
||||
- CHRW
|
||||
- CHTR
|
||||
- CHWY
|
||||
- CI
|
||||
- CIEN
|
||||
- CL
|
||||
- CLS
|
||||
- CLX
|
||||
- CM
|
||||
- CMCSA
|
||||
- CME
|
||||
- CMG
|
||||
- CMS
|
||||
- CNC
|
||||
- CNH
|
||||
- CNI
|
||||
- CNP
|
||||
- CNQ
|
||||
- COF
|
||||
- COHR
|
||||
- COIN
|
||||
- COO
|
||||
- COP
|
||||
- COR
|
||||
- COST
|
||||
- CP
|
||||
- CPNG
|
||||
- CPRT
|
||||
- CPT
|
||||
- CRBG
|
||||
- CRCL
|
||||
- CRDO
|
||||
- CRH
|
||||
- CRM
|
||||
- CRWD
|
||||
- CRWV
|
||||
- CSCO
|
||||
- CSGP
|
||||
- CSX
|
||||
- CTAS
|
||||
- CTRA
|
||||
- CTSH
|
||||
- CTVA
|
||||
- CUK
|
||||
- CVE
|
||||
- CVNA
|
||||
- CVS
|
||||
- CVX
|
||||
- CX
|
||||
- D
|
||||
- DAL
|
||||
- DASH
|
||||
- DB
|
||||
- DD
|
||||
- DDOG
|
||||
- DE
|
||||
- DECK
|
||||
- DELL
|
||||
- DEO
|
||||
- DG
|
||||
- DHI
|
||||
- DHR
|
||||
- DINO
|
||||
- DIS
|
||||
- DKNG
|
||||
- DKS
|
||||
- DLR
|
||||
- DLTR
|
||||
- DOC
|
||||
- DOV
|
||||
- DOW
|
||||
- DRI
|
||||
- DRS
|
||||
- DT
|
||||
- DTE
|
||||
- DUK
|
||||
- DVA
|
||||
- DVN
|
||||
- DXCM
|
||||
- EA
|
||||
- EBAY
|
||||
- EC
|
||||
- ECL
|
||||
- ED
|
||||
- EFX
|
||||
- EHC
|
||||
- EIX
|
||||
- EL
|
||||
- ELAN
|
||||
- ELS
|
||||
- ELV
|
||||
- EMBJ
|
||||
- EMR
|
||||
- ENB
|
||||
- ENTG
|
||||
- EOG
|
||||
- EPD
|
||||
- EQH
|
||||
- EQNR
|
||||
- EQR
|
||||
- EQT
|
||||
- ERIC
|
||||
- ES
|
||||
- ET
|
||||
- ETN
|
||||
- ETR
|
||||
- EVRG
|
||||
- EW
|
||||
- EWBC
|
||||
- EXAS
|
||||
- EXC
|
||||
- EXE
|
||||
- EXEL
|
||||
- EXPD
|
||||
- EXPE
|
||||
- EXR
|
||||
- F
|
||||
- FANG
|
||||
- FAST
|
||||
- FCX
|
||||
- FDX
|
||||
- FE
|
||||
- FER
|
||||
- FERG
|
||||
- FHN
|
||||
- FIG
|
||||
- FIS
|
||||
- FISV
|
||||
- FITB
|
||||
- FIVE
|
||||
- FLEX
|
||||
- FLUT
|
||||
- FNF
|
||||
- FOX
|
||||
- FOXA
|
||||
- FSLR
|
||||
- FTAI
|
||||
- FTI
|
||||
- FTNT
|
||||
- FTV
|
||||
- FUTU
|
||||
- FWONK
|
||||
- GD
|
||||
- GDDY
|
||||
- GE
|
||||
- GEHC
|
||||
- GEN
|
||||
- GEV
|
||||
- GFI
|
||||
- GFL
|
||||
- GFS
|
||||
- GH
|
||||
- GIL
|
||||
- GILD
|
||||
- GIS
|
||||
- GLPI
|
||||
- GLW
|
||||
- GM
|
||||
- GMAB
|
||||
- GME
|
||||
- GMED
|
||||
- GNRC
|
||||
- GOOG
|
||||
- GOOGL
|
||||
- GPC
|
||||
- GPN
|
||||
- GRMN
|
||||
- GS
|
||||
- GSK
|
||||
- GWRE
|
||||
- HAL
|
||||
- HAS
|
||||
- HBAN
|
||||
- HCA
|
||||
- HD
|
||||
- HDB
|
||||
- HIG
|
||||
- HL
|
||||
- HLN
|
||||
- HLT
|
||||
- HMC
|
||||
- HOLX
|
||||
- HON
|
||||
- HOOD
|
||||
- HPE
|
||||
- HPQ
|
||||
- HRL
|
||||
- HSBC
|
||||
- HST
|
||||
- HSY
|
||||
- HTHT
|
||||
- HUBS
|
||||
- HUM
|
||||
- HWM
|
||||
- IAG
|
||||
- IBKR
|
||||
- IBM
|
||||
- IBN
|
||||
- ICE
|
||||
- IFF
|
||||
- ILMN
|
||||
- INCY
|
||||
- INFY
|
||||
- ING
|
||||
- INSM
|
||||
- INTC
|
||||
- INTU
|
||||
- INVH
|
||||
- IONQ
|
||||
- IONS
|
||||
- IOT
|
||||
- IP
|
||||
- IQV
|
||||
- IR
|
||||
- IREN
|
||||
- IRM
|
||||
- ISRG
|
||||
- IT
|
||||
- ITUB
|
||||
- ITW
|
||||
- IVZ
|
||||
- JAZZ
|
||||
- JBHT
|
||||
- JBL
|
||||
- JBS
|
||||
- JCI
|
||||
- JD
|
||||
- JHX
|
||||
- JNJ
|
||||
- JPM
|
||||
- KDP
|
||||
- KEY
|
||||
- KEYS
|
||||
- KGC
|
||||
- KHC
|
||||
- KIM
|
||||
- KKR
|
||||
- KLAC
|
||||
- KMB
|
||||
- KMI
|
||||
- KO
|
||||
- KR
|
||||
- KRMN
|
||||
- KT
|
||||
- KTOS
|
||||
- KVUE
|
||||
- LDOS
|
||||
- LEN
|
||||
- LHX
|
||||
- LI
|
||||
- LIN
|
||||
- LITE
|
||||
- LLY
|
||||
- LMT
|
||||
- LNG
|
||||
- LNT
|
||||
- LOGI
|
||||
- LOW
|
||||
- LRCX
|
||||
- LSCC
|
||||
- LTM
|
||||
- LULU
|
||||
- LUV
|
||||
- LVS
|
||||
- LYB
|
||||
- LYG
|
||||
- LYV
|
||||
- MA
|
||||
- MAA
|
||||
- MAR
|
||||
- MAS
|
||||
- MCD
|
||||
- MCHP
|
||||
- MCO
|
||||
- MDB
|
||||
- MDLN
|
||||
- MDLZ
|
||||
- MDT
|
||||
- MET
|
||||
- META
|
||||
- MFC
|
||||
- MFG
|
||||
- MGA
|
||||
- MGM
|
||||
- MKC
|
||||
- MKSI
|
||||
- MMM
|
||||
- MNST
|
||||
- MO
|
||||
- MP
|
||||
- MPC
|
||||
- MPLX
|
||||
- MRK
|
||||
- MRNA
|
||||
- MRSH
|
||||
- MRVL
|
||||
- MS
|
||||
- MSFT
|
||||
- MSI
|
||||
- MSTR
|
||||
- MT
|
||||
- MTB
|
||||
- MTSI
|
||||
- MU
|
||||
- MUFG
|
||||
- NBIS
|
||||
- NBIX
|
||||
- NDAQ
|
||||
- NEE
|
||||
- NEM
|
||||
- NET
|
||||
- NFLX
|
||||
- NI
|
||||
- NIO
|
||||
- NKE
|
||||
- NLY
|
||||
- NMR
|
||||
- NOK
|
||||
- NOW
|
||||
- NRG
|
||||
- NSC
|
||||
- NTAP
|
||||
- NTNX
|
||||
- NTR
|
||||
- NTRA
|
||||
- NTRS
|
||||
- NU
|
||||
- NUE
|
||||
- NVDA
|
||||
- NVO
|
||||
- NVS
|
||||
- NVT
|
||||
- NWG
|
||||
- NWS
|
||||
- NWSA
|
||||
- NXPI
|
||||
- NXT
|
||||
- NYT
|
||||
- O
|
||||
- ODFL
|
||||
- OHI
|
||||
- OKE
|
||||
- OKTA
|
||||
- OMC
|
||||
- ON
|
||||
- ONON
|
||||
- ORCL
|
||||
- ORLY
|
||||
- OTIS
|
||||
- OVV
|
||||
- OWL
|
||||
- OXY
|
||||
- PAA
|
||||
- PAAS
|
||||
- PANW
|
||||
- PAYX
|
||||
- PBA
|
||||
- PBR
|
||||
- PBR-A
|
||||
- PCAR
|
||||
- PCG
|
||||
- PDD
|
||||
- PEG
|
||||
- PEN
|
||||
- PEP
|
||||
- PFE
|
||||
- PFG
|
||||
- PFGC
|
||||
- PG
|
||||
- PGR
|
||||
- PHM
|
||||
- PINS
|
||||
- PLD
|
||||
- PLTR
|
||||
- PM
|
||||
- PNC
|
||||
- PNFP
|
||||
- PNR
|
||||
- PNW
|
||||
- PPG
|
||||
- PPL
|
||||
- PR
|
||||
- PRU
|
||||
- PSA
|
||||
- PSKY
|
||||
- PSTG
|
||||
- PSX
|
||||
- PTC
|
||||
- PWR
|
||||
- PYPL
|
||||
- Q
|
||||
- QCOM
|
||||
- QSR
|
||||
- QXO
|
||||
- RBA
|
||||
- RBLX
|
||||
- RBRK
|
||||
- RCI
|
||||
- RCL
|
||||
- RDDT
|
||||
- RDY
|
||||
- REG
|
||||
- RELX
|
||||
- RF
|
||||
- RIO
|
||||
- RIVN
|
||||
- RJF
|
||||
- RKLB
|
||||
- RKT
|
||||
- RMBS
|
||||
- RMD
|
||||
- ROIV
|
||||
- ROKU
|
||||
- ROL
|
||||
- ROP
|
||||
- ROST
|
||||
- RPM
|
||||
- RPRX
|
||||
- RRC
|
||||
- RRX
|
||||
- RSG
|
||||
- RTO
|
||||
- RTX
|
||||
- RVMD
|
||||
- RY
|
||||
- RYAAY
|
||||
- SAN
|
||||
- SAP
|
||||
- SATS
|
||||
- SBS
|
||||
- SBUX
|
||||
- SCCO
|
||||
- SCHW
|
||||
- SCI
|
||||
- SE
|
||||
- SF
|
||||
- SGI
|
||||
- SHEL
|
||||
- SHOP
|
||||
- SHW
|
||||
- SJM
|
||||
- SKM
|
||||
- SLB
|
||||
- SMCI
|
||||
- SMFG
|
||||
- SMMT
|
||||
- SN
|
||||
- SNDK
|
||||
- SNOW
|
||||
- SNPS
|
||||
- SNY
|
||||
- SO
|
||||
- SOFI
|
||||
- SOLS
|
||||
- SOLV
|
||||
- SONY
|
||||
- SPG
|
||||
- SPGI
|
||||
- SPOT
|
||||
- SQM
|
||||
- SRE
|
||||
- SSNC
|
||||
- STLA
|
||||
- STLD
|
||||
- STM
|
||||
- STT
|
||||
- STX
|
||||
- STZ
|
||||
- SU
|
||||
- SUNB
|
||||
- SUZ
|
||||
- SW
|
||||
- SWK
|
||||
- SYF
|
||||
- SYK
|
||||
- SYM
|
||||
- SYY
|
||||
- T
|
||||
- TAK
|
||||
- TCOM
|
||||
- TD
|
||||
- TEAM
|
||||
- TECK
|
||||
- TEL
|
||||
- TER
|
||||
- TEVA
|
||||
- TFC
|
||||
- TGT
|
||||
- TIGO
|
||||
- TJX
|
||||
- TKO
|
||||
- TME
|
||||
- TMO
|
||||
- TMUS
|
||||
- TOL
|
||||
- TOST
|
||||
- TPG
|
||||
- TPR
|
||||
- TRGP
|
||||
- TRI
|
||||
- TRMB
|
||||
- TROW
|
||||
- TRP
|
||||
- TRU
|
||||
- TRV
|
||||
- TS
|
||||
- TSCO
|
||||
- TSEM
|
||||
- TSLA
|
||||
- TSM
|
||||
- TSN
|
||||
- TT
|
||||
- TTD
|
||||
- TTE
|
||||
- TTWO
|
||||
- TU
|
||||
- TW
|
||||
- TWLO
|
||||
- TXN
|
||||
- TXT
|
||||
- UAL
|
||||
- UBER
|
||||
- UBS
|
||||
- UDR
|
||||
- UL
|
||||
- ULS
|
||||
- UMC
|
||||
- UNH
|
||||
- UNM
|
||||
- UNP
|
||||
- UPS
|
||||
- USB
|
||||
- USFD
|
||||
- V
|
||||
- VALE
|
||||
- VEEV
|
||||
- VG
|
||||
- VICI
|
||||
- VIK
|
||||
- VLO
|
||||
- VLTO
|
||||
- VMC
|
||||
- VNOM
|
||||
- VOD
|
||||
- VRSK
|
||||
- VRT
|
||||
- VRTX
|
||||
- VST
|
||||
- VTR
|
||||
- VTRS
|
||||
- VZ
|
||||
- WAT
|
||||
- WBD
|
||||
- WBS
|
||||
- WCN
|
||||
- WDAY
|
||||
- WDC
|
||||
- WDS
|
||||
- WEC
|
||||
- WELL
|
||||
- WES
|
||||
- WFC
|
||||
- WLK
|
||||
- WM
|
||||
- WMB
|
||||
- WMG
|
||||
- WMT
|
||||
- WPC
|
||||
- WPM
|
||||
- WRB
|
||||
- WSM
|
||||
- WTRG
|
||||
- WY
|
||||
- WYNN
|
||||
- XEL
|
||||
- XOM
|
||||
- XPEV
|
||||
- XPO
|
||||
- XYL
|
||||
- XYZ
|
||||
- YPF
|
||||
- YUM
|
||||
- YUMC
|
||||
- Z
|
||||
- ZBH
|
||||
- ZG
|
||||
- ZM
|
||||
- ZS
|
||||
- ZTO
|
||||
- ZTS
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue