Daveabc12 commited on
Commit
28ce6cb
·
verified ·
1 Parent(s): dc3da35

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +48 -25
  2. config.json +2 -0
app.py CHANGED
@@ -60,6 +60,7 @@ def load_settings():
60
  "smart_exit_atr_multiplier": 3.0,
61
  "intelligent_tsl_pct": 0.2,
62
  "norm_lookback_years": 1,
 
63
  "benchmark_rank": 99,
64
  "use_ma_floor_filter": True,
65
  "catcher_stop_pct": 0.03,
@@ -587,7 +588,8 @@ def run_backtest(data, params,
587
  intelligent_tsl_pct=1.0,
588
  analysis_start_date=None,
589
  analysis_end_date=None,
590
- benchmark_lookback_years=None):
 
591
 
592
  df = data.copy()
593
  required_cols = ['Close']
@@ -642,7 +644,7 @@ def run_backtest(data, params,
642
  else:
643
  df['RSI'], df['Volatility_p'], df['ADX'], df['ATR'] = np.nan, np.nan, np.nan, np.nan
644
 
645
- # --- [NEW] MFI & SUPERTREND CALCULATIONS (Fixed) ---
646
  if len(df) >= 14:
647
  # 1. MFI Calculation
648
  try:
@@ -750,36 +752,47 @@ def run_backtest(data, params,
750
  best_markov_setup=markov_setup
751
  )
752
 
753
- if long_score_95_percentile is None:
754
- # If user specified a lookback (e.g. 1 year), use Rolling Window
755
- if benchmark_lookback_years is not None and benchmark_lookback_years > 0:
756
- window_days = int(benchmark_lookback_years * 252) # Approx trading days
757
- # Calculate Rolling Percentile. 'min_periods' ensures we get data early in the chart.
758
- long_95 = raw_long_score.rolling(window=window_days, min_periods=50).quantile(benchmark_rank).fillna(method='bfill')
759
- else:
760
- # Fallback to old "Global" method if no lookback specified
761
  long_scores_gt_zero = raw_long_score[raw_long_score > 0]
762
  val = long_scores_gt_zero.quantile(benchmark_rank) if not long_scores_gt_zero.empty else 1.0
763
  long_95 = pd.Series(val, index=df.index)
764
- else:
765
- long_95 = pd.Series(long_score_95_percentile, index=df.index)
766
-
767
- if short_score_95_percentile is None:
768
- # Same logic for Short
769
- if benchmark_lookback_years is not None and benchmark_lookback_years > 0:
770
- window_days = int(benchmark_lookback_years * 252)
771
- short_95 = raw_short_score.rolling(window=window_days, min_periods=50).quantile(benchmark_rank).fillna(method='bfill')
772
  else:
 
 
 
773
  short_scores_gt_zero = raw_short_score[raw_short_score > 0]
774
  val = short_scores_gt_zero.quantile(benchmark_rank) if not short_scores_gt_zero.empty else 1.0
775
- short_95 = pd.Series(val, index=df.index)
 
 
 
 
 
776
  else:
777
- short_95 = pd.Series(short_score_95_percentile, index=df.index)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
778
 
779
  # Safety: Ensure we don't divide by zero
780
  long_95 = long_95.replace(0, 1.0)
781
  short_95 = short_95.replace(0, 1.0)
782
-
 
783
  df['long_confidence_score'] = (raw_long_score / long_95 * 100).clip(0, 100).fillna(0.0)
784
  df['short_confidence_score'] = (raw_short_score / short_95 * 100).clip(0, 100).fillna(0.0)
785
 
@@ -3728,6 +3741,13 @@ def main():
3728
  "• **1-2 Years:** Adaptive. Judges trades only against recent market volatility (good for changing regimes)."
3729
  "• **Default is set to 1 year as we are usually most interested in the recent year.")
3730
  )
 
 
 
 
 
 
 
3731
 
3732
  # 2. Grade Benchmark - HIDDEN IN PRODUCTION MODE
3733
  if not production_mode:
@@ -4332,6 +4352,7 @@ def main():
4332
  "smart_exit_atr_multiplier": st.session_state.get('smart_exit_atr_multiplier', 3.0),
4333
  "intelligent_tsl_pct": st.session_state.intelligent_tsl_pct / 100.0,
4334
  "norm_lookback_years": norm_lookback_years,
 
4335
  "benchmark_rank": benchmark_percentile_setting,
4336
  "use_vol": st.session_state.use_vol, "vol_w": st.session_state.vol_w,
4337
  "use_trend": st.session_state.use_trend, "trend_w": st.session_state.trend_w,
@@ -4592,9 +4613,10 @@ def main():
4592
  smart_exit_atr_multiplier=smart_atr_m,
4593
  intelligent_tsl_pct=intelligent_tsl,
