Sistemato salvataggio grafici e tolta visualizzazione dall'esecuzione

This commit is contained in:
fredmaloggia
2025-11-21 07:21:29 +01:00
parent f342c3aac3
commit 8716d80ecd
3 changed files with 89 additions and 63 deletions

View File

@@ -20,6 +20,7 @@ import numpy as np
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy import text from sqlalchemy import text
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from pathlib import Path
from shared_utils import ( from shared_utils import (
build_pattern_library, build_pattern_library,
@@ -70,6 +71,10 @@ def savefig_safe(path, **kwargs):
# ========================================= # =========================================
# PARAMETRI GLOBALI # PARAMETRI GLOBALI
# ========================================= # =========================================
OUTPUT_DIR = Path("output")
PLOT_DIR = Path("plot")
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
PLOT_DIR.mkdir(parents=True, exist_ok=True)
CONFIG = load_config() CONFIG = load_config()
DB_CONFIG = require_section(CONFIG, "db") DB_CONFIG = require_section(CONFIG, "db")
PATTERN_CONFIG = require_section(CONFIG, "pattern") PATTERN_CONFIG = require_section(CONFIG, "pattern")
@@ -80,12 +85,18 @@ PATTERN_CONFIG = CONFIG.get("pattern", {})
TAGGING_CONFIG = CONFIG.get("tagging", {}) TAGGING_CONFIG = CONFIG.get("tagging", {})
RANKING_CONFIG = CONFIG.get("ranking", {}) RANKING_CONFIG = CONFIG.get("ranking", {})
UNIVERSO_XLSX = "Universo per Trading System.xlsx" UNIVERSO_XLSX = "Input/Universo per Trading System.xlsx"
# Export # Export
OUTPUT_HURST_XLSX = "hurst_by_isin.xlsx" OUTPUT_HURST_XLSX = OUTPUT_DIR / "hurst_by_isin.xlsx"
OUTPUT_PATTERN_XLSX = "pattern_signals.xlsx" OUTPUT_PATTERN_XLSX = OUTPUT_DIR / "pattern_signals.xlsx"
ERROR_LOG_CSV = "errori_isin.csv" ERROR_LOG_CSV = OUTPUT_DIR / "errori_isin.csv"
FORWARD_BT_SIGNALS_XLSX = OUTPUT_DIR / "forward_bt_signals.xlsx"
FORWARD_BT_SUMMARY_XLSX = OUTPUT_DIR / "forward_bt_summary.xlsx"
TRADES_REPORT_XLSX = OUTPUT_DIR / "trades_report.xlsx"
DAILY_FROM_TRADES_CSV = OUTPUT_DIR / "daily_from_trades.csv"
DAILY_FROM_TRADES_XLSX = OUTPUT_DIR / "daily_from_trades.xlsx"
FINAL_METRICS_XLSX = OUTPUT_DIR / "final_metrics.xlsx"
# Stored Procedure & parametri # Stored Procedure & parametri
STORED_PROC = str(require_value(DB_CONFIG, "stored_proc", "db")) STORED_PROC = str(require_value(DB_CONFIG, "stored_proc", "db"))
@@ -883,10 +894,10 @@ bt_summary_df = pd.DataFrame(bt_summary) if bt_summary else pd.DataFrame(
columns=["ISIN","Nome","Categoria","Asset Class","CAGR_%","AnnVol_%","Sharpe","MaxDD_%eq","Calmar","HitRate_%","AvgTradeRet_bps","Turnover_%/step","N_Steps"] columns=["ISIN","Nome","Categoria","Asset Class","CAGR_%","AnnVol_%","Sharpe","MaxDD_%eq","Calmar","HitRate_%","AvgTradeRet_bps","Turnover_%/step","N_Steps"]
) )
bt_signals_df.to_excel("forward_bt_signals.xlsx", index=False) bt_signals_df.to_excel(FORWARD_BT_SIGNALS_XLSX, index=False)
bt_summary_df.to_excel("forward_bt_summary.xlsx", index=False) bt_summary_df.to_excel(FORWARD_BT_SUMMARY_XLSX, index=False)
print(f"✅ Salvato: forward_bt_signals.xlsx ({len(bt_signals_df):,} righe)") print(f"✅ Salvato: {FORWARD_BT_SIGNALS_XLSX} ({len(bt_signals_df):,} righe)")
print(f"✅ Salvato: forward_bt_summary.xlsx ({len(bt_summary_df):,} righe)") print(f"✅ Salvato: {FORWARD_BT_SUMMARY_XLSX} ({len(bt_summary_df):,} righe)")
if errors: if errors:
pd.DataFrame(errors).to_csv(ERROR_LOG_CSV, index=False) pd.DataFrame(errors).to_csv(ERROR_LOG_CSV, index=False)
@@ -957,7 +968,7 @@ def plot_heatmap_monthly(r: pd.Series, title: str, save_path: str = None):
plt.tight_layout() plt.tight_layout()
if save_path: if save_path:
savefig_safe(save_path, dpi=150) savefig_safe(save_path, dpi=150)
plt.show() # Non mostrare il plot durante l'esecuzione
def inverse_vol_weights(df, window=60, max_weight=None): def inverse_vol_weights(df, window=60, max_weight=None):
vol = df.rolling(window).std() vol = df.rolling(window).std()
@@ -1420,7 +1431,7 @@ def plot_portfolio_composition(weights: pd.DataFrame,
full = save_path full = save_path
print(f"💾 Salvato: {full}") print(f"💾 Salvato: {full}")
plt.show() # Plot salvato senza visualizzazione interattiva
def make_active_weights(w_base: pd.DataFrame, def make_active_weights(w_base: pd.DataFrame,
sig: pd.DataFrame, sig: pd.DataFrame,
@@ -1469,12 +1480,11 @@ plt.plot(eq_eq, label="Equal Weight")
plt.plot(eq_rp, label="Risk Parity") plt.plot(eq_rp, label="Risk Parity")
plt.legend(); plt.grid(); plt.title("Equity line - Selezione dinamica (Top N)") plt.legend(); plt.grid(); plt.title("Equity line - Selezione dinamica (Top N)")
plt.tight_layout() plt.tight_layout()
savefig_safe("equity_line_portafogli.png", dpi=150) savefig_safe(str(PLOT_DIR / "equity_line_portafogli.png"), dpi=150)
plt.show()
for name, r, path in [ for name, r, path in [
("Equal Weight", ret_eq, "heatmap_equal_weight.png"), ("Equal Weight", ret_eq, PLOT_DIR / "heatmap_equal_weight.png"),
("Risk Parity", ret_rp, "heatmap_risk_parity.png"), ("Risk Parity", ret_rp, PLOT_DIR / "heatmap_risk_parity.png"),
]: ]:
m = portfolio_metrics(r) m = portfolio_metrics(r)
@@ -1491,7 +1501,7 @@ import matplotlib.pyplot as plt
def plot_portfolio_composition_fixed(weights: pd.DataFrame, def plot_portfolio_composition_fixed(weights: pd.DataFrame,
title: str, title: str,
save_path: str | None = None, save_path: str | None = None,
max_legend: int = 12): max_legend: int = 20):
""" """
Stacked area dei pesi nel tempo. Stacked area dei pesi nel tempo.
'weights' deve essere già quello ATTIVO (già mascherato con i Signal) 'weights' deve essere già quello ATTIVO (già mascherato con i Signal)
@@ -1527,10 +1537,17 @@ def plot_portfolio_composition_fixed(weights: pd.DataFrame,
# Raggruppa coda lunga in "Altri" # Raggruppa coda lunga in "Altri"
if len(ordered) > max_legend: if len(ordered) > max_legend:
head, tail = ordered[:max_legend], ordered[max_legend:] head = ordered[:max_legend]
# Garantisce che 'Cash' resti in legenda anche oltre il cap
if "Cash" not in head and "Cash" in ordered:
head = head[:-1] + ["Cash"]
tail = [c for c in ordered if c not in head]
W_show = W[head].copy() W_show = W[head].copy()
if tail:
W_show["Altri"] = W[tail].sum(1) W_show["Altri"] = W[tail].sum(1)
ordered = head + ["Altri"] ordered = head + ["Altri"]
else:
ordered = head
else: else:
W_show = W[ordered].copy() W_show = W[ordered].copy()
@@ -1563,7 +1580,7 @@ def plot_portfolio_composition_fixed(weights: pd.DataFrame,
fig.savefig(save_path, dpi=150, bbox_inches="tight") fig.savefig(save_path, dpi=150, bbox_inches="tight")
print(f"💾 Salvato: {os.path.abspath(save_path)}") print(f"💾 Salvato: {os.path.abspath(save_path)}")
plt.show() # Plot salvato senza visualizzazione interattiva
# --- 1) Pesi teorici dei tre portafogli (già costruiti sopra) --- # --- 1) Pesi teorici dei tre portafogli (già costruiti sopra) ---
# w_eq : equal weight su 'cols' # w_eq : equal weight su 'cols'
@@ -1607,9 +1624,8 @@ w_rp_act = make_active_weights(w_rp, wide_sig, renorm_to_1=False, add_cash=Tru
w_agg_act = make_active_weights(w_agg, wide_sig, renorm_to_1=False, add_cash=True, cash_label="Cash") w_agg_act = make_active_weights(w_agg, wide_sig, renorm_to_1=False, add_cash=True, cash_label="Cash")
# --- 3) Plot + salvataggio --- # --- 3) Plot + salvataggio ---
os.makedirs("plots", exist_ok=True) plot_portfolio_composition_fixed(w_eq_act, "Equal Weight (attivi + Cash)", str(PLOT_DIR / "composition_equal_weight_active.png"))
plot_portfolio_composition_fixed(w_eq_act, "Equal Weight (attivi + Cash)", "plots/composition_equal_weight_active.png") plot_portfolio_composition_fixed(w_rp_act, "Risk Parity (attivi + Cash)", str(PLOT_DIR / "composition_risk_parity_active.png"))
plot_portfolio_composition_fixed(w_rp_act, "Risk Parity (attivi + Cash)", "plots/composition_risk_parity_active.png")
# ----------------------------- # -----------------------------
@@ -1721,11 +1737,11 @@ rep_rp = make_trades_report(wide_sig[[c for c in cols if c in wide_sig.columns]
wide_pnl[[c for c in cols if c in wide_pnl.columns]], wide_pnl[[c for c in cols if c in wide_pnl.columns]],
w_rp, "Risk Parity") w_rp, "Risk Parity")
with pd.ExcelWriter("trades_report.xlsx") as xw: with pd.ExcelWriter(TRADES_REPORT_XLSX) as xw:
rep_eq.to_excel(xw, "Equal_Weight", index=False) rep_eq.to_excel(xw, "Equal_Weight", index=False)
rep_rp.to_excel(xw, "Risk_Parity", index=False) rep_rp.to_excel(xw, "Risk_Parity", index=False)
print("✅ Report trades salvato in trades_report.xlsx") print(f"✅ Report trades salvato in {TRADES_REPORT_XLSX}")
# ============================================================ # ============================================================
# 5.6 Rebuild DAILY PnL from trades_report (calendarized) # 5.6 Rebuild DAILY PnL from trades_report (calendarized)
# → per rendere coerente il compounding dei trade con equity/heatmap # → per rendere coerente il compounding dei trade con equity/heatmap
@@ -1808,17 +1824,15 @@ daily_from_trades = rebuild_daily_from_trades_dict(trades_dict)
# Salva su disco (CSV + XLSX) per ispezione # Salva su disco (CSV + XLSX) per ispezione
if not daily_from_trades.empty: if not daily_from_trades.empty:
daily_from_trades.to_csv("daily_from_trades.csv", index_label="Date") daily_from_trades.to_csv(DAILY_FROM_TRADES_CSV, index_label="Date")
try: try:
with pd.ExcelWriter("daily_from_trades.xlsx") as xw: with pd.ExcelWriter(DAILY_FROM_TRADES_XLSX) as xw:
daily_from_trades.to_excel(xw, "Daily", index=True) daily_from_trades.to_excel(xw, "Daily", index=True)
except Exception as e: except Exception as e:
print(f"[WARN] Impossibile scrivere daily_from_trades.xlsx: {e}") print(f"[WARN] Impossibile scrivere {DAILY_FROM_TRADES_XLSX}: {e}")
# Plot equity & heatmap basati sui DAILY da trade (coerenti col compounding) # Plot equity & heatmap basati sui DAILY da trade (coerenti col compounding)
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from pathlib import Path
Path("plots_by_topN").mkdir(exist_ok=True)
fig, ax = plt.subplots(figsize=(10,6)) fig, ax = plt.subplots(figsize=(10,6))
for col, lab in [("Equal_Weight","Equal Weight"), for col, lab in [("Equal_Weight","Equal Weight"),
@@ -1829,9 +1843,9 @@ if not daily_from_trades.empty:
ax.legend(); ax.grid(True) ax.legend(); ax.grid(True)
ax.set_title("Equity line ricostruita dai trades (calendarizzata)") ax.set_title("Equity line ricostruita dai trades (calendarizzata)")
fig.tight_layout() fig.tight_layout()
fig.savefig("plots_by_topN/equity_from_trades.png", dpi=150) fig.savefig(PLOT_DIR / "equity_from_trades.png", dpi=150)
plt.close(fig) plt.close(fig)
print("💾 Salvato: plots_by_topN/equity_from_trades.png") print(f"💾 Salvato: {PLOT_DIR / 'equity_from_trades.png'}")
# Heatmap per ciascuna strategia # Heatmap per ciascuna strategia
for col, lab, fname in [ for col, lab, fname in [
@@ -1841,7 +1855,7 @@ if not daily_from_trades.empty:
if col in daily_from_trades.columns: if col in daily_from_trades.columns:
try: try:
plot_heatmap_monthly(daily_from_trades[col], f"Heatmap mensile {lab} (da trades)", plot_heatmap_monthly(daily_from_trades[col], f"Heatmap mensile {lab} (da trades)",
save_path=f"plots_by_topN/{fname}") save_path=PLOT_DIR / fname)
except Exception as e: except Exception as e:
print(f"[WARN] Heatmap {lab} da trades: {e}") print(f"[WARN] Heatmap {lab} da trades: {e}")
else: else:
@@ -2093,13 +2107,13 @@ final_byN_df = pd.DataFrame(rows_byN)[[
# Salvataggio: aggiunge/riscrive i fogli in final_metrics.xlsx # Salvataggio: aggiunge/riscrive i fogli in final_metrics.xlsx
# - mantiene (se vuoi) anche il foglio "Portfolio_Metrics" del caso corrente TOP_N # - mantiene (se vuoi) anche il foglio "Portfolio_Metrics" del caso corrente TOP_N
try: try:
with pd.ExcelWriter("final_metrics.xlsx", engine="openpyxl", mode="a", if_sheet_exists="replace") as xw: with pd.ExcelWriter(FINAL_METRICS_XLSX, engine="openpyxl", mode="a", if_sheet_exists="replace") as xw:
final_byN_df.to_excel(xw, "Portfolio_Metrics_By_N", index=False) final_byN_df.to_excel(xw, "Portfolio_Metrics_By_N", index=False)
except Exception: except Exception:
with pd.ExcelWriter("final_metrics.xlsx") as xw: with pd.ExcelWriter(FINAL_METRICS_XLSX) as xw:
final_byN_df.to_excel(xw, "Portfolio_Metrics_By_N", index=False) final_byN_df.to_excel(xw, "Portfolio_Metrics_By_N", index=False)
print("✅ Salvato: final_metrics.xlsx (Portfolio_Metrics_By_N) per TopN = 8..15") print(f"✅ Salvato: {FINAL_METRICS_XLSX} (Portfolio_Metrics_By_N) per TopN = 8..15")
# ====================================================================== # ======================================================================
# 6bis) Plot per ciascun TopN (8..15): Equity + Heatmap per strategia # 6bis) Plot per ciascun TopN (8..15): Equity + Heatmap per strategia
@@ -2108,8 +2122,8 @@ import os
import numpy as np import numpy as np
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
OUT_DIR = "plots_by_topN" OUT_DIR = PLOT_DIR
os.makedirs(OUT_DIR, exist_ok=True) OUT_DIR.mkdir(parents=True, exist_ok=True)
def _safe_series(r: pd.Series) -> pd.Series: def _safe_series(r: pd.Series) -> pd.Series:
"""Forza tipo numerico e se tutto NaN, rimpiazza con 0.0 (linea piatta ma plot salvato).""" """Forza tipo numerico e se tutto NaN, rimpiazza con 0.0 (linea piatta ma plot salvato)."""
@@ -2133,9 +2147,9 @@ def _save_equity_plot_byN(ret_eq, ret_rp, top_n: int):
eq_rp.plot(ax=ax, label="Risk Parity") eq_rp.plot(ax=ax, label="Risk Parity")
ax.legend() ax.legend()
ax.grid(True) ax.grid(True)
ax.set_title(f"Equity line TopN={top_n}") ax.set_title(f"Equity line - TopN={top_n}")
fig.tight_layout() fig.tight_layout()
savefig_safe(os.path.join(OUT_DIR, f"equity_topN_{top_n}.png"), dpi=150) savefig_safe(str(OUT_DIR / f"equity_topN_{top_n}.png"), dpi=150)
plt.close(fig) plt.close(fig)
def _save_heatmaps_byN(ret_eq, ret_rp, top_n: int): def _save_heatmaps_byN(ret_eq, ret_rp, top_n: int):
@@ -2144,13 +2158,13 @@ def _save_heatmaps_byN(ret_eq, ret_rp, top_n: int):
plot_heatmap_monthly( plot_heatmap_monthly(
ret_eq, ret_eq,
f"Heatmap mensile Equal Weight (TopN={top_n})", f"Heatmap mensile - Equal Weight (TopN={top_n})",
save_path=os.path.join(OUT_DIR, f"heatmap_equal_topN_{top_n}.png") save_path=OUT_DIR / f"heatmap_equal_topN_{top_n}.png"
) )
plot_heatmap_monthly( plot_heatmap_monthly(
ret_rp, ret_rp,
f"Heatmap mensile Risk Parity (TopN={top_n})", f"Heatmap mensile - Risk Parity (TopN={top_n})",
save_path=os.path.join(OUT_DIR, f"heatmap_rp_topN_{top_n}.png") save_path=OUT_DIR / f"heatmap_rp_topN_{top_n}.png"
) )
# Loop 8..15 replicando i plot per ciascuna combinazione # Loop 8..15 replicando i plot per ciascuna combinazione
@@ -2170,8 +2184,8 @@ import os
import numpy as np import numpy as np
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
OUT_DIR = "plots_by_topN" OUT_DIR = PLOT_DIR
os.makedirs(OUT_DIR, exist_ok=True) OUT_DIR.mkdir(parents=True, exist_ok=True)
# -- safety: helper per pesi attivi e plotting, se mancassero già nel file -- # -- safety: helper per pesi attivi e plotting, se mancassero già nel file --
@@ -2203,7 +2217,7 @@ if 'plot_portfolio_composition_fixed' not in globals():
def plot_portfolio_composition_fixed(weights: pd.DataFrame, def plot_portfolio_composition_fixed(weights: pd.DataFrame,
title: str, title: str,
save_path: str | None = None, save_path: str | None = None,
max_legend: int = 12): max_legend: int = 20):
if weights is None or getattr(weights, "empty", True): if weights is None or getattr(weights, "empty", True):
print(f"[SKIP] Nessun peso per: {title}") print(f"[SKIP] Nessun peso per: {title}")
return return
@@ -2221,10 +2235,16 @@ if 'plot_portfolio_composition_fixed' not in globals():
if "Cash" in ordered: if "Cash" in ordered:
ordered = [c for c in ordered if c!="Cash"] + ["Cash"] ordered = [c for c in ordered if c!="Cash"] + ["Cash"]
if len(ordered) > max_legend: if len(ordered) > max_legend:
head, tail = ordered[:max_legend], ordered[max_legend:] head = ordered[:max_legend]
if "Cash" not in head and "Cash" in ordered:
head = head[:-1] + ["Cash"]
tail = [c for c in ordered if c not in head]
W_show = W[head].copy() W_show = W[head].copy()
if tail:
W_show["Altri"] = W[tail].sum(1) W_show["Altri"] = W[tail].sum(1)
ordered = head + ["Altri"] ordered = head + ["Altri"]
else:
ordered = head
else: else:
W_show = W[ordered].copy() W_show = W[ordered].copy()
cmap = plt.colormaps.get_cmap("tab20") cmap = plt.colormaps.get_cmap("tab20")
@@ -2245,7 +2265,7 @@ if 'plot_portfolio_composition_fixed' not in globals():
os.makedirs(folder, exist_ok=True) os.makedirs(folder, exist_ok=True)
fig.savefig(save_path, dpi=150, bbox_inches="tight") fig.savefig(save_path, dpi=150, bbox_inches="tight")
print(f"💾 Salvato: {os.path.abspath(save_path)}") print(f"💾 Salvato: {os.path.abspath(save_path)}")
plt.show() # Nessuna visualizzazione interattiva
def _build_weights_for_isins(base_isins_N, crypto_isin_N, wide_pnl): def _build_weights_for_isins(base_isins_N, crypto_isin_N, wide_pnl):
"""Costruisce i pesi TEORICI per Equal / Risk Parity / Aggressiva su un dato insieme di ISIN.""" """Costruisce i pesi TEORICI per Equal / Risk Parity / Aggressiva su un dato insieme di ISIN."""
@@ -2295,8 +2315,8 @@ for top_n in range(8, 16):
w_rp_act_N = make_active_weights(w_rp_N, wide_sig, renorm_to_1=False, add_cash=True, cash_label="Cash") w_rp_act_N = make_active_weights(w_rp_N, wide_sig, renorm_to_1=False, add_cash=True, cash_label="Cash")
# path di salvataggio # path di salvataggio
sp_eq = os.path.join(OUT_DIR, f"composition_equal_topN_{top_n}.png") sp_eq = OUT_DIR / f"composition_equal_topN_{top_n}.png"
sp_rp = os.path.join(OUT_DIR, f"composition_rp_topN_{top_n}.png") sp_rp = OUT_DIR / f"composition_rp_topN_{top_n}.png"
# plot + salvataggio (SOLO Equal e Risk Parity) # plot + salvataggio (SOLO Equal e Risk Parity)
plot_portfolio_composition_fixed(w_eq_act_N, f"Equal Weight (attivi + Cash) TopN={top_n}", sp_eq) plot_portfolio_composition_fixed(w_eq_act_N, f"Equal Weight (attivi + Cash) TopN={top_n}", sp_eq)

View File

@@ -31,14 +31,16 @@ from shared_utils import (
# PATH & OUTPUT # PATH & OUTPUT
# ============================================================================= # =============================================================================
BASE_DIR = Path(__file__).resolve().parent BASE_DIR = Path(__file__).resolve().parent
AUDIT_LOG_CSV = BASE_DIR / "trades_audit_log.csv" OUTPUT_DIR = BASE_DIR / "output"
PLOT_DIR = BASE_DIR / "plot"
AUDIT_LOG_CSV = OUTPUT_DIR / "trades_audit_log.csv"
CONNECTION_TXT = BASE_DIR / "connection.txt" CONNECTION_TXT = BASE_DIR / "connection.txt"
OUT_DAILY_CSV = BASE_DIR / "daily_returns_by_strategy.csv" OUT_DAILY_CSV = OUTPUT_DIR / "daily_returns_by_strategy.csv"
OUT_EQUITY_CSV = BASE_DIR / "equity_by_strategy.csv" OUT_EQUITY_CSV = OUTPUT_DIR / "equity_by_strategy.csv"
OUT_DEBUG_CSV = BASE_DIR / "debug_daily_by_strategy.csv" OUT_DEBUG_CSV = OUTPUT_DIR / "debug_daily_by_strategy.csv"
PLOT_EQUITY = BASE_DIR / "equity_by_strategy.png" PLOT_EQUITY = PLOT_DIR / "equity_by_strategy.png"
PLOT_DD = BASE_DIR / "drawdown_by_strategy.png" PLOT_DD = PLOT_DIR / "drawdown_by_strategy.png"
# Stored procedure # Stored procedure
SP_NAME_DEFAULT = "opt_RendimentoGiornaliero1_ALL" SP_NAME_DEFAULT = "opt_RendimentoGiornaliero1_ALL"
@@ -243,6 +245,8 @@ def rebuild_daily_from_log(audit: pd.DataFrame, returns_wide: pd.DataFrame) -> p
# MAIN # MAIN
# ============================================================================= # =============================================================================
def main(): def main():
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
PLOT_DIR.mkdir(parents=True, exist_ok=True)
if not AUDIT_LOG_CSV.exists(): if not AUDIT_LOG_CSV.exists():
raise FileNotFoundError("Missing trades_audit_log.csv") raise FileNotFoundError("Missing trades_audit_log.csv")
@@ -295,7 +299,6 @@ def main():
plt.legend() plt.legend()
plt.tight_layout() plt.tight_layout()
plt.savefig(str(PLOT_EQUITY), dpi=150) plt.savefig(str(PLOT_EQUITY), dpi=150)
plt.show()
plt.close() plt.close()
# Drawdown # Drawdown
@@ -308,7 +311,6 @@ def main():
plt.legend() plt.legend()
plt.tight_layout() plt.tight_layout()
plt.savefig(str(PLOT_DD), dpi=150) plt.savefig(str(PLOT_DD), dpi=150)
plt.show()
plt.close() plt.close()
print("Salvati:") print("Salvati:")

View File

@@ -69,15 +69,17 @@ RANKING_CONFIG = CONFIG.get("ranking", {})
SIGNALS_CONFIG = CONFIG.get("signals", {}) SIGNALS_CONFIG = CONFIG.get("signals", {})
BASE_DIR = Path(".") BASE_DIR = Path(".")
UNIVERSO_XLSX = BASE_DIR / "Universo per Trading System.xlsx" OUTPUT_DIR = BASE_DIR / "output"
# Universe now expected inside Input folder
UNIVERSO_XLSX = BASE_DIR / "Input" / "Universo per Trading System.xlsx"
CONNECTION_TXT = BASE_DIR / "connection.txt" CONNECTION_TXT = BASE_DIR / "connection.txt"
AUDIT_LOG_CSV = BASE_DIR / "trades_audit_log.csv" AUDIT_LOG_CSV = OUTPUT_DIR / "trades_audit_log.csv"
OPEN_TRADES_DIR = BASE_DIR / "open_trades" OPEN_TRADES_DIR = BASE_DIR / "open_trades"
DROPBOX_EXPORT_DIR = Path(r"C:\Users\Admin\Dropbox\Condivisa Lavoro\Segnali di trading su ETF") DROPBOX_EXPORT_DIR = Path(r"C:\Users\Admin\Dropbox\Condivisa Lavoro\Segnali di trading su ETF")
def _dated_signals_filename() -> Path: def _dated_signals_filename() -> Path:
date_prefix = pd.Timestamp.today().strftime("%Y%m%d") date_prefix = pd.Timestamp.today().strftime("%Y%m%d")
return BASE_DIR / f"{date_prefix}_signals.xlsx" return OUTPUT_DIR / f"{date_prefix}_signals.xlsx"
# Stored procedure / parametri DB # Stored procedure / parametri DB
SP_NAME_DEFAULT = str(require_value(DB_CONFIG, "stored_proc", "db")) SP_NAME_DEFAULT = str(require_value(DB_CONFIG, "stored_proc", "db"))
@@ -448,6 +450,7 @@ def save_open_trades(strategy: str, df: pd.DataFrame):
def append_audit_rows(rows: List[Dict]): def append_audit_rows(rows: List[Dict]):
if not rows: if not rows:
return return
ensure_dir(AUDIT_LOG_CSV.parent)
log = pd.DataFrame(rows) log = pd.DataFrame(rows)
if AUDIT_LOG_CSV.exists(): if AUDIT_LOG_CSV.exists():
old = pd.read_csv(AUDIT_LOG_CSV) old = pd.read_csv(AUDIT_LOG_CSV)
@@ -701,6 +704,7 @@ def update_positions_and_build_orders(universe: pd.DataFrame,
# ========================= # =========================
def main_run(run_date: Optional[dt.date] = None): def main_run(run_date: Optional[dt.date] = None):
today = run_date or dt.date.today() today = run_date or dt.date.today()
ensure_dir(OUTPUT_DIR)
# 1) Universo # 1) Universo
universe = load_universe(UNIVERSO_XLSX) universe = load_universe(UNIVERSO_XLSX)