Skip to content

Commit a121fdd

Browse files
committed
fixed: dashboard and backtest
1 parent 71ec9a4 commit a121fdd

2 files changed

Lines changed: 195 additions & 195 deletions

File tree

dashboard/pages/backtest_lab.py

Lines changed: 67 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,22 @@
44
import plotly.graph_objects as go
55
from pathlib import Path
66
import json
7-
import subprocess
7+
8+
# IMPORT DIRETTO DEL SERVIZIO BACKEND
9+
from services.backtest import run_backtest_session
810
from src.strategies import STRATEGY_MAP
911
from src.settings_manager import SettingsManager
1012

1113
st.set_page_config(page_title="Backtest Lab", page_icon="🧪", layout="wide")
1214
st.title("🧪 Backtest Laboratory")
1315

1416
# --- HELPER FUNCTIONS ---
17+
1518
def load_benchmark_data(session_path: Path):
1619
"""
1720
Scansiona una cartella di sessione (TIMESTAMP) e carica i dati di tutte le strategie trovate.
1821
Ritorna:
19-
- strategies_data: dict { 'StrategyName': {'equity': df, 'config': dict} }
22+
- strategies_data: dict { 'StrategyName': {'equity': df, 'config': dict, 'path': Path} }
2023
- comparison_df: DataFrame riassuntivo per la tabella
2124
"""
2225
strategies_data = {}
@@ -84,12 +87,13 @@ def plot_comparison(strategies_data):
8487
return fig
8588

8689
# --- SIDEBAR CONFIGURATION ---
90+
8791
with st.sidebar:
8892
st.header("⚙️ Configuration")
8993

9094
mode = st.radio("Run Mode", ["Single Strategy", "Benchmark (Run All)"])
9195

92-
# Carica parametri salvati per pre-compilare
96+
# Carica parametri salvati per pre-compilare i campi
9397
try:
9498
manager = SettingsManager()
9599
saved_config = manager.load_config()
@@ -102,85 +106,89 @@ def plot_comparison(strategies_data):
102106
if mode == "Single Strategy":
103107
selected_strat = st.selectbox("Select Strategy", list(STRATEGY_MAP.keys()))
104108

105-
# Parametri override (UI semplificata per RSI/EMA)
109+
# Recupera default dal JSON salvato
106110
default_params = saved_config.get("strategies_params", {}).get(selected_strat, {})
111+
107112
st.subheader("Parameters Override")
113+
st.caption("Modifica i parametri per questo test (non salva nel sistema).")
108114

115+
# UI Dinamica per Override Parametri
116+
# I valori inseriti qui finiscono nel dizionario 'params'
109117
if selected_strat == "RSI":
110118
params['rsi_period'] = st.number_input("RSI Period", value=default_params.get('rsi_period', 14))
111-
params['rsi_lower'] = st.number_input("Lower", value=default_params.get('rsi_lower', 30))
112-
params['rsi_upper'] = st.number_input("Upper", value=default_params.get('rsi_upper', 70))
113-
params['atr_period'] = st.number_input("ATR", value=default_params.get('atr_period', 14))
119+
params['rsi_lower'] = st.number_input("Lower (<)", value=default_params.get('rsi_lower', 30))
120+
params['rsi_upper'] = st.number_input("Upper (>)", value=default_params.get('rsi_upper', 70))
121+
params['atr_period'] = st.number_input("ATR Period", value=default_params.get('atr_period', 14))
122+
114123
elif selected_strat == "EMA":
115124
params['short_window'] = st.number_input("Fast EMA", value=default_params.get('short_window', 50))
116125
params['long_window'] = st.number_input("Slow EMA", value=default_params.get('long_window', 200))
117-
params['atr_period'] = st.number_input("ATR", value=default_params.get('atr_period', 14))
126+
params['atr_period'] = st.number_input("ATR Period", value=default_params.get('atr_period', 14))
118127

119128
st.markdown("---")
129+
st.subheader("Simulation Settings")
120130
initial_cap = st.number_input("Initial Capital (€)", value=10000, step=1000)
121131
years = st.slider("History (Years)", 1, 5, 2)
122132

123133
run_btn = st.button("🚀 RUN SIMULATION", type="primary")
124134

125135
# --- MAIN AREA ---
136+
126137
tab_run, tab_results = st.tabs(["🚀 Run Simulation", "📊 Results Analysis"])
127138

139+
# TAB 1: ESECUZIONE
128140
with tab_run:
129141
if run_btn:
130142
st.info("Simulation started... please wait.")
131143
progress_bar = st.progress(0)
132144

133145
try:
134-
# Costruzione comando per subprocess
135-
# Usiamo subprocess per garantire che il backtest giri in un processo pulito
136-
# e per supportare facilmente la modalità 'ALL' gestita dal main() del backend.
137-
cmd = ["python", "-m", "services.backtest"]
146+
session_path = ""
138147

148+
# CHIAMATA DIRETTA AL BACKEND
139149
if mode == "Benchmark (Run All)":
140-
cmd.append("ALL")
150+
# Esegue tutte le strategie usando i parametri nel JSON
151+
session_path = run_backtest_session(
152+
mode="ALL",
153+
initial_capital=initial_cap,
154+
years=years
155+
)
141156
else:
142-
cmd.append(selected_strat)
143-
# Nota: Per passare i parametri override al subprocess servirebbe un meccanismo CLI più complesso.
144-
# Per ora, in modalità Single, questo userà i parametri del JSON salvato se non modifichiamo services/backtest.py
145-
# PER ORA: Accettiamo che il 'Single' da UI legga dal JSON o implementiamo un fix rapido.
146-
# FIX RAPIDO: Per semplicità, in questa versione 'Single' usa i parametri salvati nel JSON.
147-
# Se vuoi l'override live, dovremmo passare i parametri come stringa JSON al comando CLI.
148-
149-
# Esecuzione
150-
process = subprocess.run(cmd, capture_output=True, text=True)
157+
# Esegue singola strategia usando i parametri della Sidebar (params)
158+
session_path = run_backtest_session(
159+
mode="SINGLE_OVERRIDE",
160+
override_strat_name=selected_strat,
161+
override_params=params, # <--- Qui passiamo l'override!
162+
initial_capital=initial_cap,
163+
years=years
164+
)
165+
151166
progress_bar.progress(100)
152167

