Files
profitable-expert-advisor/polymarket/analytics/metrics.py
T
zhutoutoutousan 98a87a69ca Update
2026-02-13 08:03:25 +01:00

217 lines
6.7 KiB
Python

"""
Performance Metrics and Analytics
Calculates various performance metrics for strategies.
"""
from typing import Dict, List
import numpy as np
import pandas as pd
class PerformanceMetrics:
"""Calculate performance metrics from backtest results"""
@staticmethod
def calculate_sharpe_ratio(returns: List[float], risk_free_rate: float = 0.0) -> float:
"""
Calculate Sharpe ratio.
Args:
returns: List of daily returns
risk_free_rate: Annual risk-free rate
Returns:
Sharpe ratio
"""
if not returns:
return 0.0
returns_array = np.array(returns)
excess_returns = returns_array - (risk_free_rate / 365)
if returns_array.std() == 0:
return 0.0
sharpe = np.sqrt(365) * excess_returns.mean() / returns_array.std()
return sharpe
@staticmethod
def calculate_sortino_ratio(returns: List[float], risk_free_rate: float = 0.0) -> float:
"""
Calculate Sortino ratio (downside deviation only).
Args:
returns: List of daily returns
risk_free_rate: Annual risk-free rate
Returns:
Sortino ratio
"""
if not returns:
return 0.0
returns_array = np.array(returns)
excess_returns = returns_array - (risk_free_rate / 365)
# Calculate downside deviation
downside_returns = excess_returns[excess_returns < 0]
if len(downside_returns) == 0:
return 0.0
downside_std = np.std(downside_returns)
if downside_std == 0:
return 0.0
sortino = np.sqrt(365) * excess_returns.mean() / downside_std
return sortino
@staticmethod
def calculate_max_drawdown(equity_curve: List[float]) -> Dict[str, float]:
"""
Calculate maximum drawdown.
Args:
equity_curve: List of equity values over time
Returns:
Dictionary with max_drawdown, max_drawdown_percent, and drawdown_duration
"""
if not equity_curve:
return {'max_drawdown': 0.0, 'max_drawdown_percent': 0.0, 'drawdown_duration': 0}
equity_array = np.array(equity_curve)
peak = np.maximum.accumulate(equity_array)
drawdown = peak - equity_array
drawdown_percent = (drawdown / peak) * 100
max_dd = float(np.max(drawdown))
max_dd_percent = float(np.max(drawdown_percent))
# Calculate drawdown duration
in_drawdown = drawdown > 0
if np.any(in_drawdown):
# Count consecutive periods in drawdown
durations = []
current_duration = 0
for in_dd in in_drawdown:
if in_dd:
current_duration += 1
else:
if current_duration > 0:
durations.append(current_duration)
current_duration = 0
if current_duration > 0:
durations.append(current_duration)
max_duration = max(durations) if durations else 0
else:
max_duration = 0
return {
'max_drawdown': max_dd,
'max_drawdown_percent': max_dd_percent,
'drawdown_duration': max_duration
}
@staticmethod
def calculate_calmar_ratio(total_return: float, max_drawdown_percent: float) -> float:
"""
Calculate Calmar ratio (return / max drawdown).
Args:
total_return: Total return percentage
max_drawdown_percent: Maximum drawdown percentage
Returns:
Calmar ratio
"""
if max_drawdown_percent == 0:
return 0.0
return total_return / max_drawdown_percent
@staticmethod
def calculate_profit_factor(total_profit: float, total_loss: float) -> float:
"""
Calculate profit factor.
Args:
total_profit: Total profit
total_loss: Total loss (absolute value)
Returns:
Profit factor
"""
if total_loss == 0:
return 0.0 if total_profit == 0 else float('inf')
return abs(total_profit / total_loss)
@staticmethod
def calculate_expectancy(win_rate: float, avg_win: float, avg_loss: float) -> float:
"""
Calculate expectancy per trade.
Args:
win_rate: Win rate (0-1)
avg_win: Average winning trade
avg_loss: Average losing trade (absolute value)
Returns:
Expectancy
"""
return (win_rate * avg_win) - ((1 - win_rate) * avg_loss)
@staticmethod
def generate_report(backtest_results: Dict) -> str:
"""
Generate formatted performance report.
Args:
backtest_results: Results dictionary from backtest
Returns:
Formatted report string
"""
equity_curve = [point['equity'] for point in backtest_results.get('equity_curve', [])]
daily_returns = backtest_results.get('daily_returns', [])
# Calculate additional metrics
sharpe = PerformanceMetrics.calculate_sharpe_ratio(daily_returns)
sortino = PerformanceMetrics.calculate_sortino_ratio(daily_returns)
dd_metrics = PerformanceMetrics.calculate_max_drawdown(equity_curve)
report = f"""
{'='*70}
POLYMARKET BACKTEST REPORT
{'='*70}
Strategy: {backtest_results.get('strategy', 'Unknown')}
Period: {backtest_results.get('start_date')} to {backtest_results.get('end_date')}
INITIAL METRICS:
Initial Balance: ${backtest_results.get('initial_balance', 0):,.2f}
Final Equity: ${backtest_results.get('final_equity', 0):,.2f}
Total Return: {backtest_results.get('total_return', 0):.2f}%
TRADE STATISTICS:
Total Trades: {backtest_results.get('total_trades', 0)}
Winning Trades: {backtest_results.get('winning_trades', 0)}
Losing Trades: {backtest_results.get('losing_trades', 0)}
Win Rate: {backtest_results.get('win_rate', 0):.2f}%
PROFITABILITY:
Total Profit: ${backtest_results.get('total_profit', 0):,.2f}
Total Loss: ${backtest_results.get('total_loss', 0):,.2f}
Net Profit: ${backtest_results.get('net_profit', 0):,.2f}
Profit Factor: {backtest_results.get('profit_factor', 0):.2f}
RISK METRICS:
Maximum Drawdown: {dd_metrics['max_drawdown_percent']:.2f}%
Drawdown Duration: {dd_metrics['drawdown_duration']} periods
Sharpe Ratio: {sharpe:.2f}
Sortino Ratio: {sortino:.2f}
{'='*70}
"""
return report