JayLacoma commited on
Commit
d793675
·
verified ·
1 Parent(s): b183b82

Update feature_engineering.py

Browse files
Files changed (1) hide show
  1. feature_engineering.py +89 -82
feature_engineering.py CHANGED
@@ -42,9 +42,10 @@ class IntegratedTheoryFeatures:
42
  def dalio_forces(self):
43
  """Ray Dalio's Five Forces Framework"""
44
  # 1. Debt Cycle
45
- yield_curve = self.df.get('DGS10', 0) - self.df.get('DGS2', 0)
46
- inflation_mom = self.df.get('CPIAUCSL', pd.Series(0)).pct_change(12) * 100
47
- hy_spread = self.df.get('BAMLH0A0HYM2', pd.Series(0)) / 100
 
48
  self.features['dalio_debt_cycle'] = (
49
  yield_curve * 0.3 +
50
  inflation_mom * 0.4 +
@@ -53,15 +54,15 @@ class IntegratedTheoryFeatures:
53
 
54
  # 2. Internal Conflict (Inequality & Social Stress)
55
  consumer_weakness = safe_divide(
56
- self.df.get('Consumer_Discretionary', 0),
57
- self.df.get('Consumer_Staples', 1)
58
  ).pct_change(63) * -1
59
 
60
- unemployment_stress = self.df.get('UNRATE', pd.Series(0)).diff() * 2
61
 
62
  small_large_gap = safe_divide(
63
- self.df.get('Small_Cap_Value', 0),
64
- self.df.get('SP500', 1)
65
  ).pct_change(63) * -1
66
 
67
  self.features['dalio_internal_conflict'] = (
@@ -71,14 +72,17 @@ class IntegratedTheoryFeatures:
71
  )
72
 
73
  # 3. External Conflict (Geopolitical)
74
- defense_momentum = self.df.get('Defense_Stocks', pd.Series(0)).pct_change(21)
75
 
76
- sp_corr = self.df.get('SP500', pd.Series(0)).pct_change(5) < -0.05
77
- dollar_weak = self.df.get('DXY', pd.Series(0)).pct_change(5) < 0
78
- dollar_anomaly = (sp_corr & dollar_weak).astype(float)
79
 
80
- taiwan = self.df.get('Taiwan', pd.Series(0))
81
- china = self.df.get('China', pd.Series(0))
 
 
 
 
82
  china_taiwan_tension = (taiwan.pct_change(21) - china.pct_change(21)).fillna(0)
83
 
84
  self.features['dalio_external_conflict'] = (
@@ -88,18 +92,18 @@ class IntegratedTheoryFeatures:
88
  )
89
 
90
  # 4. Nature Force (Climate & Resources)
91
- water_stress = self.df.get('Water', pd.Series(0)).pct_change(63)
92
- ag_vol = self.df.get('Agricultural', pd.Series(0)).pct_change().rolling(63).std() * 100
93
  self.features['dalio_nature_force'] = water_stress * 0.6 + ag_vol * 0.4
94
 
95
  # 5. Technology Force
96
  tech_outperform = safe_divide(
97
- self.df.get('Technology', 0),
98
- self.df.get('SP500', 1)
99
  ).pct_change(21)
100
 
101
- cloud_mom = self.df.get('Cloud_Computing', pd.Series(0)).pct_change(63)
102
- ai_mom = self.df.get('Robotics_AI', pd.Series(0)).pct_change(63)
103
 
104
  self.features['dalio_tech_force'] = (
105
  tech_outperform * 0.4 +
@@ -122,35 +126,36 @@ class IntegratedTheoryFeatures:
122
  """Betsey Stevenson's Economic Inequality Framework"""
123
  # Wealth Concentration
124
  asset_rich = (
125
- self.df.get('Gold', 0) +
126
- self.df.get('Real_Estate', 0) +
127
- self.df.get('Growth_Stocks', 0)
128
  ) / 3
129
 
130
  middle_class = (
131
- self.df.get('Consumer_Staples', 0) +
132
- self.df.get('Regional_Banks', 0) +
133
- self.df.get('Small_Cap_Value', 0)
134
  ) / 3
135
 
136
  wealth_flow = asset_rich.pct_change(63) - middle_class.pct_change(63)
137
 
138
  # Consumer Spending Gap
139
- luxury = self.df.get('Retail_Luxury', pd.Series(0)).pct_change(21)
140
  mass_market = (
141
- (self.df.get('Restaurants', 0) + self.df.get('Retail', 0)) / 2
 
142
  ).pct_change(21)
143
  cons_gap = luxury - mass_market
144
 
145
  # Credit Access Gap
146
  quality = (
147
- self.df.get('Investment_Grade_Spread', 0) +
148
- self.df.get('Preferred_Stock', 0)
149
  ) / 2
150
  junk = (
151
- self.df.get('HYG', 0) +
152
- self.df.get('JNK', 0) +
153
- self.df.get('Emerging_Market_Debt', 0)
154
  ) / 3
155
  credit_gap = quality.pct_change(63) - junk.pct_change(63)
156
 
@@ -162,20 +167,20 @@ class IntegratedTheoryFeatures:
162
  def thiel_monopoly(self):
163
  """Peter Thiel's Zero to One / Monopoly Framework"""
164
  # Cash Flow Moats
165
- tech = self.df.get('Technology', 0)
166
- finance = self.df.get('Financials', 1)
167
  cash_moat = tech.pct_change(63) - finance.pct_change(63)
168
 
169
  # Network Effects
170
  network = (
171
- self.df.get('Cloud_Computing', 0) * 0.4 +
172
- self.df.get('Communication_Services', 0) * 0.3 +
173
- self.df.get('Fintech', 0) * 0.3
174
  ).pct_change(63)
175
 
176
  # Defensibility (Low volatility + semiconductor dominance)
177
- tech_vol = self.df.get('Technology', pd.Series(1)).pct_change().rolling(63).std()
178
- chip = self.df.get('Semiconductors', pd.Series(0)).pct_change(63)
179
  defensibility = safe_divide(1, tech_vol) * 0.01 + chip * 0.5
180
 
181
  self.features['thiel_monopoly_norm'] = normalize(
@@ -186,34 +191,35 @@ class IntegratedTheoryFeatures:
186
  def gundlach_reckoning(self):
187
  """Jeffrey Gundlach's Debt Reckoning Framework"""
188
  # Yield Anomalies
189
- fed = self.df.get('DGS3MO', pd.Series(0))
190
- teny = self.df.get('DGS10', pd.Series(0))
191
- yield_anomaly = (
192
- ((fed.diff() < -0.05) & (teny.diff() > 0)).astype(float) +
193
- (teny - fed)
194
- )
195
 
196
  # Flight to Safety Shift (Gold vs Bonds)
197
- gold_ret = self.df.get('Gold', pd.Series(0)).pct_change(21)
198
- tlt_ret = self.df.get('US_Treasuries_Long', pd.Series(1)).pct_change(21)
199
  flight_shift = safe_divide(gold_ret, tlt_ret)
200
 
201
  # Capital Flow Reversal
202
- dxy_weak = self.df.get('DXY', pd.Series(0)).pct_change(21) * -1
203
- em = (self.df.get('Emerging_Markets', 0) + self.df.get('Europe', 0)) / 2
 
204
  em_out = em.pct_change(21)
205
- sp_ret = self.df.get('SP500', pd.Series(0)).pct_change(21)
206
  capital_reversal = dxy_weak * 0.5 + (em_out - sp_ret) * 0.5
207
  self.features['gundlach_capital_reversal'] = capital_reversal
208
 
209
  # Private Credit Risk
210
  reg_banks = safe_divide(
211
- self.df.get('Regional_Banks', 0),
212
- self.df.get('Financials', 1)
213
  ).pct_change(21)
214
 
215
- mortgage_reit = self.df.get('Mortgage_REITs', pd.Series(0)).pct_change(21)
216
- real_estate_vol = self.df.get('Real_Estate', pd.Series(1)).pct_change().rolling(21).std() * 100
217
 
218
  private_credit_risk = (
219
  reg_banks * -0.4 +
@@ -235,22 +241,23 @@ class IntegratedTheoryFeatures:
235
  def geopolitical_indicators(self):
236
  """Enhanced Geopolitical Risk Indicators"""
237
  # Middle East Risk
238
- oil_vol = self.df.get('Oil', pd.Series(1)).pct_change().rolling(3).std() * 100
239
- def_spike = self.df.get('Defense_Stocks', pd.Series(0)).pct_change(5)
240
- gold_haven = self.df.get('Gold_Safe_Haven', pd.Series(0)).pct_change(5)
241
  me_risk = oil_vol * 0.4 + def_spike * 0.3 + gold_haven * 0.3
242
 
243
  # Europe Risk
244
- gas_vol = self.df.get('NaturalGas', pd.Series(1)).pct_change().rolling(5).std() * 100
245
- eu_decline = self.df.get('Europe', pd.Series(0)).pct_change(21) * -1
246
- chf_str = self.df.get('Swiss_Franc', pd.Series(0)).pct_change(21) * -1
247
  eu_risk = gas_vol * 0.5 + eu_decline * 0.3 + chf_str * 0.2
248
 
249
  # Asia-Pacific Risk
250
- chip_stress = self.df.get('Semiconductors', pd.Series(1)).pct_change().rolling(21).std() * 100
251
- tw_kr = (self.df.get('Taiwan', 0) + self.df.get('South_Korea', 0)) / 2
252
- china_div = tw_kr.pct_change(21) - self.df.get('China', pd.Series(0)).pct_change(21)
253
- rare_earth = self.df.get('Rare_Earth', pd.Series(0)).pct_change(21)
 
254
  asia_risk = chip_stress * 0.4 + china_div * 0.3 + rare_earth * 0.3
255
 
256
  self.features['geopolitical_risk_norm'] = normalize(
@@ -272,19 +279,21 @@ class IntegratedTheoryFeatures:
272
  )
273
 
274
  # Stagflation Probability
275
- inflation_high = (df['CPIAUCSL'].pct_change(12) * 100 > 2.5).astype(float)
 
276
  unemp_rising = (df['UNRATE'].diff() > 0).astype(float)
 
277
  f['prob_stagflation'] = np.clip(
278
- (inflation_high & unemp_rising) * 0.3 +
279
  safe_zscore(f['dalio_external_conflict']) * 0.03 +
280
- safe_zscore(f.get('gundlach_capital_reversal', pd.Series(0))) * 0.02 +
281
  f['stevenson_inequality_norm'] * 0.2,
282
  0, 1
283
  )
284
 
285
  # Tech Boom Probability
286
- china_tech = df.get('China_Tech', pd.Series(0)).pct_change(63)
287
- tech = df.get('Technology', pd.Series(0)).pct_change(63)
288
  china_tech_lag = (china_tech < tech).astype(float)
289
 
290
  f['prob_tech_boom'] = np.clip(
@@ -301,15 +310,13 @@ class IntegratedTheoryFeatures:
301
  f = self.features
302
 
303
  # Binary regime flags
304
- f['debt_unsustainable'] = (
305
- (f['gundlach_reckoning_norm'] > 0.5) &
306
- (f['prob_credit_collapse'] > 0.3)
307
- ).astype(int)
308
 
309
- f['inequality_trap'] = (
310
- (f['stevenson_inequality_norm'] > 0.6) &
311
- (f['prob_stagflation'] > 0.4)
312
- ).astype(int)
313
 
314
  f['tech_monopoly'] = (f['thiel_monopoly_norm'] > 0.6).astype(int)
315
 
@@ -317,10 +324,10 @@ class IntegratedTheoryFeatures:
317
 
318
  # Regime classification
319
  conditions = [
320
- f['debt_unsustainable'],
321
- f['tech_monopoly'],
322
- f['inequality_trap'],
323
- f['geopolitical_shock']
324
  ]
325
  choices = ['CRISIS', 'TECH_MONOPOLY', 'INEQUALITY_TRAP', 'GEOPOLITICAL_SHOCK']
326
  f['regime'] = np.select(conditions, choices, default='TRANSITION')
 
42
  def dalio_forces(self):
43
  """Ray Dalio's Five Forces Framework"""
44
  # 1. Debt Cycle
45
+ yield_curve = (self.df.get('DGS10', pd.Series(0, index=self.df.index)) -
46
+ self.df.get('DGS2', pd.Series(0, index=self.df.index)))
47
+ inflation_mom = self.df.get('CPIAUCSL', pd.Series(0, index=self.df.index)).pct_change(12) * 100
48
+ hy_spread = self.df.get('BAMLH0A0HYM2', pd.Series(0, index=self.df.index)) / 100
49
  self.features['dalio_debt_cycle'] = (
50
  yield_curve * 0.3 +
51
  inflation_mom * 0.4 +
 
54
 
55
  # 2. Internal Conflict (Inequality & Social Stress)
56
  consumer_weakness = safe_divide(
57
+ self.df.get('Consumer_Discretionary', pd.Series(0, index=self.df.index)),
58
+ self.df.get('Consumer_Staples', pd.Series(1, index=self.df.index))
59
  ).pct_change(63) * -1
60
 
61
+ unemployment_stress = self.df.get('UNRATE', pd.Series(0, index=self.df.index)).diff() * 2
62
 
63
  small_large_gap = safe_divide(
64
+ self.df.get('Small_Cap_Value', pd.Series(0, index=self.df.index)),
65
+ self.df.get('SP500', pd.Series(1, index=self.df.index))
66
  ).pct_change(63) * -1
67
 
68
  self.features['dalio_internal_conflict'] = (
 
72
  )
73
 
74
  # 3. External Conflict (Geopolitical)
75
+ defense_momentum = self.df.get('Defense_Stocks', pd.Series(0, index=self.df.index)).pct_change(21)
76
 
77
+ sp_ret = self.df.get('SP500', pd.Series(0, index=self.df.index)).pct_change(5)
78
+ dxy_ret = self.df.get('DXY', pd.Series(0, index=self.df.index)).pct_change(5)
 
79
 
80
+ sp_corr = (sp_ret < -0.05).astype(float)
81
+ dollar_weak = (dxy_ret < 0).astype(float)
82
+ dollar_anomaly = sp_corr * dollar_weak
83
+
84
+ taiwan = self.df.get('Taiwan', pd.Series(0, index=self.df.index))
85
+ china = self.df.get('China', pd.Series(0, index=self.df.index))
86
  china_taiwan_tension = (taiwan.pct_change(21) - china.pct_change(21)).fillna(0)
87
 
88
  self.features['dalio_external_conflict'] = (
 
92
  )
93
 
94
  # 4. Nature Force (Climate & Resources)
95
+ water_stress = self.df.get('Water', pd.Series(0, index=self.df.index)).pct_change(63)
96
+ ag_vol = self.df.get('Agricultural', pd.Series(0, index=self.df.index)).pct_change().rolling(63).std() * 100
97
  self.features['dalio_nature_force'] = water_stress * 0.6 + ag_vol * 0.4
98
 
99
  # 5. Technology Force
100
  tech_outperform = safe_divide(
101
+ self.df.get('Technology', pd.Series(0, index=self.df.index)),
102
+ self.df.get('SP500', pd.Series(1, index=self.df.index))
103
  ).pct_change(21)
104
 
105
+ cloud_mom = self.df.get('Cloud_Computing', pd.Series(0, index=self.df.index)).pct_change(63)
106
+ ai_mom = self.df.get('Robotics_AI', pd.Series(0, index=self.df.index)).pct_change(63)
107
 
108
  self.features['dalio_tech_force'] = (
109
  tech_outperform * 0.4 +
 
126
  """Betsey Stevenson's Economic Inequality Framework"""
127
  # Wealth Concentration
128
  asset_rich = (
129
+ self.df.get('Gold', pd.Series(0, index=self.df.index)) +
130
+ self.df.get('Real_Estate', pd.Series(0, index=self.df.index)) +
131
+ self.df.get('Growth_Stocks', pd.Series(0, index=self.df.index))
132
  ) / 3
133
 
134
  middle_class = (
135
+ self.df.get('Consumer_Staples', pd.Series(0, index=self.df.index)) +
136
+ self.df.get('Regional_Banks', pd.Series(0, index=self.df.index)) +
137
+ self.df.get('Small_Cap_Value', pd.Series(0, index=self.df.index))
138
  ) / 3
139
 
140
  wealth_flow = asset_rich.pct_change(63) - middle_class.pct_change(63)
141
 
142
  # Consumer Spending Gap
143
+ luxury = self.df.get('Retail_Luxury', pd.Series(0, index=self.df.index)).pct_change(21)
144
  mass_market = (
145
+ (self.df.get('Restaurants', pd.Series(0, index=self.df.index)) +
146
+ self.df.get('Retail', pd.Series(0, index=self.df.index))) / 2
147
  ).pct_change(21)
148
  cons_gap = luxury - mass_market
149
 
150
  # Credit Access Gap
151
  quality = (
152
+ self.df.get('Investment_Grade_Spread', pd.Series(0, index=self.df.index)) +
153
+ self.df.get('Preferred_Stock', pd.Series(0, index=self.df.index))
154
  ) / 2
155
  junk = (
156
+ self.df.get('HYG', pd.Series(0, index=self.df.index)) +
157
+ self.df.get('JNK', pd.Series(0, index=self.df.index)) +
158
+ self.df.get('Emerging_Market_Debt', pd.Series(0, index=self.df.index))
159
  ) / 3
160
  credit_gap = quality.pct_change(63) - junk.pct_change(63)
161
 
 
167
  def thiel_monopoly(self):
168
  """Peter Thiel's Zero to One / Monopoly Framework"""
169
  # Cash Flow Moats
170
+ tech = self.df.get('Technology', pd.Series(0, index=self.df.index))
171
+ finance = self.df.get('Financials', pd.Series(1, index=self.df.index))
172
  cash_moat = tech.pct_change(63) - finance.pct_change(63)
173
 
174
  # Network Effects
175
  network = (
176
+ self.df.get('Cloud_Computing', pd.Series(0, index=self.df.index)) * 0.4 +
177
+ self.df.get('Communication_Services', pd.Series(0, index=self.df.index)) * 0.3 +
178
+ self.df.get('Fintech', pd.Series(0, index=self.df.index)) * 0.3
179
  ).pct_change(63)
180
 
181
  # Defensibility (Low volatility + semiconductor dominance)
182
+ tech_vol = self.df.get('Technology', pd.Series(1, index=self.df.index)).pct_change().rolling(63).std()
183
+ chip = self.df.get('Semiconductors', pd.Series(0, index=self.df.index)).pct_change(63)
184
  defensibility = safe_divide(1, tech_vol) * 0.01 + chip * 0.5
185
 
186
  self.features['thiel_monopoly_norm'] = normalize(
 
191
  def gundlach_reckoning(self):
192
  """Jeffrey Gundlach's Debt Reckoning Framework"""
193
  # Yield Anomalies
194
+ fed = self.df.get('DGS3MO', pd.Series(0, index=self.df.index))
195
+ teny = self.df.get('DGS10', pd.Series(0, index=self.df.index))
196
+
197
+ fed_drop = (fed.diff() < -0.05).astype(float)
198
+ teny_rise = (teny.diff() > 0).astype(float)
199
+ yield_anomaly = fed_drop * teny_rise + (teny - fed)
200
 
201
  # Flight to Safety Shift (Gold vs Bonds)
202
+ gold_ret = self.df.get('Gold', pd.Series(0, index=self.df.index)).pct_change(21)
203
+ tlt_ret = self.df.get('US_Treasuries_Long', pd.Series(1, index=self.df.index)).pct_change(21)
204
  flight_shift = safe_divide(gold_ret, tlt_ret)
205
 
206
  # Capital Flow Reversal
207
+ dxy_weak = self.df.get('DXY', pd.Series(0, index=self.df.index)).pct_change(21) * -1
208
+ em = (self.df.get('Emerging_Markets', pd.Series(0, index=self.df.index)) +
209
+ self.df.get('Europe', pd.Series(0, index=self.df.index))) / 2
210
  em_out = em.pct_change(21)
211
+ sp_ret = self.df.get('SP500', pd.Series(0, index=self.df.index)).pct_change(21)
212
  capital_reversal = dxy_weak * 0.5 + (em_out - sp_ret) * 0.5
213
  self.features['gundlach_capital_reversal'] = capital_reversal
214
 
215
  # Private Credit Risk
216
  reg_banks = safe_divide(
217
+ self.df.get('Regional_Banks', pd.Series(0, index=self.df.index)),
218
+ self.df.get('Financials', pd.Series(1, index=self.df.index))
219
  ).pct_change(21)
220
 
221
+ mortgage_reit = self.df.get('Mortgage_REITs', pd.Series(0, index=self.df.index)).pct_change(21)
222
+ real_estate_vol = self.df.get('Real_Estate', pd.Series(1, index=self.df.index)).pct_change().rolling(21).std() * 100
223
 
224
  private_credit_risk = (
225
  reg_banks * -0.4 +
 
241
  def geopolitical_indicators(self):
242
  """Enhanced Geopolitical Risk Indicators"""
243
  # Middle East Risk
244
+ oil_vol = self.df.get('Oil', pd.Series(1, index=self.df.index)).pct_change().rolling(3).std() * 100
245
+ def_spike = self.df.get('Defense_Stocks', pd.Series(0, index=self.df.index)).pct_change(5)
246
+ gold_haven = self.df.get('Gold_Safe_Haven', pd.Series(0, index=self.df.index)).pct_change(5)
247
  me_risk = oil_vol * 0.4 + def_spike * 0.3 + gold_haven * 0.3
248
 
249
  # Europe Risk
250
+ gas_vol = self.df.get('NaturalGas', pd.Series(1, index=self.df.index)).pct_change().rolling(5).std() * 100
251
+ eu_decline = self.df.get('Europe', pd.Series(0, index=self.df.index)).pct_change(21) * -1
252
+ chf_str = self.df.get('Swiss_Franc', pd.Series(0, index=self.df.index)).pct_change(21) * -1
253
  eu_risk = gas_vol * 0.5 + eu_decline * 0.3 + chf_str * 0.2
254
 
255
  # Asia-Pacific Risk
256
+ chip_stress = self.df.get('Semiconductors', pd.Series(1, index=self.df.index)).pct_change().rolling(21).std() * 100
257
+ tw_kr = (self.df.get('Taiwan', pd.Series(0, index=self.df.index)) +
258
+ self.df.get('South_Korea', pd.Series(0, index=self.df.index))) / 2
259
+ china_div = tw_kr.pct_change(21) - self.df.get('China', pd.Series(0, index=self.df.index)).pct_change(21)
260
+ rare_earth = self.df.get('Rare_Earth', pd.Series(0, index=self.df.index)).pct_change(21)
261
  asia_risk = chip_stress * 0.4 + china_div * 0.3 + rare_earth * 0.3
262
 
263
  self.features['geopolitical_risk_norm'] = normalize(
 
279
  )
280
 
281
  # Stagflation Probability
282
+ cpi_ret = df['CPIAUCSL'].pct_change(12) * 100
283
+ inflation_high = (cpi_ret > 2.5).astype(float)
284
  unemp_rising = (df['UNRATE'].diff() > 0).astype(float)
285
+
286
  f['prob_stagflation'] = np.clip(
287
+ inflation_high * unemp_rising * 0.3 +
288
  safe_zscore(f['dalio_external_conflict']) * 0.03 +
289
+ safe_zscore(f.get('gundlach_capital_reversal', pd.Series(0, index=f.index))) * 0.02 +
290
  f['stevenson_inequality_norm'] * 0.2,
291
  0, 1
292
  )
293
 
294
  # Tech Boom Probability
295
+ china_tech = df.get('China_Tech', pd.Series(0, index=df.index)).pct_change(63)
296
+ tech = df.get('Technology', pd.Series(0, index=df.index)).pct_change(63)
297
  china_tech_lag = (china_tech < tech).astype(float)
298
 
299
  f['prob_tech_boom'] = np.clip(
 
310
  f = self.features
311
 
312
  # Binary regime flags
313
+ gundlach_high = (f['gundlach_reckoning_norm'] > 0.5).astype(float)
314
+ credit_risk_high = (f['prob_credit_collapse'] > 0.3).astype(float)
315
+ f['debt_unsustainable'] = (gundlach_high * credit_risk_high).astype(int)
 
316
 
317
+ inequality_high = (f['stevenson_inequality_norm'] > 0.6).astype(float)
318
+ stag_high = (f['prob_stagflation'] > 0.4).astype(float)
319
+ f['inequality_trap'] = (inequality_high * stag_high).astype(int)
 
320
 
321
  f['tech_monopoly'] = (f['thiel_monopoly_norm'] > 0.6).astype(int)
322
 
 
324
 
325
  # Regime classification
326
  conditions = [
327
+ f['debt_unsustainable'] == 1,
328
+ f['tech_monopoly'] == 1,
329
+ f['inequality_trap'] == 1,
330
+ f['geopolitical_shock'] == 1
331
  ]
332
  choices = ['CRISIS', 'TECH_MONOPOLY', 'INEQUALITY_TRAP', 'GEOPOLITICAL_SHOCK']
333
  f['regime'] = np.select(conditions, choices, default='TRANSITION')