Require aligned action for qualified setups
This commit is contained in:
@@ -54,6 +54,7 @@ from app.services.outcome_service import (
|
||||
from app.services.price_service import query_ohlcv
|
||||
from app.services.qualification import (
|
||||
HIGH_CONVICTION_ACTIONS,
|
||||
_action_direction,
|
||||
best_target_probability,
|
||||
setup_qualifies,
|
||||
)
|
||||
@@ -917,7 +918,10 @@ def _gate_ablation(candidates: list[dict], activation: dict, threshold: float) -
|
||||
return (c["confidence"] or 0.0) >= min_conf
|
||||
|
||||
def neutral_ok(c: dict) -> bool:
|
||||
return not exclude_neutral or (c.get("action") or "NEUTRAL") != "NEUTRAL"
|
||||
if not exclude_neutral:
|
||||
return True
|
||||
action_direction = _action_direction(c.get("action"))
|
||||
return action_direction != "neutral" and action_direction == c["direction"]
|
||||
|
||||
def tighteners_ok(c: dict) -> bool:
|
||||
if require_high and (c.get("action") or "") not in HIGH_CONVICTION_ACTIONS:
|
||||
|
||||
@@ -17,6 +17,16 @@ from typing import Any
|
||||
HIGH_CONVICTION_ACTIONS = {"LONG_HIGH", "SHORT_HIGH"}
|
||||
|
||||
|
||||
def _action_direction(action: str | None) -> str:
|
||||
if not action or action == "NEUTRAL":
|
||||
return "neutral"
|
||||
if action.startswith("LONG"):
|
||||
return "long"
|
||||
if action.startswith("SHORT"):
|
||||
return "short"
|
||||
return "neutral"
|
||||
|
||||
|
||||
def best_target_probability(setup: Any) -> float:
|
||||
"""Highest probability among a setup's targets, 0 if none."""
|
||||
targets = getattr(setup, "targets", None) or []
|
||||
@@ -78,12 +88,14 @@ def setup_qualifies(setup: Any, config: dict) -> bool:
|
||||
momentum_percentile = getattr(setup, "momentum_percentile", None)
|
||||
if momentum_percentile is not None and momentum_percentile < min_pct:
|
||||
return False
|
||||
# A NEUTRAL recommendation means the engine found no clear directional setup —
|
||||
# not an actionable signal, so by default it doesn't qualify (and can't be a
|
||||
# top pick). ``exclude_neutral`` defaults on; turn it off to also count
|
||||
# no-clear-direction residual momentum leaders.
|
||||
# A setup is actionable only when the live ticker action points in the same
|
||||
# direction. NEUTRAL means no clear signal; an opposite action means the
|
||||
# setup is counter-bias. ``exclude_neutral`` defaults on; callers that omit
|
||||
# it keep legacy floor-only behavior.
|
||||
if config.get("exclude_neutral"):
|
||||
if (setup.recommended_action or "NEUTRAL") == "NEUTRAL":
|
||||
action_direction = _action_direction(getattr(setup, "recommended_action", None))
|
||||
setup_direction = (getattr(setup, "direction", "long") or "long").lower()
|
||||
if action_direction == "neutral" or action_direction != setup_direction:
|
||||
return False
|
||||
if config.get("require_high_conviction"):
|
||||
if (setup.recommended_action or "") not in HIGH_CONVICTION_ACTIONS:
|
||||
|
||||
Reference in New Issue
Block a user