Daveabc12 commited on
Commit
be82c45
·
verified ·
1 Parent(s): a001d04

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +254 -111
app.py CHANGED
@@ -3169,7 +3169,105 @@ def update_state():
3169
  if main_key in st.session_state:
3170
  st.session_state[main_key] = st.session_state[widget_key]
3171
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3172
  def main():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3173
  st.set_page_config(page_title="Dave's Quant System", page_icon="🔴", layout="wide")
3174
 
3175
  # --- [FIX 1: Robust Initialization] ---
@@ -3305,7 +3403,7 @@ def main():
3305
  st.session_state.ticker_select = st.session_state.ticker_list[0]
3306
  st.session_state.load_message = load_message
3307
  st.session_state.outlier_report = outlier_report
3308
-
3309
  master_df = st.session_state.master_df
3310
  ticker_list = st.session_state.ticker_list
3311
 
@@ -3354,20 +3452,7 @@ def main():
3354
  div[data-testid="stSidebar"] button[kind="primary"] { width: 100%; }
3355
  div[data-testid="stSidebar"] div[data-testid="stButton"] button { width: 100%; }
3356
  </style>""", unsafe_allow_html=True)
3357
-
3358
- # --- DISPLAY LOAD MESSAGES (Moved here to check Production Mode) ---
3359
- if 'load_message' in st.session_state:
3360
- with main_content_placeholder.container():
3361
- # Always show the main load success/fail message
3362
- st.info(st.session_state.load_message)
3363
-
3364
- # Only show Data Cleaning details if NOT in Production Mode
3365
- if st.session_state.outlier_report and not production_mode:
3366
- st.info(f"Data Cleaning: Found and removed price spikes >100% in {len(st.session_state.outlier_report)} tickers.")
3367
-
3368
- del st.session_state.load_message
3369
- if 'outlier_report' in st.session_state: del st.session_state.outlier_report
3370
-
3371
 
3372
  # --- SIDEBAR SECTION 1: Select Test Mode & Dates (Common to both) ---
3373
  st.sidebar.header("1. Select Test Mode & Dates")
@@ -4334,112 +4419,170 @@ def main():
4334
  else: st.info("No chart data available.")
4335
 
4336
  # 13. Display Full Analysis Summary
4337
- elif st.session_state.get('summary_df') is not None and not st.session_state.summary_df.empty:
4338
- # 1. Top Cards
4339
- display_summary_analytics(st.session_state.summary_df)
4340
-
4341
- # 2. Histogram 1: Profit Distribution
4342
- st.markdown("---")
4343
- try:
4344
- if 'generate_profit_distribution_chart' in globals():
4345
- dist_fig = generate_profit_distribution_chart(st.session_state.summary_df)
4346
- if dist_fig: st.plotly_chart(dist_fig, use_container_width=True)
4347
- except Exception: pass
4348
 
4349
- # 3. Histogram 2: Trades Over Time (NEW)
4350
- try:
4351
- if 'generate_trades_timeline_histogram' in globals() and st.session_state.get('open_trades_df') is not None:
4352
- timeline_fig = generate_trades_timeline_histogram(
4353
- st.session_state.open_trades_df,
4354
- st.session_state.start_date,
4355
- st.session_state.end_date
4356
- )
4357
- if timeline_fig: st.plotly_chart(timeline_fig, use_container_width=True)
4358
- except Exception as e: st.error(f"Chart Error: {e}")
4359
- st.markdown("---")
 
 
 
 
 
 
 
4360
 
4361
- # 4. Results Per Ticker Table
4362
- st.subheader("Results per Ticker")
4363
- if st.checkbox("Only show tickers with trades", value=True):
4364
- df_to_display = st.session_state.summary_df[(st.session_state.summary_df['Num Long Trades'] > 0) | (st.session_state.summary_df['Num Short Trades'] > 0)].copy()
4365
- else:
4366
- df_to_display = st.session_state.summary_df.copy()
 
 
4367
 
4368
- date_cols = ["First Long Entry", "Last Long Exit", "First Short Entry", "Last Short Exit"]
4369
- for col in date_cols:
4370
- if col in df_to_display.columns: df_to_display[col] = pd.to_datetime(df_to_display[col], errors='coerce').dt.strftime('%Y-%m-%d')
4371
- df_to_display.fillna('-', inplace=True)
4372
- st.dataframe(df_to_display.style.format({ "Cumulative Long P&L": "{:.2%}", "Avg Long Profit per Trade": "{:.2%}", "Avg Long Duration (Days)": "{:.1f}", "Cumulative Short P&L": "{:.2%}", "Avg Short Profit per Trade": "{:.2%}", "Avg Short Duration (Days)": "{:.1f}", "Avg Long Confidence": "{:.0f}%", "Avg Short Confidence": "{:.0f}%" }, na_rep='-'))
 
4373
 
4374
- if not production_mode:
4375
- if st.button("💾 Add these settings to User-Defined List", key="save_setup_from_analysis", on_click=add_setup_to_user_list): pass
4376
-
4377
- # 5. Open Positions Table (With Filter)
4378
- st.subheader("👨🏻‍💼 Open Positions & Recently Closed",
4379
- help="This table displays all currently ACTIVE trades, plus any trades that closed within the last 30 days.")
4380
 
4381
- if st.session_state.get('open_trades_df') is not None and not st.session_state.open_trades_df.empty:
4382
- full_df = st.session_state.open_trades_df.copy()
4383
-
4384
- # --- FILTER: Show 'Open' OR 'Closed in last 30 days' ---
4385
- cutoff = pd.Timestamp.now() - pd.Timedelta(days=30)
4386
- # Ensure date column is datetime
4387
- full_df['Date Closed'] = pd.to_datetime(full_df['Date Closed'], errors='coerce')
4388
-
4389
- mask_open = (full_df['Status'] == 'Open')
4390
- mask_recent = (full_df['Status'] == 'Closed') & (full_df['Date Closed'] >= cutoff)
4391
-
4392
- display_open_df = full_df[mask_open | mask_recent].copy()
4393
- # -------------------------------------------------------
4394
 
4395
- display_open_df.sort_values(by=['Status', 'Date Open'], ascending=[True, False], inplace=True)
4396
-
4397
- cols_order_manual = ['Ticker', 'Status', 'Final % P/L', 'Side', 'Date Open', 'Date Closed', 'Start Confidence']
4398
- existing_cols_open = [col for col in cols_order_manual if col in display_open_df.columns]
4399
-
4400
- if existing_cols_open and not display_open_df.empty:
4401
- st.dataframe(display_open_df[existing_cols_open].style.format({
4402
- "Final % P/L": lambda x: f"{x:.2%}" if pd.notna(x) else '-',
4403
- "Date Open": lambda x: x.strftime('%Y-%m-%d') if pd.notna(x) else '-',
4404
- "Date Closed": lambda x: x.strftime('%Y-%m-%d') if pd.notna(x) else '-',
4405
- "Start Confidence": lambda x: f"{x:.0f}%" if pd.notna(x) else '-'
4406
- }, na_rep='-'))
4407
- else:
4408
- st.info("No Open or Recent trades found (older trades are hidden).")
4409
  else:
4410
- st.info("No trades found.")
4411
-
4412
- # 14. Default Message
4413
  else:
4414
- if not any([st.session_state.get(k) for k in ['run_advanced_advisor','run_user_advisor_setup','advisor_df','confidence_results_df','single_ticker_results','summary_df','load_message']]):
4415
- st.info("Click a 'Run' button in the sidebar to start.")
4416
 
4417
- # 15. Footer Buttons (Hidden in Production)
4418
- if not production_mode:
4419
- st.markdown("---")
4420
- col_load1, col_load2, col_load3 = st.columns(3)
4421
- with col_load1:
4422
- if st.session_state.get('best_params'): st.button("⬇️ Load Optimal Parameters", on_click=apply_best_params_to_widgets, use_container_width=True)
4423
- with col_load2:
4424
- if 'apply_best_weights_to_widgets' in locals() and st.session_state.get('best_weights'): st.button("⬇️ Load Optimal Weights", on_click=apply_best_weights_to_widgets, use_container_width=True)
4425
- with col_load3:
4426
- if st.session_state.get('worst_confidence_setups_list'):
4427
- if st.button("Apply Top Worst Setups as Veto Filter", use_container_width=True):
4428
- st.session_state.veto_setup_list = st.session_state.worst_confidence_setups_list
4429
- st.session_state.worst_confidence_setups_list = None
4430
- st.sidebar.info(f"Applying {len(st.session_state.veto_setup_list)} Veto filters.")
4431
- st.rerun()
4432
-
4433
- col_load4 = st.columns(1)[0]
4434
- with col_load4:
4435
- is_markov_relevant = (st.session_state.primary_driver == 'Markov State') or st.session_state.use_markov
4436
- if st.session_state.get('best_markov_setup') and is_markov_relevant:
4437
- if st.button("⬇️ Load Best Markov Setup", on_click=None, use_container_width=True):
4438
- st.session_state.primary_driver = "Markov State"
4439
- st.session_state.use_markov = True
4440
- st.sidebar.success("Best Markov setup loaded!")
4441
- st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4442
 
 
4443
  def generate_trades_timeline_histogram(trades_df, start_date, end_date):
4444
  """
