learn(iterate): 2026-04-13 — automated iteration run

This commit is contained in:
github-actions[bot] 2026-04-13 07:52:59 +00:00
parent ddb2a010e7
commit 17e45df41a
4 changed files with 38 additions and 8 deletions

View File

@ -1,6 +1,6 @@
# Learnings Index # Learnings Index
**Last analyzed run:** 2026-04-12 **Last analyzed run:** 2026-04-13
| Domain | File | Last Updated | One-line Summary | | Domain | File | Last Updated | One-line Summary |
|--------|------|--------------|-----------------| |--------|------|--------------|-----------------|
@ -13,7 +13,7 @@
| early_accumulation | scanners/early_accumulation.md | 2026-04-12 | Sub-threshold (score=60); no catalyst → structurally score-capped by ranker | | early_accumulation | scanners/early_accumulation.md | 2026-04-12 | Sub-threshold (score=60); no catalyst → structurally score-capped by ranker |
| social_dd | scanners/social_dd.md | 2026-04-12 | Sub-threshold (score=56); BUT 55% 30d win rate — diverges from social_hype; ranker may be suppressing it incorrectly | | social_dd | scanners/social_dd.md | 2026-04-12 | Sub-threshold (score=56); BUT 55% 30d win rate — diverges from social_hype; ranker may be suppressing it incorrectly |
| volume_accumulation | scanners/volume_accumulation.md | — | No data yet | | volume_accumulation | scanners/volume_accumulation.md | — | No data yet |
| short_squeeze | scanners/short_squeeze.md | — | No data yet — new scanner, research: high SI (>20%) + catalyst = squeeze risk; not a directional signal alone | | short_squeeze | scanners/short_squeeze.md | 2026-04-13 | First real data: 60% 7d win rate, +2.15% avg 7d (n=10) — best 7d performer; DTC now surfaced in context |
## Research ## Research

View File

@ -3,18 +3,35 @@
## Current Understanding ## Current Understanding
Identifies stocks with structurally high short interest (>15% of float by default, CRITICAL at >30%) Identifies stocks with structurally high short interest (>15% of float by default, CRITICAL at >30%)
where short sellers are vulnerable to forced covering on any positive catalyst. The scanner uses where short sellers are vulnerable to forced covering on any positive catalyst. The scanner uses
Finviz for discovery (screener filters) + Yahoo Finance for exact SI% verification. Finviz for discovery (screener filters) + Yahoo Finance for exact SI% and days-to-cover (shortRatio)
verification.
Key distinction: High SI alone predicts *negative* long-term returns on average (academic consensus). Key distinction: High SI alone predicts *negative* long-term returns on average (academic consensus).
The scanner is a squeeze-risk flag, not a directional buy signal. Value comes from cross-scanner However, first real P&L data (n=10) shows 60% 7d win rate and +2.15% avg 7d — best 7d performer in
confluence: a stock appearing here AND in options_flow or earnings_calendar is significantly stronger the pipeline. This may reflect that discovery-pipeline filtering (technical confirmation, enrichment)
than either signal alone. already adds the catalyst signal needed to convert squeeze-risk into a directional trade. Cross-scanner
confluence (short_squeeze + options_flow or earnings_calendar) remains a stronger signal than either
alone and is the primary confluence hypothesis under test.
## Evidence Log ## Evidence Log
_(populated by /iterate runs)_ ### 2026-04-13 — P&L review (first real outcome data)
- 10 tracked recommendations, 5/10 1d wins (50% win rate), 6/10 7d wins (60% win rate).
- Avg 7d return: +2.15%. This makes short_squeeze the **best 7d performer** among scanners with ≥5 samples.
- Outperforms analyst_upgrade (50% 7d), insider_buying (46.4% 7d), options_flow (45.6% 7d).
- The scanner is producing positive outcomes as a standalone signal, not only as a cross-scanner modifier.
- However, ranker prompt says "Focus on days to cover" but context string only shows SI%. DTC value is available in Yahoo Finance (`shortRatio`) but was not being fetched or passed through — gap confirmed.
- Confidence: medium (small sample n=10; 30d data will be more conclusive; DTC gap has been fixed)
### 2026-04-13 — Code fix: days_to_cover surfaced in context
- Added `days_to_cover` extraction (`shortRatio` from Yahoo Finance) to `finviz_scraper.py`.
- Applied `min_days_to_cover` filter (previously accepted as parameter but never enforced).
- Updated `short_squeeze.py` context string to include DTC value so ranker can use "days to cover" criterion.
- Confidence: high (this is a clear context gap between ranker criteria and available data)
## Pending Hypotheses ## Pending Hypotheses
- [ ] Does short_squeeze + options_flow confluence produce better 7d win rate than either scanner alone? - [ ] Does short_squeeze + options_flow confluence produce better 7d win rate than either scanner alone?
- [ ] Does short_squeeze + earnings_calendar (SI>20%) produce better outcomes than earnings alone? (See earnings_calendar.md pending hypothesis) - [ ] Does short_squeeze + earnings_calendar (SI>20%) produce better outcomes than earnings alone? (See earnings_calendar.md pending hypothesis)
- [ ] Is there a volume threshold (e.g., market cap <$2B small-cap) that sharpens the signal? - [ ] Is there a volume threshold (e.g., market cap <$2B small-cap) that sharpens the signal?
- [ ] Does DTC >5 (now surfaced in context) predict better outcomes than DTC 2-5 within the scanner?
- [ ] Does standalone short_squeeze (no cross-scanner confluence) continue to outperform at 7d as sample grows?

