Files
AI-Trader/tests/unit/test_pnl_calculator.py
2025-11-03 23:12:49 -05:00

153 lines
4.9 KiB
Python

import pytest
from agent.pnl_calculator import DailyPnLCalculator
class TestDailyPnLCalculator:
def test_first_day_zero_pnl(self):
"""First trading day should have zero P&L."""
calculator = DailyPnLCalculator(initial_cash=10000.0)
result = calculator.calculate(
previous_day=None,
current_date="2025-01-15",
current_prices={"AAPL": 150.0}
)
assert result["daily_profit"] == 0.0
assert result["daily_return_pct"] == 0.0
assert result["starting_portfolio_value"] == 10000.0
assert result["days_since_last_trading"] == 0
def test_positive_pnl_from_price_increase(self):
"""Portfolio gains value when holdings appreciate."""
calculator = DailyPnLCalculator(initial_cash=10000.0)
# Previous day: 10 shares of AAPL at $100, cash $9000
previous_day = {
"date": "2025-01-15",
"ending_cash": 9000.0,
"ending_portfolio_value": 10000.0, # 10 * $100 + $9000
"holdings": [{"symbol": "AAPL", "quantity": 10}]
}
# Current day: AAPL now $150
current_prices = {"AAPL": 150.0}
result = calculator.calculate(
previous_day=previous_day,
current_date="2025-01-16",
current_prices=current_prices
)
# New value: 10 * $150 + $9000 = $10,500
# Profit: $10,500 - $10,000 = $500
assert result["daily_profit"] == 500.0
assert result["daily_return_pct"] == 5.0
assert result["starting_portfolio_value"] == 10500.0
assert result["days_since_last_trading"] == 1
def test_negative_pnl_from_price_decrease(self):
"""Portfolio loses value when holdings depreciate."""
calculator = DailyPnLCalculator(initial_cash=10000.0)
previous_day = {
"date": "2025-01-15",
"ending_cash": 9000.0,
"ending_portfolio_value": 10000.0,
"holdings": [{"symbol": "AAPL", "quantity": 10}]
}
# AAPL drops from $100 to $80
current_prices = {"AAPL": 80.0}
result = calculator.calculate(
previous_day=previous_day,
current_date="2025-01-16",
current_prices=current_prices
)
# New value: 10 * $80 + $9000 = $9,800
# Loss: $9,800 - $10,000 = -$200
assert result["daily_profit"] == -200.0
assert result["daily_return_pct"] == -2.0
def test_weekend_gap_calculation(self):
"""Calculate P&L correctly across weekend."""
calculator = DailyPnLCalculator(initial_cash=10000.0)
# Friday
previous_day = {
"date": "2025-01-17", # Friday
"ending_cash": 9000.0,
"ending_portfolio_value": 10000.0,
"holdings": [{"symbol": "AAPL", "quantity": 10}]
}
# Monday (3 days later)
current_prices = {"AAPL": 120.0}
result = calculator.calculate(
previous_day=previous_day,
current_date="2025-01-20", # Monday
current_prices=current_prices
)
# New value: 10 * $120 + $9000 = $10,200
assert result["daily_profit"] == 200.0
assert result["days_since_last_trading"] == 3
def test_multiple_holdings(self):
"""Calculate P&L with multiple stock positions."""
calculator = DailyPnLCalculator(initial_cash=10000.0)
previous_day = {
"date": "2025-01-15",
"ending_cash": 8000.0,
"ending_portfolio_value": 10000.0,
"holdings": [
{"symbol": "AAPL", "quantity": 10}, # Was $100
{"symbol": "MSFT", "quantity": 5} # Was $200
]
}
# Prices change
current_prices = {
"AAPL": 110.0, # +$10
"MSFT": 190.0 # -$10
}
result = calculator.calculate(
previous_day=previous_day,
current_date="2025-01-16",
current_prices=current_prices
)
# AAPL: 10 * $110 = $1,100 (was $1,000, +$100)
# MSFT: 5 * $190 = $950 (was $1,000, -$50)
# Cash: $8,000 (unchanged)
# New total: $10,050
# Profit: $50
assert result["daily_profit"] == 50.0
def test_missing_price_raises_error(self):
"""Raise error if price data missing for holding."""
calculator = DailyPnLCalculator(initial_cash=10000.0)
previous_day = {
"date": "2025-01-15",
"ending_cash": 9000.0,
"ending_portfolio_value": 10000.0,
"holdings": [{"symbol": "AAPL", "quantity": 10}]
}
# Missing AAPL price
current_prices = {"MSFT": 150.0}
with pytest.raises(ValueError, match="Missing price data for AAPL"):
calculator.calculate(
previous_day=previous_day,
current_date="2025-01-16",
current_prices=current_prices
)