153-
if process.returncode == 0:
154-
st.success("Simulation completed successfully!")
155-
156-
# Parsing dell'output per trovare dove ha salvato i dati
157-
# Cerchiamo la riga "Cartella Sessione: ..." nei log
158-
for line in process.stdout.splitlines() + process.stderr.splitlines():
159-
if "Cartella Sessione:" in line:
160-
session_path_str = line.split("Cartella Sessione:")[-1].strip()
161-
st.session_state['last_run_path'] = session_path_str
162-
st.experimental_rerun() # Ricarica per mostrare i risultati nel tab Results
168+
if session_path:
169+
st.success(f"Simulation completed! Path: {session_path}")
170+
# Salviamo il path nella sessione per aprirlo automaticamente nel tab Results
171+
st.session_state['last_run_path'] = session_path
172+
st.rerun()
163173
else:
164-
st.error("Error during execution.")
165-
with st.expander("Show Error Logs"):
166-
st.code(process.stderr)
167-
st.code(process.stdout)
174+
st.error("Simulation returned empty path. Check logs.")
168175

169176
except Exception as e:
170-
st.error(f"Critical Error: {e}")
177+
st.error(f"Critical Error during execution: {e}")
171178

179+
# TAB 2: ANALISI RISULTATI
172180
with tab_results:
173181
st.write("### 📂 Results Browser")
174182

175183
base_dir = Path("data/backtests")
176184
if not base_dir.exists():
177-
st.warning("No backtests found.")
185+
st.warning("No backtests found yet.")
178186
st.stop()
179187

180188
# Elenco sessioni (Timestamp) ordinate dalla più recente
181189
sessions = sorted([x.name for x in base_dir.iterdir() if x.is_dir()], reverse=True)
182190

183-
# Se abbiamo appena finito una run, selezionala di default
191+
# Selettore Sessione (Default: l'ultima appena eseguita)
184192
default_idx = 0
185193
if 'last_run_path' in st.session_state:
186194
last_name = Path(st.session_state['last_run_path']).name
@@ -191,22 +199,21 @@ def plot_comparison(strategies_data):
191199

192200
if selected_session:
193201
session_path = base_dir / selected_session
194-
st.caption(f"Path: {session_path}")
202+
st.caption(f"Viewing Data from: {session_path}")
195203

196204
# CARICAMENTO DATI
197205
strat_data, summary_df = load_benchmark_data(session_path)
198206

199207
if not strat_data:
200-
st.warning("Empty session folder.")
208+
st.warning("This session folder appears to be empty.")
201209
else:
202-
# 1. GRAFICO COMPARATIVO
210+
# 1. GRAFICO COMPARATIVO PLOTLY
203211
st.subheader("📈 Equity Curve Comparison")
204212
fig = plot_comparison(strat_data)
205213
st.plotly_chart(fig, use_container_width=True)
206214

207-
# 2. TABELLA METRICHE
215+
# 2. TABELLA METRICHE RIASSUNTIVE
208216
st.subheader("🏆 Performance Summary")
209-
# Formattazione colonne
210217
st.dataframe(
211218
summary_df.style.format({
212219
"Final Equity": "€ {:,.2f}",
@@ -219,24 +226,34 @@ def plot_comparison(strategies_data):
219226
st.markdown("---")
220227

221228
# 3. DETTAGLIO SINGOLA STRATEGIA
222-
st.subheader("🔍 Deep Dive: Single Strategy Details")
223-
strat_keys = list(strat_data.keys())
224-
detail_strat = st.selectbox("Inspect Strategy:", strat_keys)
229+
st.subheader("🔍 Deep Dive: Strategy Details")
230+
231+
col_sel_strat, col_space = st.columns([1, 2])
232+
with col_sel_strat:
233+
strat_keys = list(strat_data.keys())
234+
detail_strat = st.selectbox("Inspect Strategy:", strat_keys)
225235

226236
if detail_strat:
227237
d_data = strat_data[detail_strat]
228238
d_path = d_data['path']
229239

230240
c1, c2 = st.columns([1, 1])
241+
231242
with c1:
232243
st.write("**Configuration Used:**")
233244
st.json(d_data['config'].get('params', {}))
245+
st.write("**Stats:**")
246+
st.write(f"- Initial Capital: € {d_data['config'].get('initial_capital', 0):,.2f}")
247+
st.write(f"- Final Equity: € {d_data['config'].get('final_equity', 0):,.2f}")
234248

235249
with c2:
236-
st.write("**Trade History:**")
250+
st.write("**Trade History (Top 50):**")
237251
trades_csv = d_path / "trades.csv"
238252
if trades_csv.exists():
239253
df_trades = pd.read_csv(trades_csv)
240-
st.dataframe(df_trades, height=200, use_container_width=True)
254+
if not df_trades.empty:
255+
st.dataframe(df_trades.head(50), height=300, use_container_width=True)
256+
else:
257+
st.info("No trades executed.")
241258
else:
242-
st.info("No trades executed.")
259+
st.info("Trades file not found.")

0 commit comments

Comments
 (0)