View File

@ -67,6 +67,7 @@ class ShortSqueezeScanner(BaseScanner):
continue continue
si_pct = item.get("short_interest_pct", 0) si_pct = item.get("short_interest_pct", 0)
dtc = item.get("days_to_cover", 0.0)
signal = item.get("signal", "low_squeeze_potential") signal = item.get("signal", "low_squeeze_potential")
label = _SIGNAL_LABELS.get(signal, signal) label = _SIGNAL_LABELS.get(signal, signal)
@ -78,8 +79,9 @@ class ShortSqueezeScanner(BaseScanner):
else: else:
priority = Priority.MEDIUM.value priority = Priority.MEDIUM.value
dtc_str = f"{dtc:.1f}" if dtc else "N/A"
context = ( context = (
f"Short interest {si_pct:.1f}% of float{label}" f"Short interest {si_pct:.1f}% of float, {dtc_str} days to cover{label}"
" | squeeze risk elevates if catalyst arrives" " | squeeze risk elevates if catalyst arrives"
) )
@ -91,6 +93,7 @@ class ShortSqueezeScanner(BaseScanner):
"priority": priority, "priority": priority,
"strategy": self.strategy, "strategy": self.strategy,
"short_interest_pct": si_pct, "short_interest_pct": si_pct,
"days_to_cover": dtc,
"squeeze_signal": signal, "squeeze_signal": signal,
} }
) )

View File

@ -115,6 +115,15 @@ def get_short_interest(
market_cap = info.get("marketCap", 0) market_cap = info.get("marketCap", 0)
volume = info.get("volume", info.get("regularMarketVolume", 0)) volume = info.get("volume", info.get("regularMarketVolume", 0))
# Days to cover (short ratio): shares short / avg daily volume
days_to_cover = info.get("shortRatio")
if days_to_cover is None or not isinstance(days_to_cover, (int, float)):
days_to_cover = 0.0
# Apply days-to-cover filter
if days_to_cover < min_days_to_cover:
return None
# Categorize squeeze potential # Categorize squeeze potential
if short_pct >= 30: if short_pct >= 30:
signal = "extreme_squeeze_risk" signal = "extreme_squeeze_risk"
@ -131,6 +140,7 @@ def get_short_interest(
"market_cap": market_cap, "market_cap": market_cap,
"volume": volume, "volume": volume,
"short_interest_pct": short_pct, "short_interest_pct": short_pct,
"days_to_cover": days_to_cover,
"signal": signal, "signal": signal,
} }
except Exception: except Exception: