quant-web/metrics.py
epistemophiliac 627b2326df Add Python strategy engine, parameter optimization, and faster Docker builds.
Support builtin and custom generate_signals strategies with SQLite persistence, exhaustive grid scans (VectorBT comb optimization for MA crossover), professional backtest/optimize UI, and split harvester/app requirements with BuildKit pip cache.
2026-06-19 01:29:28 -04:00

62 lines
1.6 KiB
Python

"""Portfolio metric helpers shared by engine and optimizers."""
from __future__ import annotations
from typing import Any
import pandas as pd
import vectorbt as vbt
def safe_float(value: Any) -> float:
try:
if value is None or (isinstance(value, float) and value != value):
return 0.0
return float(value)
except (TypeError, ValueError):
return 0.0
def run_from_signals(
close: pd.Series,
entries: pd.Series,
exits: pd.Series,
init_cash: float,
fees: float,
params: dict[str, Any],
metric: str = "sharpe_ratio",
) -> dict[str, Any]:
portfolio = vbt.Portfolio.from_signals(
close,
entries=entries,
exits=exits,
init_cash=init_cash,
fees=fees,
freq="1D",
)
stats = portfolio.stats()
sharpe = safe_float(stats.get("Sharpe Ratio"))
sortino = safe_float(stats.get("Sortino Ratio"))
max_dd = safe_float(stats.get("Max Drawdown [%]")) / 100.0
total_return = safe_float(stats.get("Total Return [%]")) / 100.0
win_rate = safe_float(stats.get("Win Rate [%]")) / 100.0
total_trades = int(stats.get("Total Trades", 0) or 0)
score_map = {
"sharpe_ratio": sharpe,
"sortino_ratio": sortino,
"total_return": total_return,
"max_drawdown": -max_dd,
}
return {
**params,
"sharpe_ratio": sharpe,
"sortino_ratio": sortino,
"max_drawdown": max_dd,
"total_return": total_return,
"win_rate": win_rate,
"total_trades": total_trades,
"score": score_map.get(metric, sharpe),
}