Spaces:
Running
Running
P2SAMAPA commited on
Update strategy.py
Browse files- 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
|
|
|
|
| 51 |
|
| 52 |
-
Stop-loss: if 2-day cumulative return β€ stop_loss_pct
|
| 53 |
-
|
| 54 |
-
|
| 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
|
| 119 |
-
|
| 120 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 121 |
else:
|
| 122 |
# Remain in CASH β earn daily Rf, no fee
|
| 123 |
net_ret = daily_rf
|
| 124 |
trade_signal = "CASH"
|
| 125 |
else:
|
| 126 |
-
#
|
| 127 |
-
if
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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)
|