Selection refinements from clean-universe replay of the trader's criteria
Tested the source trader's numeric selection playbook on the unbiased (open-knowable) year universe, n=2,643 point-in-time ORB entries: - Dead band RE-CONFIRMED on clean data (35-75% +0.006, 75-150% -0.066) — CLRO/VTAK were anecdotes; band exclusion + squeeze exception stand. - His $5-10 price sweet spot inverts for our system: edge RISES with price ($10-20 best at +0.143); the $1-2 bucket is negative (-0.031, n=609). SHIPPED: price floor $1->$2 (population gate band now env-driven TRADING_PRICE_MIN/MAX; watcher floor raised to match). - His rvol>=5 floor is backwards for us: rvol 2-5x at entry +0.346R vs >=10x -0.047 (n=818) — crowded spotlight names hurt our entries. NOT shipped (conflicts with the squeeze exception's rvol needs; logged for forward validation via the new per-signal relative_volume). - SHIPPED: buyout/merger headlines never rank-boost (price is pegged — the CRNX class); keyword suppression in the catalyst multiplier. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
722add6815
commit
ff3a4b46fb
3 files changed
+23
-3
No files matched your search
@@ -759,7 +759,18 @@ def main(argv: list[str] | None = None) -> int:
|
||||
news_rank_gain = float(os.environ.get("NEWS_RANK_BOOST_GAIN", "2.0"))
|
||||
dilutive_penalty = float(os.environ.get("NEWS_DILUTIVE_PENALTY", "0.1"))
|
||||
|
||||
_PEGGED = ("acqui", "merger", "buyout", "to be acquired", "take-private")
|
||||
|
||||
def _headline_pegged(sym: str) -> bool:
|
||||
"""Buyout/merger headlines PEG the price (CRNX 2026-07-07: pinned at
|
||||
$83.50 by an $85 buyout) — the opposite of a momentum catalyst. Never
|
||||
rank-boost them."""
|
||||
h = (news_map.get(sym) or "").lower()
|
||||
return any(k in h for k in _PEGGED)
|
||||
|
||||
def _cat_mult(sym: str, has_headline: bool) -> float:
|
||||
if has_headline and _headline_pegged(sym):
|
||||
return 1.0
|
||||
if not ollama_cfg.catalyst_score_enabled:
|
||||
return news_boost if has_headline else 1.0
|
||||
from research.ingestion.news_enrichment import catalyst_score as _cscore
|
||||
|
||||
@@ -851,7 +851,8 @@ def test_population_gate_full_band_semantics():
|
||||
assert ok(gap_pct=2.78, rvol=40.0) is None # LHSW: verified squeeze
|
||||
assert ok(gap_pct=2.78, rvol=0.4) is not None # split fake: rejected
|
||||
assert ok(gap_pct=2.78) is not None # unverified squeeze: rejected
|
||||
assert ok(gap_pct=0.20, price=0.98) is not None # SKIN: below $1
|
||||
assert ok(gap_pct=0.20, price=0.98) is not None # SKIN: below the floor
|
||||
assert ok(gap_pct=0.20, price=1.50) is not None # $1-2 bucket: -0.031R/609 unbiased entries
|
||||
assert ok(gap_pct=0.20, price=21.5) is not None # above $20
|
||||
assert ok(gap_pct=0.20, price=5.0) is None
|
||||
assert ok(gap_pct=None) is None # fail-open on missing gap
|
||||
+10
-2
@@ -113,6 +113,11 @@ class ExecutionSettings:
|
||||
# 0 = disabled. Fail-open: a signal missing gap/volume data is never blocked.
|
||||
min_entry_gap_pct: float = 0.0
|
||||
min_day_volume: float = 0.0
|
||||
# tradeable price band. Floor raised $1->$2 2026-07-09: the $1-2 bucket ran
|
||||
# -0.031R over 609 unbiased year-scale entries (worst bucket; spreads are
|
||||
# brutal sub-$2, and the source trader's own cutoff is ~$2).
|
||||
price_band_min: float = 2.0
|
||||
price_band_max: float = 20.0
|
||||
# UNIFIED ENTRY: run the fast path's shared anti-chase gates (over_extended,
|
||||
# day-extension, halt) on the LIVE auto path too. These previously ran ONLY on
|
||||
# the fast trigger path, leaving live auto entries unprotected (see the
|
||||
@@ -276,6 +281,8 @@ class ExecutionSettings:
|
||||
entry_confirm_by_minute=int(values.get("TRADING_ENTRY_CONFIRM_BY_MINUTE", "0")),
|
||||
min_entry_gap_pct=_qfloat(values.get("TRADING_MIN_ENTRY_GAP_PCT", "0.0")),
|
||||
min_day_volume=float(values.get("TRADING_MIN_DAY_VOLUME", "0.0")),
|
||||
price_band_min=float(values.get("TRADING_PRICE_MIN", "2.0")),
|
||||
price_band_max=float(values.get("TRADING_PRICE_MAX", "20.0")),
|
||||
unified_entry=flag("TRADING_UNIFIED_ENTRY", "1"),
|
||||
entry_fill_model=values.get("TRADING_ENTRY_FILL_MODEL", "resting"),
|
||||
liquidity_max_volume_pct=float(
|
||||
@@ -705,8 +712,9 @@ class TradingExecutionService:
|
||||
if price is not None:
|
||||
try:
|
||||
px = float(price)
|
||||
if px > 0 and not (1.0 <= px <= 20.0):
|
||||
return f"price {px:.2f} outside $1-20 band"
|
||||
lo, hi = self.settings.price_band_min, self.settings.price_band_max
|
||||
if px > 0 and not (lo <= px <= hi):
|
||||
return f"price {px:.2f} outside ${lo:g}-{hi:g} band"
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
if self.settings.min_day_volume > 0 and cum_volume is not None:
|
||||
|
||||
Reference in new issue
Block a user