4445
  Creates a stacked histogram showing trade results over time.
 
3169
  if main_key in st.session_state:
3170
  st.session_state[main_key] = st.session_state[widget_key]
3171
 
3172
+ def convert_results_to_csv(summary_df, params):
3173
+ """
3174
+ Combines the Performance Summary and the Configuration Settings into a single CSV string.
3175
+ """
3176
+ if summary_df is None or summary_df.empty:
3177
+ return ""
3178
+
3179
+ # 1. Get Performance Data (First Row)
3180
+ perf_data = summary_df.iloc[0].to_dict()
3181
+
3182
+ # 2. Get Settings Data (From Session State/Params)
3183
+ settings_data = {
3184
+ "--- SETTINGS ---": "", # Separator
3185
+ "Start Date": params.get('start_date'),
3186
+ "End Date": params.get('end_date'),
3187
+ "Primary Driver": params.get('primary_driver'),
3188
+ "Confidence Threshold": params.get('confidence_threshold'),
3189
+ "RSI Weight": params.get('rsi_w') if params.get('use_rsi') else "Off",
3190
+ "Volatility Weight": params.get('vol_w') if params.get('use_volatility') else "Off",
3191
+ "Trend Weight": params.get('trend_w') if params.get('use_trend') else "Off",
3192
+ "Volume Weight": params.get('vol_w_val') if params.get('use_volume') else "Off",
3193
+ "MACD Weight": params.get('macd_w') if params.get('use_macd') else "Off",
3194
+ "MA Slope Weight": params.get('ma_slope_w') if params.get('use_ma_slope') else "Off",
3195
+ "Markov Weight": params.get('markov_w') if params.get('use_markov') else "Off",
3196
+ "MFI Weight": params.get('mfi_w') if params.get('use_mfi') else "Off",
3197
+ "SuperTrend Weight": params.get('supertrend_w') if params.get('use_supertrend') else "Off",
3198
+ "ADX Filter": f"On (< {params.get('adx_threshold')})" if params.get('use_adx_filter') else "Off",
3199
+ "BB Period": params.get('bband_period'),
3200
+ "BB Std Dev": params.get('bband_std_dev'),
3201
+ "MA Period": params.get('large_ma_period'),
3202
+ "Long Entry Thresh %": params.get('long_entry_threshold_pct'),
3203
+ "Short Entry Thresh %": params.get('short_entry_threshold_pct'),
3204
+ "Exit Logic": params.get('exit_logic_type'),
3205
+ "Smart TSL %": params.get('smart_trailing_stop_pct')
3206
+ }
3207
+
3208
+ # 3. Combine into one dictionary
3209
+ combined_data = {**perf_data, **settings_data}
3210
+
3211
+ # 4. Create DataFrame and convert to CSV
3212
+ export_df = pd.DataFrame([combined_data])
3213
+ return export_df.to_csv(index=False).encode('utf-8')
3214
+
3215
  def main():
