mirror of
https://github.com/Xe138/AI-Trader.git
synced 2026-04-01 17:17:24 -04:00
feat: add period metrics calculation for date range queries
This commit is contained in:
50
api/routes/period_metrics.py
Normal file
50
api/routes/period_metrics.py
Normal file
@@ -0,0 +1,50 @@
|
||||
"""Period metrics calculation for date range queries."""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
def calculate_period_metrics(
|
||||
starting_value: float,
|
||||
ending_value: float,
|
||||
start_date: str,
|
||||
end_date: str,
|
||||
trading_days: int
|
||||
) -> dict:
|
||||
"""Calculate period return and annualized return.
|
||||
|
||||
Args:
|
||||
starting_value: Portfolio value at start of period
|
||||
ending_value: Portfolio value at end of period
|
||||
start_date: Start date (YYYY-MM-DD)
|
||||
end_date: End date (YYYY-MM-DD)
|
||||
trading_days: Number of actual trading days in period
|
||||
|
||||
Returns:
|
||||
Dict with period_return_pct, annualized_return_pct, calendar_days, trading_days
|
||||
"""
|
||||
# Calculate calendar days (inclusive)
|
||||
start_dt = datetime.strptime(start_date, "%Y-%m-%d")
|
||||
end_dt = datetime.strptime(end_date, "%Y-%m-%d")
|
||||
calendar_days = (end_dt - start_dt).days + 1
|
||||
|
||||
# Calculate period return
|
||||
if starting_value == 0:
|
||||
period_return_pct = 0.0
|
||||
else:
|
||||
period_return_pct = ((ending_value - starting_value) / starting_value) * 100
|
||||
|
||||
# Calculate annualized return
|
||||
if calendar_days == 0 or starting_value == 0 or ending_value <= 0:
|
||||
annualized_return_pct = 0.0
|
||||
else:
|
||||
# Formula: ((ending / starting) ** (365 / days) - 1) * 100
|
||||
annualized_return_pct = ((ending_value / starting_value) ** (365 / calendar_days) - 1) * 100
|
||||
|
||||
return {
|
||||
"starting_portfolio_value": starting_value,
|
||||
"ending_portfolio_value": ending_value,
|
||||
"period_return_pct": round(period_return_pct, 2),
|
||||
"annualized_return_pct": round(annualized_return_pct, 2),
|
||||
"calendar_days": calendar_days,
|
||||
"trading_days": trading_days
|
||||
}
|
||||
1
tests/api/__init__.py
Normal file
1
tests/api/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""API tests."""
|
||||
54
tests/api/test_period_metrics.py
Normal file
54
tests/api/test_period_metrics.py
Normal file
@@ -0,0 +1,54 @@
|
||||
"""Tests for period metrics calculations."""
|
||||
|
||||
from datetime import datetime
|
||||
from api.routes.period_metrics import calculate_period_metrics
|
||||
|
||||
|
||||
def test_calculate_period_metrics_basic():
|
||||
"""Test basic period metrics calculation."""
|
||||
metrics = calculate_period_metrics(
|
||||
starting_value=10000.0,
|
||||
ending_value=10500.0,
|
||||
start_date="2025-01-16",
|
||||
end_date="2025-01-20",
|
||||
trading_days=3
|
||||
)
|
||||
|
||||
assert metrics["starting_portfolio_value"] == 10000.0
|
||||
assert metrics["ending_portfolio_value"] == 10500.0
|
||||
assert metrics["period_return_pct"] == 5.0
|
||||
assert metrics["calendar_days"] == 5
|
||||
assert metrics["trading_days"] == 3
|
||||
# annualized_return = ((10500/10000) ** (365/5) - 1) * 100 = ~3422%
|
||||
assert 3400 < metrics["annualized_return_pct"] < 3450
|
||||
|
||||
|
||||
def test_calculate_period_metrics_zero_return():
|
||||
"""Test period metrics when no change."""
|
||||
metrics = calculate_period_metrics(
|
||||
starting_value=10000.0,
|
||||
ending_value=10000.0,
|
||||
start_date="2025-01-16",
|
||||
end_date="2025-01-16",
|
||||
trading_days=1
|
||||
)
|
||||
|
||||
assert metrics["period_return_pct"] == 0.0
|
||||
assert metrics["annualized_return_pct"] == 0.0
|
||||
assert metrics["calendar_days"] == 1
|
||||
|
||||
|
||||
def test_calculate_period_metrics_negative_return():
|
||||
"""Test period metrics with loss."""
|
||||
metrics = calculate_period_metrics(
|
||||
starting_value=10000.0,
|
||||
ending_value=9500.0,
|
||||
start_date="2025-01-16",
|
||||
end_date="2025-01-23",
|
||||
trading_days=5
|
||||
)
|
||||
|
||||
assert metrics["period_return_pct"] == -5.0
|
||||
assert metrics["calendar_days"] == 8
|
||||
# Negative annualized return
|
||||
assert metrics["annualized_return_pct"] < 0
|
||||
Reference in New Issue
Block a user