GoshawkVortexAI commited on
Commit
cfe8ecb
Β·
verified Β·
1 Parent(s): 13a6601

Update veto.py

Browse files
Files changed (1) hide show
  1. veto.py +85 -17
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
- vol_contracting = regime_data.get("vol_contracting", False)
25
- atr_trend = regime_data.get("atr_trend", "falling")
 
 
 
 
 
 
 
 
 
26
 
 
27
  if volume_score < VETO_VOLUME_MIN:
28
- reasons.append(f"WEAK_VOLUME (score={volume_score:.2f}, threshold={VETO_VOLUME_MIN})")
29
 
30
  if weak:
31
- reasons.append(f"VOL_BELOW_MA (ratio={volume_data.get('vol_ratio', 0):.2f})")
32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  if vol_ratio > VETO_VOL_RATIO_MAX:
34
- reasons.append(f"EXTREME_VOLATILITY (ratio={vol_ratio:.2f}, max={VETO_VOL_RATIO_MAX})")
 
 
 
 
 
 
 
 
35
 
 
36
  if structure_score < VETO_STRUCTURE_MIN:
37
- reasons.append(f"CONFLICTING_STRUCTURE (score={structure_score:.2f}, threshold={VETO_STRUCTURE_MIN})")
38
 
39
- if VETO_CLIMAX and climax:
40
- reasons.append("CLIMAX_VOLUME (potential exhaustion)")
 
 
 
41
 
42
- if trend == "bearish" and vol_expanding:
43
- reasons.append("BEARISH_TREND_EXPANDING_VOL (high-risk combination)")
 
 
 
44
 
45
- if vol_contracting and volume_score < 0.4:
46
- reasons.append(f"VOL_CONTRACTING_WEAK_FLOW (vol_ratio={vol_ratio:.2f})")
 
 
 
 
 
 
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"