Spaces:
Sleeping
Sleeping
Update veto.py
Browse files
veto.py
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
from typing import Dict, Any, Tuple
|
| 2 |
|
| 3 |
from config import (
|
|
@@ -5,45 +18,102 @@ from config import (
|
|
| 5 |
VETO_VOL_RATIO_MAX,
|
| 6 |
VETO_STRUCTURE_MIN,
|
| 7 |
VETO_CLIMAX,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
)
|
| 9 |
|
|
|
|
|
|
|
|
|
|
| 10 |
|
| 11 |
def apply_veto(
|
| 12 |
regime_data: Dict[str, Any],
|
| 13 |
volume_data: Dict[str, Any],
|
| 14 |
structure_score: float,
|
|
|
|
| 15 |
) -> Tuple[bool, str]:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
reasons = []
|
| 17 |
|
| 18 |
volume_score = volume_data.get("volume_score", 0.0)
|
| 19 |
vol_ratio = regime_data.get("vol_ratio", 1.0)
|
| 20 |
-
climax = volume_data.get("climax", False)
|
| 21 |
-
weak = volume_data.get("weak", False)
|
| 22 |
trend = regime_data.get("trend", "ranging")
|
|
|
|
| 23 |
vol_expanding = regime_data.get("vol_expanding", False)
|
| 24 |
-
|
| 25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
|
|
|
|
| 27 |
if volume_score < VETO_VOLUME_MIN:
|
| 28 |
-
reasons.append(f"
|
| 29 |
|
| 30 |
if weak:
|
| 31 |
-
reasons.append(f"
|
| 32 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
if vol_ratio > VETO_VOL_RATIO_MAX:
|
| 34 |
-
reasons.append(f"EXTREME_VOLATILITY (ratio={vol_ratio:.2f}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
|
|
|
|
| 36 |
if structure_score < VETO_STRUCTURE_MIN:
|
| 37 |
-
reasons.append(f"
|
| 38 |
|
| 39 |
-
if
|
| 40 |
-
reasons.append("
|
|
|
|
|
|
|
|
|
|
| 41 |
|
| 42 |
-
|
| 43 |
-
|
|
|
|
|
|
|
|
|
|
| 44 |
|
| 45 |
-
|
| 46 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
|
| 48 |
if reasons:
|
| 49 |
return True, " | ".join(reasons)
|
|
@@ -51,6 +121,4 @@ def apply_veto(
|
|
| 51 |
|
| 52 |
|
| 53 |
def veto_summary(vetoed: bool, reason: str) -> str:
|
| 54 |
-
if vetoed
|
| 55 |
-
return f"VETOED β {reason}"
|
| 56 |
-
return "APPROVED"
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
veto.py β Hard filters. Any single veto cancels the setup entirely.
|
| 3 |
+
|
| 4 |
+
Key fixes vs prior version:
|
| 5 |
+
- Absorption is now a hard veto (was missing)
|
| 6 |
+
- Climax volume is a hard veto (was only a score penalty)
|
| 7 |
+
- Failed breakout in last N bars = hard veto
|
| 8 |
+
- Price too extended from mean = hard veto (separate per direction)
|
| 9 |
+
- Vol ratio threshold lowered to 2.2 (fires more reliably)
|
| 10 |
+
- No volatility compression = veto (require setups to emerge from bases)
|
| 11 |
+
- Regime confidence below minimum = veto (new structural gate)
|
| 12 |
+
"""
|
| 13 |
+
|
| 14 |
from typing import Dict, Any, Tuple
|
| 15 |
|
| 16 |
from config import (
|
|
|
|
| 18 |
VETO_VOL_RATIO_MAX,
|
| 19 |
VETO_STRUCTURE_MIN,
|
| 20 |
VETO_CLIMAX,
|
| 21 |
+
VETO_ABSORPTION,
|
| 22 |
+
VETO_EXTENDED_PRICE,
|
| 23 |
+
VETO_NO_COMPRESSION,
|
| 24 |
+
REGIME_CONFIDENCE_MIN,
|
| 25 |
+
VOL_COMPRESSION_LOOKBACK,
|
| 26 |
)
|
| 27 |
|
| 28 |
+
_FAILED_BREAKOUT_LOOKBACK = 5 # bars to look back for recent fake signals
|
| 29 |
+
_CONSEC_FAILED_MAX = 2 # veto if N or more recent fakes
|
| 30 |
+
|
| 31 |
|
| 32 |
def apply_veto(
|
| 33 |
regime_data: Dict[str, Any],
|
| 34 |
volume_data: Dict[str, Any],
|
| 35 |
structure_score: float,
|
| 36 |
+
direction: int = 1, # +1 = evaluating long, -1 = evaluating short
|
| 37 |
) -> Tuple[bool, str]:
|
| 38 |
+
"""
|
| 39 |
+
Returns (vetoed: bool, reason: str).
|
| 40 |
+
direction controls which extension check applies.
|
| 41 |
+
"""
|
| 42 |
reasons = []
|
| 43 |
|
| 44 |
volume_score = volume_data.get("volume_score", 0.0)
|
| 45 |
vol_ratio = regime_data.get("vol_ratio", 1.0)
|
|
|
|
|
|
|
| 46 |
trend = regime_data.get("trend", "ranging")
|
| 47 |
+
vol_compressed = regime_data.get("vol_compressed", False)
|
| 48 |
vol_expanding = regime_data.get("vol_expanding", False)
|
| 49 |
+
regime_confidence = regime_data.get("regime_confidence", 0.0)
|
| 50 |
+
price_extended_long = regime_data.get("price_extended_long", False)
|
| 51 |
+
price_extended_short = regime_data.get("price_extended_short", False)
|
| 52 |
+
adx = regime_data.get("adx", 0.0)
|
| 53 |
+
|
| 54 |
+
spike = volume_data.get("spike", False)
|
| 55 |
+
climax = volume_data.get("climax", False)
|
| 56 |
+
absorption = volume_data.get("absorption", False)
|
| 57 |
+
failed_breakout = volume_data.get("failed_breakout", False)
|
| 58 |
+
recent_failed = volume_data.get("recent_failed_count", 0)
|
| 59 |
+
weak = volume_data.get("weak", False)
|
| 60 |
|
| 61 |
+
# ββ VOLUME GATES βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 62 |
if volume_score < VETO_VOLUME_MIN:
|
| 63 |
+
reasons.append(f"WEAK_VOLUME_SCORE ({volume_score:.2f} < {VETO_VOLUME_MIN})")
|
| 64 |
|
| 65 |
if weak:
|
| 66 |
+
reasons.append(f"VOLUME_BELOW_MA (ratio={volume_data.get('vol_ratio', 0):.2f})")
|
| 67 |
|
| 68 |
+
if VETO_CLIMAX and climax:
|
| 69 |
+
reasons.append("CLIMAX_VOLUME β potential exhaustion, not entry")
|
| 70 |
+
|
| 71 |
+
if VETO_ABSORPTION and absorption:
|
| 72 |
+
reasons.append("ABSORPTION_DETECTED β institutional supply at resistance")
|
| 73 |
+
|
| 74 |
+
# ββ BREAKOUT INTEGRITY ββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 75 |
+
if failed_breakout:
|
| 76 |
+
reasons.append("FAILED_BREAKOUT β most recent breakout reversed")
|
| 77 |
+
|
| 78 |
+
if recent_failed >= _CONSEC_FAILED_MAX:
|
| 79 |
+
reasons.append(f"REPEATED_FAKE_BREAKOUTS ({recent_failed} in last 10 bars)")
|
| 80 |
+
|
| 81 |
+
# ββ VOLATILITY GATES ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 82 |
if vol_ratio > VETO_VOL_RATIO_MAX:
|
| 83 |
+
reasons.append(f"EXTREME_VOLATILITY (ratio={vol_ratio:.2f} > {VETO_VOL_RATIO_MAX})")
|
| 84 |
+
|
| 85 |
+
if VETO_NO_COMPRESSION and not vol_compressed and not vol_expanding:
|
| 86 |
+
# Allow through if currently expanding β expansion from base is fine
|
| 87 |
+
# Only veto if vol is neither compressed nor just broke out
|
| 88 |
+
if vol_ratio < 0.9 or vol_ratio > 1.6:
|
| 89 |
+
reasons.append(
|
| 90 |
+
f"NO_VOLATILITY_BASE (ratio={vol_ratio:.2f}, compressed={vol_compressed})"
|
| 91 |
+
)
|
| 92 |
|
| 93 |
+
# ββ STRUCTURE GATE ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 94 |
if structure_score < VETO_STRUCTURE_MIN:
|
| 95 |
+
reasons.append(f"WEAK_STRUCTURE ({structure_score:.2f} < {VETO_STRUCTURE_MIN})")
|
| 96 |
|
| 97 |
+
if trend == "bearish" and direction == 1:
|
| 98 |
+
reasons.append("BEARISH_TREND β long entry contra-trend")
|
| 99 |
+
|
| 100 |
+
if trend == "bullish" and direction == -1:
|
| 101 |
+
reasons.append("BULLISH_TREND β short entry contra-trend")
|
| 102 |
|
| 103 |
+
# ββ REGIME CONFIDENCE GATE ββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 104 |
+
if regime_confidence < REGIME_CONFIDENCE_MIN:
|
| 105 |
+
reasons.append(
|
| 106 |
+
f"LOW_REGIME_CONFIDENCE ({regime_confidence:.2f} < {REGIME_CONFIDENCE_MIN})"
|
| 107 |
+
)
|
| 108 |
|
| 109 |
+
# ββ PRICE EXTENSION GATE βββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 110 |
+
if VETO_EXTENDED_PRICE:
|
| 111 |
+
if direction == 1 and price_extended_long:
|
| 112 |
+
dist = regime_data.get("dist_atr", 0.0)
|
| 113 |
+
reasons.append(f"PRICE_EXTENDED_LONG ({dist:.2f} ATR above mean)")
|
| 114 |
+
if direction == -1 and price_extended_short:
|
| 115 |
+
dist = regime_data.get("dist_atr", 0.0)
|
| 116 |
+
reasons.append(f"PRICE_EXTENDED_SHORT ({dist:.2f} ATR below mean)")
|
| 117 |
|
| 118 |
if reasons:
|
| 119 |
return True, " | ".join(reasons)
|
|
|
|
| 121 |
|
| 122 |
|
| 123 |
def veto_summary(vetoed: bool, reason: str) -> str:
|
| 124 |
+
return f"VETOED β {reason}" if vetoed else "APPROVED"
|
|
|
|
|
|