GoshawkVortexAI commited on
Commit
33a396a
·
verified ·
1 Parent(s): cb3d9ec

Update regime.py

Browse files
Files changed (1) hide show
  1. regime.py +76 -57
regime.py CHANGED
@@ -1,14 +1,19 @@
1
- import pandas as pd
2
- import numpy as np
3
  from typing import Dict, Any
4
- from config import ATR_PERIOD, STRUCTURE_LOOKBACK, VOLATILITY_EXPANSION_MULTIPLIER
 
 
 
 
 
 
 
 
 
 
5
 
6
 
7
  def compute_atr(df: pd.DataFrame, period: int = ATR_PERIOD) -> pd.Series:
8
- high = df["high"]
9
- low = df["low"]
10
- close = df["close"]
11
- prev_close = close.shift(1)
12
  tr = pd.concat(
13
  [
14
  high - low,
@@ -17,67 +22,79 @@ def compute_atr(df: pd.DataFrame, period: int = ATR_PERIOD) -> pd.Series:
17
  ],
18
  axis=1,
19
  ).max(axis=1)
20
- atr = tr.ewm(span=period, adjust=False).mean()
21
- return atr
22
-
23
-
24
- def detect_structure(df: pd.DataFrame, lookback: int = STRUCTURE_LOOKBACK) -> pd.Series:
25
- highs = df["high"].rolling(lookback).max()
26
- lows = df["low"].rolling(lookback).min()
27
- prev_highs = highs.shift(lookback // 2)
28
- prev_lows = lows.shift(lookback // 2)
29
- hh = highs > prev_highs
30
- hl = lows > prev_lows
31
- lh = highs < prev_highs
32
- ll = lows < prev_lows
 
33
  structure = pd.Series(0, index=df.index)
34
  structure[hh & hl] = 1
35
  structure[lh & ll] = -1
36
  return structure
37
 
38
 
39
- def compute_volatility_regime(df: pd.DataFrame, period: int = ATR_PERIOD) -> pd.Series:
40
  atr = compute_atr(df, period)
41
- atr_ma = atr.rolling(period * 2).mean()
42
- ratio = atr / atr_ma.replace(0, np.nan)
43
- return ratio
 
 
 
 
 
 
 
 
 
 
44
 
45
 
46
  def detect_regime(df: pd.DataFrame) -> Dict[str, Any]:
47
- atr = compute_atr(df, ATR_PERIOD)
48
- structure = detect_structure(df, STRUCTURE_LOOKBACK)
49
- vol_ratio = compute_volatility_regime(df, ATR_PERIOD)
50
-
51
- last_atr = atr.iloc[-1]
52
- last_structure = structure.iloc[-1]
53
- last_vol_ratio = vol_ratio.iloc[-1]
54
-
55
- recent_structure = structure.iloc[-5:]
56
- bullish_count = (recent_structure == 1).sum()
57
- bearish_count = (recent_structure == -1).sum()
58
-
59
- if bullish_count >= 3:
60
- trend = "bullish"
61
- elif bearish_count >= 3:
62
- trend = "bearish"
63
- else:
64
- trend = "ranging"
65
 
66
- volatility_expanding = last_vol_ratio > VOLATILITY_EXPANSION_MULTIPLIER
 
 
 
67
 
68
- if trend == "bullish" and not volatility_expanding:
69
  regime_score = 1.0
70
- elif trend == "bullish" and volatility_expanding:
71
- regime_score = 0.6
 
 
72
  elif trend == "ranging":
73
- regime_score = 0.3
74
- elif trend == "bearish" and not volatility_expanding:
75
- regime_score = 0.2
76
  else:
77
- regime_score = 0.1
 
 
 
 
 
78
 
79
- close = df["close"].iloc[-1]
80
- atr_pct = last_atr / close if close > 0 else 0.0
 
81
 
82
  return {
83
  "atr": last_atr,
@@ -85,9 +102,11 @@ def detect_regime(df: pd.DataFrame) -> Dict[str, Any]:
85
  "structure": last_structure,
86
  "trend": trend,
87
  "vol_ratio": last_vol_ratio,
88
- "volatility_expanding": volatility_expanding,
89
- "regime_score": regime_score,
90
- "atr_series": atr,
91
- "structure_series": structure,
92
- "vol_ratio_series": vol_ratio,
 
 
93
  }
 
 
 
1
  from typing import Dict, Any
2
+
3
+ import numpy as np
4
+ import pandas as pd
5
+
6
+ from config import (
7
+ ATR_PERIOD,
8
+ STRUCTURE_LOOKBACK,
9
+ STRUCTURE_CONFIRM_BARS,
10
+ VOLATILITY_EXPANSION_MULT,
11
+ VOLATILITY_CONTRACTION_MULT,
12
+ )
13
 
14
 
15
  def compute_atr(df: pd.DataFrame, period: int = ATR_PERIOD) -> pd.Series:
16
+ high, low, prev_close = df["high"], df["low"], df["close"].shift(1)
 
 
 
17
  tr = pd.concat(
18
  [
19
  high - low,
 
22
  ],
23
  axis=1,
24
  ).max(axis=1)
25
+ return tr.ewm(span=period, adjust=False).mean()
26
+
27
+
28
+ def compute_structure(df: pd.DataFrame, lookback: int = STRUCTURE_LOOKBACK) -> pd.Series:
29
+ roll_high = df["high"].rolling(lookback).max()
30
+ roll_low = df["low"].rolling(lookback).min()
31
+ prev_roll_high = roll_high.shift(lookback // 2)
32
+ prev_roll_low = roll_low.shift(lookback // 2)
33
+
34
+ hh = roll_high > prev_roll_high
35
+ hl = roll_low > prev_roll_low
36
+ lh = roll_high < prev_roll_high
37
+ ll = roll_low < prev_roll_low
38
+
39
  structure = pd.Series(0, index=df.index)
40
  structure[hh & hl] = 1
41
  structure[lh & ll] = -1
42
  return structure
43
 
44
 
45
+ def compute_vol_ratio(df: pd.DataFrame, period: int = ATR_PERIOD) -> pd.Series:
46
  atr = compute_atr(df, period)
47
+ atr_ma = atr.rolling(period * 2).mean().replace(0, np.nan)
48
+ return atr / atr_ma
49
+
50
+
51
+ def classify_trend(structure_series: pd.Series, lookback: int = STRUCTURE_CONFIRM_BARS) -> str:
52
+ recent = structure_series.iloc[-lookback:]
53
+ bullish = (recent == 1).sum()
54
+ bearish = (recent == -1).sum()
55
+ if bullish > bearish and bullish >= max(1, lookback // 2):
56
+ return "bullish"
57
+ if bearish > bullish and bearish >= max(1, lookback // 2):
58
+ return "bearish"
59
+ return "ranging"
60
 
61
 
62
  def detect_regime(df: pd.DataFrame) -> Dict[str, Any]:
63
+ atr_series = compute_atr(df, ATR_PERIOD)
64
+ structure_series = compute_structure(df, STRUCTURE_LOOKBACK)
65
+ vol_ratio_series = compute_vol_ratio(df, ATR_PERIOD)
66
+
67
+ last_atr = float(atr_series.iloc[-1])
68
+ last_structure = int(structure_series.iloc[-1])
69
+ last_vol_ratio = float(vol_ratio_series.iloc[-1]) if not np.isnan(vol_ratio_series.iloc[-1]) else 1.0
70
+ last_close = float(df["close"].iloc[-1])
 
 
 
 
 
 
 
 
 
 
71
 
72
+ trend = classify_trend(structure_series, STRUCTURE_CONFIRM_BARS)
73
+ vol_expanding = last_vol_ratio > VOLATILITY_EXPANSION_MULT
74
+ vol_contracting = last_vol_ratio < VOLATILITY_CONTRACTION_MULT
75
+ atr_pct = last_atr / last_close if last_close > 0 else 0.0
76
 
77
+ if trend == "bullish" and not vol_expanding:
78
  regime_score = 1.0
79
+ elif trend == "bullish" and vol_expanding:
80
+ regime_score = 0.55
81
+ elif trend == "ranging" and not vol_expanding and not vol_contracting:
82
+ regime_score = 0.35
83
  elif trend == "ranging":
84
+ regime_score = 0.25
85
+ elif trend == "bearish" and not vol_expanding:
86
+ regime_score = 0.15
87
  else:
88
+ regime_score = 0.05
89
+
90
+ if last_structure == 1:
91
+ regime_score = min(1.0, regime_score + 0.1)
92
+ elif last_structure == -1:
93
+ regime_score = max(0.0, regime_score - 0.1)
94
 
95
+ atr_ma_20 = atr_series.rolling(20).mean().iloc[-1]
96
+ atr_ma_50 = atr_series.rolling(50).mean().iloc[-1] if len(df) >= 50 else atr_ma_20
97
+ atr_trend = "rising" if atr_ma_20 > atr_ma_50 else "falling"
98
 
99
  return {
100
  "atr": last_atr,
 
102
  "structure": last_structure,
103
  "trend": trend,
104
  "vol_ratio": last_vol_ratio,
105
+ "vol_expanding": vol_expanding,
106
+ "vol_contracting": vol_contracting,
107
+ "atr_trend": atr_trend,
108
+ "regime_score": round(float(np.clip(regime_score, 0.0, 1.0)), 4),
109
+ "atr_series": atr_series,
110
+ "structure_series": structure_series,
111
+ "vol_ratio_series": vol_ratio_series,
112
  }