diff --git a/.Jules/palette.md b/.Jules/palette.md new file mode 100644 index 0000000..9b3d5cd --- /dev/null +++ b/.Jules/palette.md @@ -0,0 +1,3 @@ +## 2024-05-18 - Added Tooltip Concrete Examples for Quant/Financial Parameters +**Learning:** Users in data-heavy or financial applications often face cognitive load parsing abstract parameters like "bps" (basis points) or statistical quantiles. Tooltips that only repeat the label are unhelpful. +**Action:** Always provide concrete numerical examples in tooltips for abstract or scaled units (e.g., `10 bps = 0.10%`, `0.80 = top 20%`) to immediately anchor the user's understanding. diff --git a/src/dashboard.py b/src/dashboard.py index 4156c1d..082c438 100644 --- a/src/dashboard.py +++ b/src/dashboard.py @@ -170,7 +170,14 @@ def get_cache_key(*args) -> str: else: factor_window = st.slider("Factor Beta Window (days)", 20, 252, 63, 7) vol_window = st.slider("Regime Vol Window (days)", 10, 60, 21, 5) - adv_pct = st.slider("ADV Participation %", 0.01, 0.30, float(DEFAULT_ADV_PCT), 0.01) + adv_pct = st.slider( + "ADV Participation %", + 0.01, + 0.30, + float(DEFAULT_ADV_PCT), + 0.01, + help="Maximum allowed percentage of Average Daily Volume to trade (e.g., 0.10 = 10%).", + ) st.markdown("---") st.subheader("4. Research Rigor") @@ -185,12 +192,25 @@ def get_cache_key(*args) -> str: st.info("Using full-sample quantiles (exploratory mode)") vol_q_high = st.slider( - "High Volatility Quantile", 0.5, 0.95, DEFAULT_VOL_QUANTILE_HIGH, 0.05 + "High Volatility Quantile", + 0.5, + 0.95, + DEFAULT_VOL_QUANTILE_HIGH, + 0.05, + help="Quantile threshold defining 'High Volatility' (e.g., 0.80 = top 20% most volatile days).", ) if mode == "Single-Asset": st.subheader("5. Backtest Settings") - bt_cost = st.number_input("Transaction Cost (bps)", value=DEFAULT_COST_BPS, step=1) / 10000 + bt_cost = ( + st.number_input( + "Transaction Cost (bps)", + value=DEFAULT_COST_BPS, + step=1, + help="Cost per trade in basis points (e.g., 10 bps = 0.10%).", + ) + / 10000 + ) allow_short = st.checkbox("Allow Short Selling?", value=False) else: st.subheader("5. Alert Thresholds") @@ -650,11 +670,11 @@ def get_cache_key(*args) -> str: # --- Regime Detection --- # Using 21-day annualized vol with option for out-of-sample analysis df = signals.detect_volatility_regime( - df, - vol_col='Vol_21d', - quantile_high=vol_q_high, + df, + vol_col="Vol_21d", + quantile_high=vol_q_high, quantile_low=0.25, - use_expanding=use_oos # Toggle between in-sample and out-of-sample + use_expanding=use_oos, # Toggle between in-sample and out-of-sample ) # --- Dashboard Header --- @@ -672,14 +692,12 @@ def get_cache_key(*args) -> str: h4.metric("Trend Status", "BULLISH" if latest['Close'] > latest[f'SMA_{sma_window}'] else "BEARISH") # --- Backtest (cached for reuse) --- -df['Signal_Trend'] = np.where(df['Close'] > df[f'SMA_{sma_window}'], 1, -1 if allow_short else 0) -bt_cache_key = get_cache_key( - signal_cache_key, bt_cost, allow_short, use_oos, vol_q_high -) +df["Signal_Trend"] = np.where(df["Close"] > df[f"SMA_{sma_window}"], 1, -1 if allow_short else 0) +bt_cache_key = get_cache_key(signal_cache_key, bt_cost, allow_short, use_oos, vol_q_high) if bt_cache_key not in st.session_state.backtest_results: with st.spinner("Running backtest simulation..."): - res_df = backtester.run_backtest(df, 'Signal_Trend', cost_bps=bt_cost, rebalance_freq='M') + res_df = backtester.run_backtest(df, "Signal_Trend", cost_bps=bt_cost, rebalance_freq="M") st.session_state.backtest_results[bt_cache_key] = res_df res_df = st.session_state.backtest_results[bt_cache_key] @@ -705,9 +723,7 @@ def get_cache_key(*args) -> str: transition_stats = regime_analysis.compute_transition_stats( res_df['Strategy_Net_Return'], res_df['Vol_Regime'] ) - sweep_df = sweep.run_sma_regime_sweep( - df, DEFAULT_SMA_SWEEP, mom_window, vol_q_high, use_oos - ) + sweep_df = sweep.run_sma_regime_sweep(df, DEFAULT_SMA_SWEEP, mom_window, vol_q_high, use_oos) else: cond_stats = pd.DataFrame() bench_cond = pd.DataFrame()