stock/io_utils.py
2025-08-15 12:19:07 +02:00

38 lines
1.7 KiB
Python

from __future__ import annotations
import os, json
import pandas as pd
from typing import List
from metrics import portfolio_metrics
def ensure_dirs(root_dir: str, tickers: List[str]):
os.makedirs(root_dir, exist_ok=True)
for tk in tickers:
os.makedirs(os.path.join(root_dir, tk), exist_ok=True)
def save_outputs(root_dir: str, tickers: List[str], trades: pd.DataFrame, portfolio_eq: pd.DataFrame, cash: float):
ensure_dirs(root_dir, tickers)
if not trades.empty:
for tk, tdf in trades.groupby("ticker"):
tdf2 = tdf.copy()
tdf2["datetime"] = pd.to_datetime(tdf2["time"], unit="ms", utc=True)
tdf2.to_csv(os.path.join(root_dir, tk, f"{tk}_trades.csv"), index=False)
if not portfolio_eq.empty:
portfolio_eq["datetime"] = pd.to_datetime(portfolio_eq["time"], unit="ms", utc=True)
portfolio_eq.to_csv(os.path.join(root_dir, "portfolio_equity.csv"), index=False)
eq_series = portfolio_eq.set_index("time")["equity"] if not portfolio_eq.empty else pd.Series(dtype=float)
metrics = portfolio_metrics(eq_series) if not eq_series.empty else {"max_dd":0.0,"sharpe":0.0,"cagr":0.0}
summary = {
"ending_cash": round(cash, 2),
"n_open_positions": int(trades[trades["action"]=="OPEN"]["ticker"].nunique()) if not trades.empty else 0,
"n_trades_closed": int((trades["action"]=="CLOSE").sum()) if not trades.empty else 0,
"max_drawdown": metrics["max_dd"],
"sharpe_annualized": metrics["sharpe"],
"cagr": metrics["cagr"],
}
with open(os.path.join(root_dir, "portfolio_summary.txt"), "w", encoding="utf-8") as f:
json.dump(summary, f, indent=2, ensure_ascii=False)