P2SAMAPA commited on
Commit
a9781e5
Β·
unverified Β·
1 Parent(s): 44613c7

Update strategy.py

Browse files
Files changed (1) hide show
  1. strategy.py +29 -27
strategy.py CHANGED
@@ -45,22 +45,15 @@ def compute_signal_conviction(raw_scores):
45
  def execute_strategy(preds, y_raw_test, test_dates, target_etfs, fee_bps,
46
  model_type="ensemble",
47
  stop_loss_pct=-0.12, z_reentry=1.0,
48
- sofr=0.045, all_proba=None):
49
  """
50
- Execute trading strategy with T+1 execution and trailing stop-loss.
 
51
 
52
- Stop-loss: if 2-day cumulative return ≀ stop_loss_pct, switch to CASH
53
- earning daily Rf (sofr/252). Re-enter ETF when model conviction
54
- Z-score > z_reentry threshold.
55
-
56
- Returns:
57
- strat_rets : Strategy returns (numpy array)
58
- audit_trail : List of dicts with trade details
59
- next_signal : Next trading day's ETF signal (str)
60
- next_trading_date : Next trading date (date)
61
- conviction_zscore : Z-score of the next signal vs all ETF scores (float)
62
- conviction_label : Human-readable conviction label (str)
63
- all_etf_scores : Raw model scores for all ETFs (numpy array) β€” for UI bar chart
64
  """
65
 
66
  # Filter to only trading days
@@ -115,27 +108,36 @@ def execute_strategy(preds, y_raw_test, test_dates, target_etfs, fee_bps,
115
  # Stay in CASH until conviction Z-score exceeds re-entry threshold
116
  if day_z >= z_reentry:
117
  stop_active = False
118
- # Re-enter: use model signal, apply fee
119
- net_ret = realized_ret - (fee_bps / 10000)
120
- trade_signal = signal_etf
 
 
 
 
121
  else:
122
  # Remain in CASH β€” earn daily Rf, no fee
123
  net_ret = daily_rf
124
  trade_signal = "CASH"
125
  else:
126
- # Check 2-day cumulative return for stop trigger
127
- if len(recent_rets) >= 2:
128
- cum_2d = (1 + recent_rets[-2]) * (1 + recent_rets[-1]) - 1
129
- if cum_2d <= stop_loss_pct:
130
- stop_active = True
131
- net_ret = daily_rf # switch to CASH immediately today
132
- trade_signal = "CASH"
 
 
 
 
 
 
 
 
133
  else:
134
  net_ret = realized_ret - (fee_bps / 10000)
135
  trade_signal = signal_etf
136
- else:
137
- net_ret = realized_ret - (fee_bps / 10000)
138
- trade_signal = signal_etf
139
 
140
  strat_rets.append(net_ret)
141
  recent_rets.append(net_ret)
 
45
  def execute_strategy(preds, y_raw_test, test_dates, target_etfs, fee_bps,
46
  model_type="ensemble",
47
  stop_loss_pct=-0.12, z_reentry=1.0,
48
+ sofr=0.045, all_proba=None, z_min_entry=0.5):
49
  """
50
+ Execute trading strategy with T+1 execution, trailing stop-loss,
51
+ and minimum conviction gate.
52
 
53
+ Stop-loss : if 2-day cumulative return ≀ stop_loss_pct β†’ CASH earning Rf
54
+ Re-entry : return to ETF when conviction Z-score β‰₯ z_reentry
55
+ Conviction gate: only enter ETF if conviction Z-score β‰₯ z_min_entry,
56
+ otherwise hold CASH (avoids low-confidence trades)
 
 
 
 
 
 
 
 
57
  """
58
 
59
  # Filter to only trading days
 
108
  # Stay in CASH until conviction Z-score exceeds re-entry threshold
109
  if day_z >= z_reentry:
110
  stop_active = False
111
+ # Re-enter only if conviction also meets minimum entry bar
112
+ if day_z >= z_min_entry:
113
+ net_ret = realized_ret - (fee_bps / 10000)
114
+ trade_signal = signal_etf
115
+ else:
116
+ net_ret = daily_rf
117
+ trade_signal = "CASH"
118
  else:
119
  # Remain in CASH β€” earn daily Rf, no fee
120
  net_ret = daily_rf
121
  trade_signal = "CASH"
122
  else:
123
+ # ── Conviction gate: only trade if model is decisive enough ──────
124
+ if day_z < z_min_entry:
125
+ net_ret = daily_rf
126
+ trade_signal = "CASH"
127
+ else:
128
+ # Check 2-day cumulative return for stop trigger
129
+ if len(recent_rets) >= 2:
130
+ cum_2d = (1 + recent_rets[-2]) * (1 + recent_rets[-1]) - 1
131
+ if cum_2d <= stop_loss_pct:
132
+ stop_active = True
133
+ net_ret = daily_rf # switch to CASH immediately today
134
+ trade_signal = "CASH"
135
+ else:
136
+ net_ret = realized_ret - (fee_bps / 10000)
137
+ trade_signal = signal_etf
138
  else:
139
  net_ret = realized_ret - (fee_bps / 10000)
140
  trade_signal = signal_etf
 
 
 
141
 
142
  strat_rets.append(net_ret)
143
  recent_rets.append(net_ret)