Spaces:
Running
Running
Updated the chart
Browse files
app.py
CHANGED
|
@@ -581,16 +581,9 @@ def main():
|
|
| 581 |
st.session_state.first_run = True
|
| 582 |
st.session_state.widget_defaults = load_settings()
|
| 583 |
st.session_state.veto_setup = load_veto_setup()
|
| 584 |
-
st.session_state.summary_df = None
|
| 585 |
-
st.session_state.single_ticker_results = None
|
| 586 |
-
st.session_state.confidence_results_df = None
|
| 587 |
-
st.session_state.open_trades_df = None
|
| 588 |
-
st.session_state.best_params = None
|
| 589 |
-
|
| 590 |
st.title("π Stock Backtesting Sandbox")
|
| 591 |
st.success(f"Good morning! Today is {date.today().strftime('%A, %d %B %Y')}.")
|
| 592 |
main_content_placeholder = st.empty()
|
| 593 |
-
|
| 594 |
if 'master_df' not in st.session_state:
|
| 595 |
with main_content_placeholder.container():
|
| 596 |
master_df, load_message = load_all_data('csv_data')
|
|
@@ -605,7 +598,6 @@ def main():
|
|
| 605 |
st.download_button("β¬οΈ Download Outlier Report", report_df.to_csv(index=False).encode('utf-8'), "outlier_report.csv", "text/csv")
|
| 606 |
st.session_state.master_df = master_df
|
| 607 |
st.session_state.ticker_list = sorted([col for col in master_df.columns if '_volume' not in str(col).lower()])
|
| 608 |
-
|
| 609 |
master_df = st.session_state.master_df
|
| 610 |
ticker_list = st.session_state.ticker_list
|
| 611 |
defaults = st.session_state.widget_defaults
|
|
@@ -617,11 +609,7 @@ def main():
|
|
| 617 |
st.sidebar.date_input("Start Date", master_df.index.min().date(), key='start_date')
|
| 618 |
st.sidebar.date_input("End Date", master_df.index.max().date(), key='end_date')
|
| 619 |
st.markdown("""<style>div[data-testid="stSidebar"] button[kind="primary"] { background-color: #4CAF50; color: white; border-color: #4CAF50;}</style>""", unsafe_allow_html=True)
|
| 620 |
-
|
| 621 |
-
if st.sidebar.button("π Run Analysis", type="primary", key="run_analysis_button"):
|
| 622 |
-
st.session_state.confidence_results_df = None
|
| 623 |
-
st.session_state.best_params = None
|
| 624 |
-
|
| 625 |
st.sidebar.markdown("---")
|
| 626 |
|
| 627 |
st.sidebar.header("2. Confidence Score Factors (for Main Signal)")
|
|
@@ -630,7 +618,7 @@ def main():
|
|
| 630 |
st.sidebar.toggle("Use Volatility", value=True, key='use_vol')
|
| 631 |
st.sidebar.number_input("Volatility Weight", 0.1, 5.0, 1.0, 0.1, key='vol_w', disabled=not st.session_state.get('use_vol', True))
|
| 632 |
st.sidebar.toggle("Use Trend (200d MA)", value=True, key='use_trend')
|
| 633 |
-
st.sidebar.number_input("Trend Weight", 0.1, 5.0, 1.
|
| 634 |
st.sidebar.toggle("Use Volume Spike", value=True, key='use_volume')
|
| 635 |
st.sidebar.number_input("Volume Weight", 0.1, 5.0, 1.0, 0.1, key='volume_w', disabled=not st.session_state.get('use_volume', True))
|
| 636 |
st.sidebar.slider("Minimum Confidence Threshold (%)", 0, 100, defaults.get("confidence_threshold", 50), 5, key='confidence_slider')
|
|
@@ -643,6 +631,7 @@ def main():
|
|
| 643 |
st.sidebar.subheader("Long Trade Logic"); st.sidebar.slider("Entry Threshold (%)", 0.0, 10.0, defaults.get("long_entry_threshold_pct", 0.0) * 100, 0.1, key='long_entry'); st.sidebar.slider("Exit MA Threshold (%)", 0.0, 10.0, defaults.get("long_exit_ma_threshold_pct", 0.0) * 100, 0.1, key='long_exit'); st.sidebar.slider("Stop Loss (%)", 0.0, 30.0, defaults.get("long_stop_loss_pct", 0.0) * 100, 0.5, key='long_sl'); st.sidebar.number_input("Delay Entry (days)", 0, 10, defaults.get("long_delay_days", 0), 1, key='long_delay')
|
| 644 |
st.sidebar.subheader("Short Trade Logic"); st.sidebar.slider("Entry Threshold (%)", 0.0, 10.0, defaults.get("short_entry_threshold_pct", 0.0) * 100, 0.1, key='short_entry'); st.sidebar.slider("Exit MA Threshold (%)", 0.0, 10.0, defaults.get("short_exit_ma_threshold_pct", 0.0) * 100, 0.1, key='short_exit'); st.sidebar.slider("Stop Loss (%)", 0.0, 30.0, defaults.get("short_stop_loss_pct", 0.0) * 100, 0.5, key='short_sl'); st.sidebar.number_input("Delay Entry (days)", 0, 10, defaults.get("short_delay_days", 0), 1, key='short_delay')
|
| 645 |
|
|
|
|
| 646 |
st.sidebar.markdown("---")
|
| 647 |
st.sidebar.header("4. Find Best Parameters")
|
| 648 |
with st.sidebar.expander("Set Optimisation Ranges"):
|
|
@@ -705,36 +694,24 @@ def main():
|
|
| 705 |
if st.sidebar.button("πΎ Save Settings as Default"):
|
| 706 |
save_settings({ "large_ma_period": st.session_state.ma_period, "bband_period": st.session_state.bb_period, "bband_std_dev": st.session_state.bb_std, "confidence_threshold": st.session_state.confidence_slider, "long_entry_threshold_pct": st.session_state.long_entry / 100, "long_exit_ma_threshold_pct": st.session_state.long_exit / 100, "long_stop_loss_pct": st.session_state.long_sl / 100, "long_delay_days": st.session_state.long_delay, "short_entry_threshold_pct": st.session_state.short_entry / 100, "short_exit_ma_threshold_pct": st.session_state.short_exit / 100, "short_stop_loss_pct": st.session_state.short_sl / 100, "short_delay_days": st.session_state.short_delay, })
|
| 707 |
|
| 708 |
-
|
| 709 |
-
# --- Trigger actions based on session state flags ---
|
| 710 |
-
if st.session_state.get('run_analysis_button'):
|
| 711 |
-
st.session_state.run_analysis_button = False
|
| 712 |
-
st.session_state.confidence_results_df = None
|
| 713 |
-
st.session_state.best_params = None
|
| 714 |
-
st.session_state.advisor_df = None
|
| 715 |
-
|
| 716 |
with main_content_placeholder.container():
|
| 717 |
veto_to_use = st.session_state.get('veto_setup')
|
| 718 |
if veto_to_use: st.info("Veto filter is active for this analysis.")
|
| 719 |
-
else: st.info("π‘ Tip: You can find and apply a 'Veto Filter' from section 5 in the sidebar.")
|
| 720 |
-
|
| 721 |
manual_params = {"large_ma_period": st.session_state.ma_period, "bband_period": st.session_state.bb_period, "bband_std_dev": st.session_state.bb_std, "confidence_threshold": st.session_state.confidence_slider, "long_entry_threshold_pct": st.session_state.long_entry / 100, "long_exit_ma_threshold_pct": st.session_state.long_exit / 100, "long_stop_loss_pct": st.session_state.long_sl / 100, "long_delay_days": st.session_state.long_delay, "short_entry_threshold_pct": st.session_state.short_entry / 100, "short_exit_ma_threshold_pct": st.session_state.short_exit / 100, "short_stop_loss_pct": st.session_state.short_sl / 100, "short_delay_days": st.session_state.short_delay, }
|
| 722 |
-
|
| 723 |
-
# --- FIX: Correct indentation for this block ---
|
| 724 |
if st.session_state.run_mode == "Analyse Single Ticker":
|
| 725 |
selected_ticker = st.session_state.get('ticker_select', ticker_list[0])
|
| 726 |
cols_to_use = [selected_ticker]
|
| 727 |
if f'{selected_ticker}_Volume' in master_df.columns: cols_to_use.append(f'{selected_ticker}_Volume')
|
| 728 |
data_for_backtest = master_df[cols_to_use].rename(columns={selected_ticker: 'Close', f'{selected_ticker}_Volume': 'Volume'})
|
| 729 |
ticker_data_series = data_for_backtest.loc[pd.Timestamp(st.session_state.start_date):pd.Timestamp(st.session_state.end_date)]
|
| 730 |
-
|
| 731 |
if not ticker_data_series.empty:
|
| 732 |
long_pnl, short_pnl, avg_long_trade, avg_short_trade, results_df, trades, open_trades = run_backtest(ticker_data_series, manual_params, st.session_state.use_rsi, st.session_state.use_vol, st.session_state.use_trend, st.session_state.use_volume, st.session_state.rsi_w, st.session_state.vol_w, st.session_state.trend_w, st.session_state.volume_w, veto_setup=veto_to_use)
|
| 733 |
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}
|
| 734 |
if open_trades: st.session_state.open_trades_df = pd.DataFrame(open_trades)
|
| 735 |
else: st.session_state.open_trades_df = pd.DataFrame()
|
| 736 |
else: st.warning("No data for this ticker in the selected date range.")
|
| 737 |
-
|
| 738 |
elif st.session_state.run_mode == "Analyse Full List":
|
| 739 |
summary_results, all_open_trades = [], []
|
| 740 |
progress_bar = st.progress(0, text="Starting analysis...")
|
|
@@ -758,12 +735,6 @@ def main():
|
|
| 758 |
else: st.warning("No trades found for any ticker with the current settings.")
|
| 759 |
if all_open_trades: st.session_state.open_trades_df = pd.DataFrame(all_open_trades)
|
| 760 |
else: st.session_state.open_trades_df = pd.DataFrame()
|
| 761 |
-
|
| 762 |
-
|
| 763 |
-
|
| 764 |
-
if st.session_state.get('run_advanced_advisor'):
|
| 765 |
-
generate_advisor_report(master_df, main_content_placeholder)
|
| 766 |
-
st.session_state.run_advanced_advisor = False
|
| 767 |
|
| 768 |
# --- Main Display Area ---
|
| 769 |
with main_content_placeholder.container():
|
|
|
|
| 581 |
st.session_state.first_run = True
|
| 582 |
st.session_state.widget_defaults = load_settings()
|
| 583 |
st.session_state.veto_setup = load_veto_setup()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 584 |
st.title("π Stock Backtesting Sandbox")
|
| 585 |
st.success(f"Good morning! Today is {date.today().strftime('%A, %d %B %Y')}.")
|
| 586 |
main_content_placeholder = st.empty()
|
|
|
|
| 587 |
if 'master_df' not in st.session_state:
|
| 588 |
with main_content_placeholder.container():
|
| 589 |
master_df, load_message = load_all_data('csv_data')
|
|
|
|
| 598 |
st.download_button("β¬οΈ Download Outlier Report", report_df.to_csv(index=False).encode('utf-8'), "outlier_report.csv", "text/csv")
|
| 599 |
st.session_state.master_df = master_df
|
| 600 |
st.session_state.ticker_list = sorted([col for col in master_df.columns if '_volume' not in str(col).lower()])
|
|
|
|
| 601 |
master_df = st.session_state.master_df
|
| 602 |
ticker_list = st.session_state.ticker_list
|
| 603 |
defaults = st.session_state.widget_defaults
|
|
|
|
| 609 |
st.sidebar.date_input("Start Date", master_df.index.min().date(), key='start_date')
|
| 610 |
st.sidebar.date_input("End Date", master_df.index.max().date(), key='end_date')
|
| 611 |
st.markdown("""<style>div[data-testid="stSidebar"] button[kind="primary"] { background-color: #4CAF50; color: white; border-color: #4CAF50;}</style>""", unsafe_allow_html=True)
|
| 612 |
+
st.sidebar.button("π Run Analysis", type="primary", key="run_analysis_button")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 613 |
st.sidebar.markdown("---")
|
| 614 |
|
| 615 |
st.sidebar.header("2. Confidence Score Factors (for Main Signal)")
|
|
|
|
| 618 |
st.sidebar.toggle("Use Volatility", value=True, key='use_vol')
|
| 619 |
st.sidebar.number_input("Volatility Weight", 0.1, 5.0, 1.0, 0.1, key='vol_w', disabled=not st.session_state.get('use_vol', True))
|
| 620 |
st.sidebar.toggle("Use Trend (200d MA)", value=True, key='use_trend')
|
| 621 |
+
st.sidebar.number_input("Trend Weight", 0.1, 5.0, 1.5, 0.1, key='trend_w', disabled=not st.session_state.get('use_trend', True))
|
| 622 |
st.sidebar.toggle("Use Volume Spike", value=True, key='use_volume')
|
| 623 |
st.sidebar.number_input("Volume Weight", 0.1, 5.0, 1.0, 0.1, key='volume_w', disabled=not st.session_state.get('use_volume', True))
|
| 624 |
st.sidebar.slider("Minimum Confidence Threshold (%)", 0, 100, defaults.get("confidence_threshold", 50), 5, key='confidence_slider')
|
|
|
|
| 631 |
st.sidebar.subheader("Long Trade Logic"); st.sidebar.slider("Entry Threshold (%)", 0.0, 10.0, defaults.get("long_entry_threshold_pct", 0.0) * 100, 0.1, key='long_entry'); st.sidebar.slider("Exit MA Threshold (%)", 0.0, 10.0, defaults.get("long_exit_ma_threshold_pct", 0.0) * 100, 0.1, key='long_exit'); st.sidebar.slider("Stop Loss (%)", 0.0, 30.0, defaults.get("long_stop_loss_pct", 0.0) * 100, 0.5, key='long_sl'); st.sidebar.number_input("Delay Entry (days)", 0, 10, defaults.get("long_delay_days", 0), 1, key='long_delay')
|
| 632 |
st.sidebar.subheader("Short Trade Logic"); st.sidebar.slider("Entry Threshold (%)", 0.0, 10.0, defaults.get("short_entry_threshold_pct", 0.0) * 100, 0.1, key='short_entry'); st.sidebar.slider("Exit MA Threshold (%)", 0.0, 10.0, defaults.get("short_exit_ma_threshold_pct", 0.0) * 100, 0.1, key='short_exit'); st.sidebar.slider("Stop Loss (%)", 0.0, 30.0, defaults.get("short_stop_loss_pct", 0.0) * 100, 0.5, key='short_sl'); st.sidebar.number_input("Delay Entry (days)", 0, 10, defaults.get("short_delay_days", 0), 1, key='short_delay')
|
| 633 |
|
| 634 |
+
if 'best_params' not in st.session_state: st.session_state.best_params = None
|
| 635 |
st.sidebar.markdown("---")
|
| 636 |
st.sidebar.header("4. Find Best Parameters")
|
| 637 |
with st.sidebar.expander("Set Optimisation Ranges"):
|
|
|
|
| 694 |
if st.sidebar.button("πΎ Save Settings as Default"):
|
| 695 |
save_settings({ "large_ma_period": st.session_state.ma_period, "bband_period": st.session_state.bb_period, "bband_std_dev": st.session_state.bb_std, "confidence_threshold": st.session_state.confidence_slider, "long_entry_threshold_pct": st.session_state.long_entry / 100, "long_exit_ma_threshold_pct": st.session_state.long_exit / 100, "long_stop_loss_pct": st.session_state.long_sl / 100, "long_delay_days": st.session_state.long_delay, "short_entry_threshold_pct": st.session_state.short_entry / 100, "short_exit_ma_threshold_pct": st.session_state.short_exit / 100, "short_stop_loss_pct": st.session_state.short_sl / 100, "short_delay_days": st.session_state.short_delay, })
|
| 696 |
|
| 697 |
+
if st.session_state.get("run_analysis_button"):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 698 |
with main_content_placeholder.container():
|
| 699 |
veto_to_use = st.session_state.get('veto_setup')
|
| 700 |
if veto_to_use: st.info("Veto filter is active for this analysis.")
|
| 701 |
+
else: st.info("π‘ Tip: You can find and apply a 'Veto Filter' from section 5 in the sidebar to potentially screen out bad trades.")
|
|
|
|
| 702 |
manual_params = {"large_ma_period": st.session_state.ma_period, "bband_period": st.session_state.bb_period, "bband_std_dev": st.session_state.bb_std, "confidence_threshold": st.session_state.confidence_slider, "long_entry_threshold_pct": st.session_state.long_entry / 100, "long_exit_ma_threshold_pct": st.session_state.long_exit / 100, "long_stop_loss_pct": st.session_state.long_sl / 100, "long_delay_days": st.session_state.long_delay, "short_entry_threshold_pct": st.session_state.short_entry / 100, "short_exit_ma_threshold_pct": st.session_state.short_exit / 100, "short_stop_loss_pct": st.session_state.short_sl / 100, "short_delay_days": st.session_state.short_delay, }
|
|
|
|
|
|
|
| 703 |
if st.session_state.run_mode == "Analyse Single Ticker":
|
| 704 |
selected_ticker = st.session_state.get('ticker_select', ticker_list[0])
|
| 705 |
cols_to_use = [selected_ticker]
|
| 706 |
if f'{selected_ticker}_Volume' in master_df.columns: cols_to_use.append(f'{selected_ticker}_Volume')
|
| 707 |
data_for_backtest = master_df[cols_to_use].rename(columns={selected_ticker: 'Close', f'{selected_ticker}_Volume': 'Volume'})
|
| 708 |
ticker_data_series = data_for_backtest.loc[pd.Timestamp(st.session_state.start_date):pd.Timestamp(st.session_state.end_date)]
|
|
|
|
| 709 |
if not ticker_data_series.empty:
|
| 710 |
long_pnl, short_pnl, avg_long_trade, avg_short_trade, results_df, trades, open_trades = run_backtest(ticker_data_series, manual_params, st.session_state.use_rsi, st.session_state.use_vol, st.session_state.use_trend, st.session_state.use_volume, st.session_state.rsi_w, st.session_state.vol_w, st.session_state.trend_w, st.session_state.volume_w, veto_setup=veto_to_use)
|
| 711 |
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}
|
| 712 |
if open_trades: st.session_state.open_trades_df = pd.DataFrame(open_trades)
|
| 713 |
else: st.session_state.open_trades_df = pd.DataFrame()
|
| 714 |
else: st.warning("No data for this ticker in the selected date range.")
|
|
|
|
| 715 |
elif st.session_state.run_mode == "Analyse Full List":
|
| 716 |
summary_results, all_open_trades = [], []
|
| 717 |
progress_bar = st.progress(0, text="Starting analysis...")
|
|
|
|
| 735 |
else: st.warning("No trades found for any ticker with the current settings.")
|
| 736 |
if all_open_trades: st.session_state.open_trades_df = pd.DataFrame(all_open_trades)
|
| 737 |
else: st.session_state.open_trades_df = pd.DataFrame()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 738 |
|
| 739 |
# --- Main Display Area ---
|
| 740 |
with main_content_placeholder.container():
|