4594
  benchmark_rank=benchmark_percentile_setting / 100.0,
4595
- benchmark_lookback_years=norm_lookback_years, # <--- NEW PASSED VALUE
4596
  analysis_start_date=user_start_date,
4597
- analysis_end_date=end_date
 
 
4598
  )
4599
  st.session_state.single_ticker_results = {"long_pnl": long_pnl, "short_pnl": short_pnl, "avg_long_trade": avg_long_trade, "avg_short_trade": avg_short_trade, "results_df": results_df, "trades": trades}
4600
  st.session_state.open_trades_df = pd.DataFrame(open_trades) if open_trades else pd.DataFrame()
@@ -4659,9 +4681,10 @@ def main():
4659
  smart_trailing_stop_pct=smart_trailing_stop, smart_exit_atr_period=smart_atr_p,
4660
  smart_exit_atr_multiplier=smart_atr_m, intelligent_tsl_pct=intelligent_tsl,
4661
  benchmark_rank=benchmark_percentile_setting / 100.0,
4662
- benchmark_lookback_years=norm_lookback_years, # <--- NEW PASSED VALUE
4663
  analysis_start_date=user_start_date,
4664
- analysis_end_date=end_date
 
 
4665
  )
4666
 
4667
  if abs(long_pnl) > PROFIT_THRESHOLD or abs(short_pnl) > PROFIT_THRESHOLD or \
 
60
  "smart_exit_atr_multiplier": 3.0,
61
  "intelligent_tsl_pct": 0.2,
62
  "norm_lookback_years": 1,
63
+ 'use_rolling_benchmark': False,
64
  "benchmark_rank": 99,
65
  "use_ma_floor_filter": True,
66
  "catcher_stop_pct": 0.03,
 
588
  intelligent_tsl_pct=1.0,
589
  analysis_start_date=None,
590
  analysis_end_date=None,
591
+ benchmark_lookback_years=None,
592
+ use_rolling_benchmark=True):
593
 
594
  df = data.copy()
595
  required_cols = ['Close']
 
644
  else:
645
  df['RSI'], df['Volatility_p'], df['ADX'], df['ATR'] = np.nan, np.nan, np.nan, np.nan
646
 
647
+ # --- [NEW] MFI & SUPERTREND CALCULATIONS (Fixed) ---
648
  if len(df) >= 14:
649
  # 1. MFI Calculation
650
  try:
 
752
  best_markov_setup=markov_setup
753
  )
754
 
755
+ # 1. GLOBAL (STATIC) MODE - "The Crystal Ball"
756
+ # Used for "Elite Filtering" of open trades.
757
+ if not use_rolling_benchmark:
758
+ if long_score_95_percentile is None:
 
 
 
 
759
  long_scores_gt_zero = raw_long_score[raw_long_score > 0]
760
  val = long_scores_gt_zero.quantile(benchmark_rank) if not long_scores_gt_zero.empty else 1.0
761
  long_95 = pd.Series(val, index=df.index)
 
 
 
 
 
 
 
 
762
  else:
763
+ long_95 = pd.Series(long_score_95_percentile, index=df.index)
764
+
765
+ if short_score_95_percentile is None:
766
  short_scores_gt_zero = raw_short_score[raw_short_score > 0]
767
  val = short_scores_gt_zero.quantile(benchmark_rank) if not short_scores_gt_zero.empty else 1.0
768
+ short_95 = pd.Series(val, index=df.index)
769
+ else:
770
+ short_95 = pd.Series(short_score_95_percentile, index=df.index)
771
+
772
+ # 2. ADAPTIVE (ROLLING) MODE - "Real World"
773
+ # Used for realistic historical simulation.
774
  else:
775
+ if long_score_95_percentile is None:
776
+ # Default to 1 year if lookback is missing
777
+ years = benchmark_lookback_years if (benchmark_lookback_years is not None and benchmark_lookback_years > 0) else 1
778
+ window_days = int(years * 252)
779
+ # Calculate Rolling Percentile
780
+ long_95 = raw_long_score.rolling(window=window_days, min_periods=50).quantile(benchmark_rank).fillna(method='bfill')
781
+ else:
782
+ long_95 = pd.Series(long_score_95_percentile, index=df.index)
783
+
784
+ if short_score_95_percentile is None:
785
+ years = benchmark_lookback_years if (benchmark_lookback_years is not None and benchmark_lookback_years > 0) else 1
786
+ window_days = int(years * 252)
787
+ short_95 = raw_short_score.rolling(window=window_days, min_periods=50).quantile(benchmark_rank).fillna(method='bfill')
788
+ else:
789
+ short_95 = pd.Series(short_score_95_percentile, index=df.index)
790
 
791
  # Safety: Ensure we don't divide by zero
792
  long_95 = long_95.replace(0, 1.0)
793
  short_95 = short_95.replace(0, 1.0)