3216
+ if 'run_advanced_advisor' not in st.session_state: st.session_state.run_advanced_advisor = False
3217
+ if 'run_user_advisor_setup' not in st.session_state: st.session_state.run_user_advisor_setup = False
3218
+
3219
+ # 2. Define ALL Defaults (Prevents "AttributeError" crashes)
3220
+ defaults = {
3221
+ 'start_date': date(2024, 1, 1), 'end_date': date.today(),
3222
+ 'bband_period': 20, 'bband_std_dev': 2.0,
3223
+ 'long_entry_threshold_pct': 0.0, 'short_entry_threshold_pct': 0.0,
3224
+ 'long_exit_ma_threshold_pct': 0.0, 'short_exit_ma_threshold_pct': 0.0,
3225
+ 'confidence_threshold': 50, 'max_long_duration': 60, 'max_short_duration': 60,
3226
+ 'long_trailing_stop_loss_pct': 0.0, 'short_trailing_stop_loss_pct': 0.0,
3227
+ 'primary_driver': 'Bollinger Bands',
3228
+ 'use_rsi': True, 'rsi_w': 0.5,
3229
+ 'use_volatility': True, 'vol_w': 0.5,
3230
+ 'use_trend': True, 'trend_w': 0.5,
3231
+ 'use_volume': True, 'vol_w_val': 0.5,
3232
+ 'use_macd': False, 'macd_w': 0.5,
3233
+ 'use_ma_slope': False, 'ma_slope_w': 0.5,
3234
+ 'use_markov': False, 'markov_w': 0.5,
3235
+ # New Indicators
3236
+ 'use_mfi': False, 'mfi_w': 0.5,
3237
+ 'use_supertrend': False, 'supertrend_w': 0.5,
3238
+ # Filters & Exits
3239
+ 'use_adx_filter': False, 'adx_threshold': 25, 'rsi_logic': 'Level',
3240
+ 'exit_logic_type': 'Standard (Price-Based)',
3241
+ 'smart_trailing_stop_pct': 0.05,
3242
+ 'smart_exit_atr_period': 14, 'smart_exit_atr_multiplier': 3.0,
3243
+ 'intelligent_tsl_pct': 1.0,
3244
+ 'use_ma_floor_filter': False,
3245
+ 'long_delay_days': 0, 'short_delay_days': 0,
3246
+ 'long_score_95_percentile': None, 'short_score_95_percentile': None,
3247
+ 'veto_setup_list': None,
3248
+ 'advisor_df': None, 'confidence_results_df': None,
3249
+ 'single_ticker_results': None, 'summary_df': None,
3250
+ 'open_trades_df': None, 'load_message': None,
3251
+ 'outlier_report': None,
3252
+ 'best_markov_setup': None,
3253
+ # Optimization
3254
+ 'ma_period': 50, 'bb_period': 20, 'bb_std': 2.0,
3255
+ 'confidence_slider': 50, 'long_entry': 0.0, 'long_exit': 0.0,
3256
+ 'long_sl': 0.0, 'long_delay': 0, 'short_entry': 0.0,
3257
+ 'short_exit': 0.0, 'short_sl': 0.0, 'short_delay': 0, 'max_duration': 60,
3258
+ 'volume_w': 0.5 # fallback for vol_w_val naming differences
3259
+ }
3260
+
3261
+ # 3. Apply Defaults if Missing
3262
+ for key, val in defaults.items():
3263
+ if key not in st.session_state: st.session_state[key] = val
3264
+
3265
+ # 4. Helper to update state from widgets
3266
+ def update_state():
3267
+ for key in defaults.keys():
3268
+ widget_key = f"widget_{key}"
3269
+ if widget_key in st.session_state: st.session_state[key] = st.session_state[widget_key]
3270
+
3271
  st.set_page_config(page_title="Dave's Quant System", page_icon="🔴", layout="wide")
