Discovery: merge %-gainer movers (the wildcards volume-ranking can't see)
Forensic from the source trader's 2026-07-02 recap: his ENTIRE day (+$8.9k) was CX — a thin reverse-split name that squeezed $3->$6 on a headline (his "wildcard effect": on dead pre-holiday days the one mover gets everyone's attention). Our system NEVER SAW IT: zero bars, zero discovery events. Root cause: discovery ranks by ABSOLUTE SHARE VOLUME (most-actives), and a thin %-squeezer never cracks a volume-ranked top-N. Every other name he called out we saw and judged the same way he did (DSY passed/backed-out, CWD too cheap, AHMA rejected) — the gap was pure discovery scope. - alpaca_paper/client.py: get_movers(top) — /v1beta1/screener/stocks/movers top %-gainers (verified working with our keys: CLRO +114%, CWD +73% live). - discover_active_symbols: wildcard gainers are fetched FIRST (warrants/units filtered: '.'-suffixed + 5-char W-endings), deduped with most-actives, same price-band filter. Additive + fail-open: a movers failure never breaks the existing actives path. Discovery/ingestion scope ONLY — every trading gate (confirmation window, quality, VWAP, cap) still applies unchanged. The immediate value is that shadow tracks + the labeler finally get BARS for wildcard movers, so the next CX is measurable instead of invisible. Verified live: universe now leads with gainer-sourced names (CLRO, CWD, USDE, YRD, DSY). 16 tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
6c36d04f7a
commit
4fc02a8c71
3 files changed
+185
-1
No files matched your search
@@ -273,9 +273,24 @@ def discover_active_symbols(
|
||||
static symbol list.
|
||||
"""
|
||||
try:
|
||||
# WILDCARD movers first: top %-GAINERS regardless of absolute share volume.
|
||||
# A thin name squeezing on a headline (CX 2026-07-02: $3->$6, the source
|
||||
# trader's entire day) never cracks a volume-ranked top-N — this is the feed
|
||||
# that sees it. Discovery/ingestion only; every trading gate still applies.
|
||||
gainers: list[str] = []
|
||||
try:
|
||||
for g in client.get_movers(top=15):
|
||||
sym = (g.get("symbol") or "").strip()
|
||||
# drop warrants/units/rights noise (BKSY.WS, EVLVW-style)
|
||||
if not sym or "." in sym or (len(sym) >= 5 and sym.endswith("W")):
|
||||
continue
|
||||
gainers.append(sym)
|
||||
except Exception: # noqa: BLE001 — movers is additive; actives still work
|
||||
pass
|
||||
# Alpaca caps the most-actives `top` at 100; never request more.
|
||||
actives = client.get_most_actives(top=min(top * 3, 100), by="volume")
|
||||
symbols = [a["symbol"] for a in actives if a.get("symbol")]
|
||||
symbols = list(dict.fromkeys(
|
||||
gainers + [a["symbol"] for a in actives if a.get("symbol")]))
|
||||
if not symbols:
|
||||
return []
|
||||
trades = client.get_latest_trades(symbols) # batched internally; price them all
|
||||
|
||||
Reference in new issue
Block a user