794
+
795
+ # Final Calculation (Removing 'if' check to avoid Series Ambiguity Error)
796
  df['long_confidence_score'] = (raw_long_score / long_95 * 100).clip(0, 100).fillna(0.0)
797
  df['short_confidence_score'] = (raw_short_score / short_95 * 100).clip(0, 100).fillna(0.0)
798
 
 
3741
  "• **1-2 Years:** Adaptive. Judges trades only against recent market volatility (good for changing regimes)."
3742
  "• **Default is set to 1 year as we are usually most interested in the recent year.")
3743
  )
3744
+ use_rolling_benchmark = st.sidebar.checkbox(
3745
+ "Use Rolling Benchmark (Adaptive)",
3746
+ value=st.session_state.get('use_rolling_benchmark', True),
3747
+ key='widget_use_rolling_benchmark',
3748
+ on_change=update_state,
3749
+ help="CHECKED: Adaptive Mode. Benchmarks adapt to recent history. Realistic backtest.\nUNCHECKED: Global Mode (Crystal Ball). Uses ALL history to set one strict benchmark. Good for filtering Open Trades."
3750
+ )
3751
 
3752
  # 2. Grade Benchmark - HIDDEN IN PRODUCTION MODE
3753
  if not production_mode:
 
4352
  "smart_exit_atr_multiplier": st.session_state.get('smart_exit_atr_multiplier', 3.0),
4353
  "intelligent_tsl_pct": st.session_state.intelligent_tsl_pct / 100.0,
4354
  "norm_lookback_years": norm_lookback_years,
4355
+ "use_rolling_benchmark": use_rolling_benchmark,
4356
  "benchmark_rank": benchmark_percentile_setting,
4357
  "use_vol": st.session_state.use_vol, "vol_w": st.session_state.vol_w,
4358
  "use_trend": st.session_state.use_trend, "trend_w": st.session_state.trend_w,
 
4613
  smart_exit_atr_multiplier=smart_atr_m,
4614
  intelligent_tsl_pct=intelligent_tsl,
4615
  benchmark_rank=benchmark_percentile_setting / 100.0,
 
4616
  analysis_start_date=user_start_date,
4617
+ analysis_end_date=end_date,
4618
+ benchmark_lookback_years=norm_lookback_years,
4619
+ use_rolling_benchmark=use_rolling_benchmark
4620
  )
4621
  st.session_state.single_ticker_results = {"long_pnl": long_pnl, "short_pnl": short_pnl, "avg_long_trade": avg_long_trade, "avg_short_trade": avg_short_trade, "results_df": results_df, "trades": trades}
4622
  st.session_state.open_trades_df = pd.DataFrame(open_trades) if open_trades else pd.DataFrame()
 
4681
  smart_trailing_stop_pct=smart_trailing_stop, smart_exit_atr_period=smart_atr_p,
4682
  smart_exit_atr_multiplier=smart_atr_m, intelligent_tsl_pct=intelligent_tsl,
4683
  benchmark_rank=benchmark_percentile_setting / 100.0,
 
4684
  analysis_start_date=user_start_date,
4685
+ analysis_end_date=end_date,
4686
+ benchmark_lookback_years=norm_lookback_years,
4687
+ use_rolling_benchmark=use_rolling_benchmark
4688
  )
4689
 
4690
  if abs(long_pnl) > PROFIT_THRESHOLD or abs(short_pnl) > PROFIT_THRESHOLD or \
config.json CHANGED
@@ -17,12 +17,14 @@
17
  "primary_driver": "Bollinger Bands",
18
  "exit_logic_type": "Intelligent (ADX/MACD/ATR)",
19
  "use_ma_floor_filter": true,
 
20
  "exit_confidence_threshold": 40,
21
  "smart_trailing_stop_pct": 5.0,
22
  "smart_exit_atr_period": 14,
23
  "smart_exit_atr_multiplier": 3.0,
24
  "intelligent_tsl_pct": 0.2,
25
  "norm_lookback_years": 1,
 
26
  "benchmark_rank": 99,
27
  "use_vol": false,
28
  "vol_w": 0.5,
 
17
  "primary_driver": "Bollinger Bands",
18
  "exit_logic_type": "Intelligent (ADX/MACD/ATR)",
19
  "use_ma_floor_filter": true,
20
+ "catcher_stop_pct": 0.03,
21
  "exit_confidence_threshold": 40,
22
  "smart_trailing_stop_pct": 5.0,
23
  "smart_exit_atr_period": 14,
24
  "smart_exit_atr_multiplier": 3.0,
25
  "intelligent_tsl_pct": 0.2,
26
  "norm_lookback_years": 1,
27
+ "use_rolling_benchmark": false,
28
  "benchmark_rank": 99,
29
  "use_vol": false,
30
  "vol_w": 0.5,