113 lines
3.3 KiB
Python
113 lines
3.3 KiB
Python
"""
|
|
Tests for the PerformanceAnalyzer class.
|
|
"""
|
|
|
|
import pytest
|
|
from decimal import Decimal
|
|
import pandas as pd
|
|
import numpy as np
|
|
|
|
from tradingagents.backtest.performance import PerformanceAnalyzer
|
|
from tradingagents.backtest.exceptions import InsufficientDataError
|
|
|
|
|
|
@pytest.fixture
|
|
def analyzer():
|
|
"""Create performance analyzer."""
|
|
return PerformanceAnalyzer(risk_free_rate=Decimal("0.02"))
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_equity_curve():
|
|
"""Create sample equity curve."""
|
|
dates = pd.date_range(start='2022-01-01', end='2022-12-31', freq='D')
|
|
np.random.seed(42)
|
|
returns = np.random.normal(0.0005, 0.02, len(dates))
|
|
values = 100000 * np.exp(np.cumsum(returns))
|
|
|
|
return pd.Series(values, index=dates)
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_trades():
|
|
"""Create sample trades."""
|
|
return pd.DataFrame({
|
|
'ticker': ['AAPL'] * 10,
|
|
'pnl': np.random.normal(100, 500, 10),
|
|
'timestamp': pd.date_range(start='2022-01-01', periods=10, freq='M'),
|
|
})
|
|
|
|
|
|
def test_analyzer_initialization(analyzer):
|
|
"""Test analyzer initialization."""
|
|
assert analyzer is not None
|
|
assert analyzer.risk_free_rate == 0.02
|
|
|
|
|
|
def test_total_return_calculation(analyzer, sample_equity_curve):
|
|
"""Test total return calculation."""
|
|
total_return = analyzer._calculate_total_return(sample_equity_curve)
|
|
|
|
assert isinstance(total_return, float)
|
|
assert total_return >= -1.0 # Can't lose more than 100%
|
|
|
|
|
|
def test_volatility_calculation(analyzer, sample_equity_curve):
|
|
"""Test volatility calculation."""
|
|
returns = sample_equity_curve.pct_change().dropna()
|
|
volatility = analyzer._calculate_volatility(returns)
|
|
|
|
assert isinstance(volatility, float)
|
|
assert volatility >= 0
|
|
|
|
|
|
def test_sharpe_ratio_calculation(analyzer, sample_equity_curve):
|
|
"""Test Sharpe ratio calculation."""
|
|
returns = sample_equity_curve.pct_change().dropna()
|
|
volatility = analyzer._calculate_volatility(returns)
|
|
sharpe = analyzer._calculate_sharpe_ratio(returns, volatility)
|
|
|
|
assert isinstance(sharpe, float)
|
|
|
|
|
|
def test_max_drawdown_calculation(analyzer, sample_equity_curve):
|
|
"""Test maximum drawdown calculation."""
|
|
drawdowns = analyzer._calculate_drawdowns(sample_equity_curve)
|
|
max_dd = analyzer._calculate_max_drawdown(drawdowns)
|
|
|
|
assert isinstance(max_dd, float)
|
|
assert max_dd <= 0 # Drawdown should be negative
|
|
|
|
|
|
def test_trade_statistics(analyzer, sample_trades):
|
|
"""Test trade statistics calculation."""
|
|
stats = analyzer._calculate_trade_statistics(sample_trades)
|
|
|
|
assert 'total_trades' in stats
|
|
assert 'winning_trades' in stats
|
|
assert 'losing_trades' in stats
|
|
assert 'win_rate' in stats
|
|
|
|
assert stats['total_trades'] == len(sample_trades)
|
|
assert 0 <= stats['win_rate'] <= 1
|
|
|
|
|
|
def test_insufficient_data_error(analyzer):
|
|
"""Test that insufficient data raises error."""
|
|
empty_series = pd.Series([])
|
|
|
|
with pytest.raises(InsufficientDataError):
|
|
analyzer.analyze(empty_series, pd.DataFrame())
|
|
|
|
|
|
def test_monthly_returns(analyzer, sample_equity_curve):
|
|
"""Test monthly returns calculation."""
|
|
monthly_returns = analyzer.calculate_monthly_returns(sample_equity_curve)
|
|
|
|
assert isinstance(monthly_returns, pd.DataFrame)
|
|
assert not monthly_returns.empty
|
|
|
|
|
|
if __name__ == '__main__':
|
|
pytest.main([__file__, '-v'])
|