Spaces:
Sleeping
Sleeping
Update src/streamlit_app.py
Browse files- src/streamlit_app.py +41 -34
src/streamlit_app.py
CHANGED
|
@@ -258,45 +258,52 @@ if st.button("Run Honest Backtest"):
|
|
| 258 |
|
| 259 |
# --- STRATEGY LOGIC (Same as before) ---
|
| 260 |
|
| 261 |
-
test_df['Signal'] = np.where(test_df['EMA_Short'] > test_df['EMA_Long'], 1,
|
| 262 |
-
|
| 263 |
avg_train_vol = train_df['Volatility'].mean()
|
| 264 |
test_df['Risk_Ratio'] = test_df['Predicted_Vol'] / avg_train_vol
|
| 265 |
|
| 266 |
-
|
| 267 |
-
test_df['Target_Position'] = 0.0
|
| 268 |
-
|
| 269 |
-
# --- CONDITIONS ---
|
| 270 |
-
# BULLISH Conditions
|
| 271 |
-
cond_bull_trend = (test_df['Signal'] == 1)
|
| 272 |
-
cond_safe_regime = (test_df['Regime'] == 0) # HMM says "Calm"
|
| 273 |
-
cond_low_risk = (test_df['Risk_Ratio'] < risk_threshold) # SVR says "Low Vol"
|
| 274 |
-
|
| 275 |
-
# BEARISH Conditions (The "Destined to go down" setup)
|
| 276 |
-
cond_bear_trend = (test_df['Signal'] == -1)
|
| 277 |
-
cond_crash_regime = (test_df['Regime'] == (n_states - 1)) # HMM says "Crash"
|
| 278 |
-
# We use Risk Ratio > 1.0 to confirm high volatility momentum (Panic Selling)
|
| 279 |
-
cond_high_vol_momentum = (test_df['Risk_Ratio'] > 1.0)
|
| 280 |
-
|
| 281 |
-
# --- LOGIC EXECUTION ---
|
| 282 |
|
| 283 |
-
#
|
| 284 |
-
|
| 285 |
-
test_df
|
| 286 |
-
|
| 287 |
-
test_df.loc[cond_bull_trend & (~cond_safe_regime | ~cond_low_risk), 'Target_Position'] = 1.0
|
| 288 |
|
| 289 |
-
#
|
| 290 |
-
|
| 291 |
-
|
|
|
|
| 292 |
|
| 293 |
-
#
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
# 3. Calculate Returns (Shift Logic)
|
| 297 |
-
# We apply the position chosen 'Today' to 'Tomorrow's' returns
|
| 298 |
-
test_df['Final_Position'] = test_df['Target_Position'].shift(1)
|
| 299 |
test_df['Simple_Returns'] = test_df['Close'].pct_change()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 300 |
|
| 301 |
-
|
| 302 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 258 |
|
| 259 |
# --- STRATEGY LOGIC (Same as before) ---
|
| 260 |
|
| 261 |
+
test_df['Signal'] = np.where(test_df['EMA_Short'] > test_df['EMA_Long'], 1, 0)
|
|
|
|
| 262 |
avg_train_vol = train_df['Volatility'].mean()
|
| 263 |
test_df['Risk_Ratio'] = test_df['Predicted_Vol'] / avg_train_vol
|
| 264 |
|
| 265 |
+
test_df['Position_Size'] = 1.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 266 |
|
| 267 |
+
# Logic
|
| 268 |
+
cond_safe = (test_df['Regime'] == 0)
|
| 269 |
+
cond_low_risk = (test_df['Risk_Ratio'] < risk_threshold)
|
| 270 |
+
cond_crash = (test_df['Regime'] == (n_states - 1))
|
|
|
|
| 271 |
|
| 272 |
+
# Boost
|
| 273 |
+
test_df['Position_Size'] = np.where(cond_safe & cond_low_risk, leverage_mult, test_df['Position_Size'])
|
| 274 |
+
# Cut
|
| 275 |
+
test_df['Position_Size'] = np.where(cond_crash, 0.0, test_df['Position_Size'])
|
| 276 |
|
| 277 |
+
# Calculate Returns
|
| 278 |
+
test_df['Final_Position'] = (test_df['Signal'] * test_df['Position_Size']).shift(1)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 279 |
test_df['Simple_Returns'] = test_df['Close'].pct_change()
|
| 280 |
+
test_df['Strategy_Returns'] = test_df['Final_Position'] * test_df['Simple_Returns']
|
| 281 |
+
|
| 282 |
+
# Metrics & Plots
|
| 283 |
+
test_df['Strategy_Value'] = (1 + test_df['Strategy_Returns'].fillna(0)).cumprod()
|
| 284 |
+
test_df['Buy_Hold_Value'] = (1 + test_df['Simple_Returns'].fillna(0)).cumprod()
|
| 285 |
+
test_df.dropna(inplace=True)
|
| 286 |
+
|
| 287 |
+
metrics_df = calculate_metrics(test_df)
|
| 288 |
+
st.subheader("Performance vs Benchmark")
|
| 289 |
+
st.table(metrics_df)
|
| 290 |
+
|
| 291 |
+
st.subheader("Equity Curve")
|
| 292 |
+
fig = go.Figure()
|
| 293 |
+
fig.add_trace(go.Scatter(x=test_df.index, y=test_df['Buy_Hold_Value'], name='Buy & Hold', line=dict(color='gray', dash='dot')))
|
| 294 |
+
fig.add_trace(go.Scatter(x=test_df.index, y=test_df['Strategy_Value'], name='Smart Leverage', line=dict(color='#00CC96', width=2)))
|
| 295 |
+
st.plotly_chart(fig, use_container_width=True)
|
| 296 |
+
|
| 297 |
+
st.subheader("Leverage Deployment")
|
| 298 |
+
fig_lev = go.Figure()
|
| 299 |
+
fig_lev.add_trace(go.Scatter(x=test_df.index, y=test_df['Position_Size'], mode='lines', fill='tozeroy', name='Lev', line=dict(color='#636EFA')))
|
| 300 |
+
st.plotly_chart(fig_lev, use_container_width=True)
|
| 301 |
|
| 302 |
+
trade_log = generate_trade_log(test_df)
|
| 303 |
+
st.subheader("📝 Trade Log")
|
| 304 |
+
if not trade_log.empty:
|
| 305 |
+
display_log = trade_log.copy()
|
| 306 |
+
display_log['Trade PnL'] = display_log['Trade PnL'].map('{:.2%}'.format)
|
| 307 |
+
st.dataframe(display_log, use_container_width=True)
|
| 308 |
+
else:
|
| 309 |
+
st.write("No trades generated.")
|