3272
 
3273
  # --- [FIX 1: Robust Initialization] ---
 
3403
  st.session_state.ticker_select = st.session_state.ticker_list[0]
3404
  st.session_state.load_message = load_message
3405
  st.session_state.outlier_report = outlier_report
3406
+
3407
  master_df = st.session_state.master_df
3408
  ticker_list = st.session_state.ticker_list
3409
 
 
3452
  div[data-testid="stSidebar"] button[kind="primary"] { width: 100%; }
3453
  div[data-testid="stSidebar"] div[data-testid="stButton"] button { width: 100%; }
3454
  </style>""", unsafe_allow_html=True)
3455
+
 
 
 
 
 
 
 
 
 
 
 
 
 
3456
 
3457
  # --- SIDEBAR SECTION 1: Select Test Mode & Dates (Common to both) ---
3458
  st.sidebar.header("1. Select Test Mode & Dates")
 
4419
  else: st.info("No chart data available.")
4420
 
4421
  # 13. Display Full Analysis Summary
4422
+ if st.session_state.get('summary_df') is not None and not st.session_state.summary_df.empty:
4423
+ # 1. Top Cards
4424
+ display_summary_analytics(st.session_state.summary_df)
4425
+
4426
+ # 2. Histogram 1: Profit Distribution
4427
+ st.markdown("---")
4428
+ try:
4429
+ if 'generate_profit_distribution_chart' in globals():
4430
+ dist_fig = generate_profit_distribution_chart(st.session_state.summary_df)
4431
+ if dist_fig: st.plotly_chart(dist_fig, use_container_width=True)
4432
+ except Exception: pass
4433
 
4434
+ # 3. Histogram 2: Trades Over Time (NEW)
4435
+ try:
4436
+ if 'generate_trades_timeline_histogram' in globals() and st.session_state.get('open_trades_df') is not None:
4437
+ timeline_fig = generate_trades_timeline_histogram(
4438
+ st.session_state.open_trades_df,
4439
+ st.session_state.start_date,
4440
+ st.session_state.end_date
4441
+ )
4442
+ if timeline_fig: st.plotly_chart(timeline_fig, use_container_width=True)
4443
+ except Exception as e: st.error(f"Chart Error: {e}")
4444
+ st.markdown("---")
4445
+
4446
+ # 4. Results Per Ticker Table
4447
+ st.subheader("Results per Ticker")
4448
+ if st.checkbox("Only show tickers with trades", value=True):
4449
+ df_to_display = st.session_state.summary_df[(st.session_state.summary_df['Num Long Trades'] > 0) | (st.session_state.summary_df['Num Short Trades'] > 0)].copy()
4450
+ else:
4451
+ df_to_display = st.session_state.summary_df.copy()
4452
 
4453
+ date_cols = ["First Long Entry", "Last Long Exit", "First Short Entry", "Last Short Exit"]
4454
+ for col in date_cols:
4455
+ if col in df_to_display.columns: df_to_display[col] = pd.to_datetime(df_to_display[col], errors='coerce').dt.strftime('%Y-%m-%d')
4456
+ df_to_display.fillna('-', inplace=True)
4457
+ st.dataframe(df_to_display.style.format({ "Cumulative Long P&L": "{:.2%}", "Avg Long Profit per Trade": "{:.2%}", "Avg Long Duration (Days)": "{:.1f}", "Cumulative Short P&L": "{:.2%}", "Avg Short Profit per Trade": "{:.2%}", "Avg Short Duration (Days)": "{:.1f}", "Avg Long Confidence": "{:.0f}%", "Avg Short Confidence": "{:.0f}%" }, na_rep='-'))
4458
+
4459
+ if not production_mode:
4460
+ if st.button("💾 Add these settings to User-Defined List", key="save_setup_from_analysis", on_click=add_setup_to_user_list): pass
4461
 
4462
+ # 5. Open Positions Table (With Filter)
4463
+ st.subheader("👨🏽‍💼 Open Positions & Recently Closed",
4464
+ help="This table displays all currently ACTIVE trades, plus any trades that closed within the last 30 days.")
4465
+
4466
+ if st.session_state.get('open_trades_df') is not None and not st.session_state.open_trades_df.empty:
4467
+ full_df = st.session_state.open_trades_df.copy()
4468
 
4469
+ # --- FILTER: Show 'Open' OR 'Closed in last 30 days' ---
4470
+ cutoff = pd.Timestamp.now() - pd.Timedelta(days=30)
4471
+ full_df['Date Closed'] = pd.to_datetime(full_df['Date Closed'], errors='coerce')
 
 
 
4472
 
4473
+ mask_open = (full_df['Status'] == 'Open')
4474
+ mask_recent = (full_df['Status'] == 'Closed') & (full_df['Date Closed'] >= cutoff)
4475
+
4476
+ display_open_df = full_df[mask_open | mask_recent].copy()
4477
+ # -------------------------------------------------------
 
 
 
 
 
 
 
 
4478
 
4479
+ display_open_df.sort_values(by=['Status', 'Date Open'], ascending=[True, False], inplace=True)
4480
+
4481
+ cols_order_manual = ['Ticker', 'Status', 'Final % P/L', 'Side', 'Date Open', 'Date Closed', 'Start Confidence']
4482
+ existing_cols_open = [col for col in cols_order_manual if col in display_open_df.columns]
4483
+
4484
+ if existing_cols_open and not display_open_df.empty:
4485
+ st.dataframe(display_open_df[existing_cols_open].style.format({
4486
+ "Final % P/L": lambda x: f"{x:.2%}" if pd.notna(x) else '-',
4487
+ "Date Open": lambda x: x.strftime('%Y-%m-%d') if pd.notna(x) else '-',
4488
+ "Date Closed": lambda x: x.strftime('%Y-%m-%d') if pd.notna(x) else '-',
4489
+ "Start Confidence": lambda x: f"{x:.0f}%" if pd.notna(x) else '-'
4490
+ }, na_rep='-'))
 
 
4491
  else:
4492
+ st.info("No Open or Recent trades found (older trades are hidden).")
 
 
4493
  else:
4494
+ st.info("No trades found.")
 
4495
 
4496
+ # --- NEW: SAVE RESULTS BUTTON ---
4497
+ st.markdown("---")
4498
+ st.subheader("💾 Save Analysis")
4499
+
4500
+ current_config = {
4501
+ 'start_date': st.session_state.start_date, 'end_date': st.session_state.end_date,
4502
+ 'primary_driver': st.session_state.primary_driver, 'confidence_threshold': st.session_state.confidence_threshold,
4503
+ 'use_rsi': st.session_state.use_rsi, 'rsi_w': st.session_state.rsi_w,
4504
+ 'use_volatility': st.session_state.use_volatility, 'vol_w': st.session_state.vol_w,
4505
+ 'use_trend': st.session_state.use_trend, 'trend_w': st.session_state.trend_w,
4506
+ 'use_volume': st.session_state.use_volume, 'vol_w_val': st.session_state.vol_w_val,
4507
+ 'use_macd': st.session_state.use_macd, 'macd_w': st.session_state.macd_w,
4508
+ 'use_ma_slope': st.session_state.use_ma_slope, 'ma_slope_w': st.session_state.ma_slope_w,
4509
+ 'use_markov': st.session_state.use_markov, 'markov_w': st.session_state.markov_w,
4510
+ 'use_mfi': st.session_state.use_mfi, 'mfi_w': st.session_state.mfi_w,
4511
+ 'use_supertrend': st.session_state.use_supertrend, 'supertrend_w': st.session_state.supertrend_w,
4512
+ 'use_adx_filter': st.session_state.use_adx_filter, 'adx_threshold': st.session_state.adx_threshold,
4513
+ 'bband_period': st.session_state.bband_period, 'bband_std_dev': st.session_state.bband_std_dev,
4514
+ 'large_ma_period': st.session_state.get('large_ma_period', 50),
4515
+ 'long_entry_threshold_pct': st.session_state.long_entry_threshold_pct,
4516
+ 'short_entry_threshold_pct': st.session_state.short_entry_threshold_pct,
4517
+ 'exit_logic_type': st.session_state.exit_logic_type,
4518
+ 'smart_trailing_stop_pct': st.session_state.smart_trailing_stop_pct
4519
+ }
4520
+
4521
+ # Generate CSV
4522
+ if 'convert_results_to_csv' in globals():
4523
+ csv_data = convert_results_to_csv(st.session_state.summary_df, current_config)
4524
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M")
4525
+ file_name = f"Backtest_Results_{timestamp}.csv"
4526
+
4527
+ st.download_button(
4528
+ label="⬇️ Download Results & Settings as CSV",
4529
+ data=csv_data,
4530
+ file_name=file_name,
4531
+ mime='text/csv',
4532
+ use_container_width=True
4533
+ )
4534
+
4535
+ # 14. Default Message
4536
+ else:
4537
+ if not any([st.session_state.get(k) for k in ['run_advanced_advisor','run_user_advisor_setup','advisor_df','confidence_results_df','single_ticker_results','summary_df','load_message']]):
4538
+ st.info("Click a 'Run' button in the sidebar to start.")
4539
+
4540
+ # 15. Footer Buttons (Hidden in Production)
4541
+ if not production_mode:
4542
+ st.markdown("---")
4543
+ col_load1, col_load2, col_load3 = st.columns(3)
4544
+ with col_load1:
4545
+ if 'apply_best_params_to_widgets' in locals() and st.session_state.get('best_params'):
4546
+ st.button("⬇️ Load Optimal Parameters", on_click=apply_best_params_to_widgets, use_container_width=True)
4547
+ with col_load2:
4548
+ if 'apply_best_weights_to_widgets' in locals() and st.session_state.get('best_weights'):
4549
+ st.button("⬇️ Load Optimal Weights", on_click=apply_best_weights_to_widgets, use_container_width=True)
4550
+ with col_load3:
4551
+ if st.session_state.get('worst_confidence_setups_list'):
4552
+ if st.button("Apply Top Worst Setups as Veto Filter", use_container_width=True):
4553
+ st.session_state.veto_setup_list = st.session_state.worst_confidence_setups_list
4554
+ st.session_state.worst_confidence_setups_list = None
4555
+ st.sidebar.info(f"Applying {len(st.session_state.veto_setup_list)} Veto filters.")
4556
+ st.rerun()
4557
+
4558
+ col_load4 = st.columns(1)[0]
4559
+ with col_load4:
4560
+ is_markov_relevant = (st.session_state.primary_driver == 'Markov State') or st.session_state.use_markov
4561
+ if st.session_state.get('best_markov_setup') and is_markov_relevant:
4562
+ if st.button("⬇️ Load Best Markov Setup", on_click=None, use_container_width=True):
4563
+ st.session_state.primary_driver = "Markov State"
4564
+ st.session_state.use_markov = True
4565
+ st.sidebar.success("Best Markov setup loaded!")
4566
+ st.rerun()
4567
+
4568
+ # --- LOGIC HANDLERS (Hidden execution) ---
4569
+ if st.session_state.run_advanced_advisor:
4570
+ with st.spinner("Running Optimization..."):
4571
+ run_optimization()
4572
+ st.session_state.run_advanced_advisor = False
4573
+
4574
+ if st.session_state.run_user_advisor_setup:
4575
+ with st.spinner("Running User Setup..."):
4576
+ current_params = {k: st.session_state[k] for k in defaults.keys() if k in st.session_state}
4577
+ current_params['use_mfi'] = st.session_state.use_mfi
4578
+ current_params['mfi_w'] = st.session_state.mfi_w
4579
+ current_params['use_supertrend'] = st.session_state.use_supertrend
4580
+ current_params['supertrend_w'] = st.session_state.supertrend_w
4581
+
4582
+ run_user_defined_setup(current_params)
4583
+ st.session_state.run_user_advisor_setup = False
4584
 
4585
+ # --- CHART FUNCTION (Must be outside main) ---
4586
  def generate_trades_timeline_histogram(trades_df, start_date, end_date):
4587
  """
4588
  Creates a stacked histogram showing trade results over time.