feat: add new day-centric results API endpoint

Implements new /results endpoint with day-centric data structure:
- Returns starting_position, daily_metrics, trades, and final_position
- Supports reasoning levels: none (default), summary, full
- Uses database helper methods from trading_days schema
- Replaces old positions-based endpoint

Changes:
- Created api/routes/results_v2.py with new endpoint
- Registered router in api/main.py
- Removed old /results endpoint (positions table)
- Added comprehensive integration tests

All tests pass.
This commit is contained in:
2025-11-03 23:43:52 -05:00
parent f770a2fe84
commit 93ba9deebb
4 changed files with 257 additions and 102 deletions

View File

@@ -0,0 +1,137 @@
import pytest
from fastapi.testclient import TestClient
from api.main import create_app
from api.database import Database
from api.routes.results_v2 import get_database
class TestResultsAPIV2:
@pytest.fixture
def client(self, db):
"""Create test client with overridden database dependency."""
# Create fresh app instance
app = create_app()
# Override the database dependency
app.dependency_overrides[get_database] = lambda: db
client = TestClient(app)
yield client
# Clean up
app.dependency_overrides.clear()
@pytest.fixture
def db(self, tmp_path):
"""Create test database with sample data."""
import importlib
migration_module = importlib.import_module('api.migrations.001_trading_days_schema')
create_trading_days_schema = migration_module.create_trading_days_schema
db_path = tmp_path / "test.db"
db = Database(str(db_path))
# Create schema
db.connection.execute("""
CREATE TABLE IF NOT EXISTS jobs (
job_id TEXT PRIMARY KEY,
status TEXT
)
""")
create_trading_days_schema(db)
# Insert sample data
db.connection.execute(
"INSERT INTO jobs (job_id, status) VALUES (?, ?)",
("test-job", "completed")
)
# Day 1
day1_id = db.create_trading_day(
job_id="test-job",
model="gpt-4",
date="2025-01-15",
starting_cash=10000.0,
starting_portfolio_value=10000.0,
daily_profit=0.0,
daily_return_pct=0.0,
ending_cash=8500.0,
ending_portfolio_value=10000.0,
reasoning_summary="First day summary",
total_actions=1
)
db.create_holding(day1_id, "AAPL", 10)
db.create_action(day1_id, "buy", "AAPL", 10, 150.0)
db.connection.commit()
return db
def test_results_without_reasoning(self, client, db):
"""Test default response excludes reasoning."""
response = client.get("/results?job_id=test-job")
assert response.status_code == 200
data = response.json()
assert data["count"] == 1
assert data["results"][0]["reasoning"] is None
def test_results_with_summary(self, client, db):
"""Test including reasoning summary."""
response = client.get("/results?job_id=test-job&reasoning=summary")
data = response.json()
result = data["results"][0]
assert result["reasoning"] == "First day summary"
def test_results_structure(self, client, db):
"""Test complete response structure."""
response = client.get("/results?job_id=test-job")
result = response.json()["results"][0]
# Basic fields
assert result["date"] == "2025-01-15"
assert result["model"] == "gpt-4"
assert result["job_id"] == "test-job"
# Starting position
assert "starting_position" in result
assert result["starting_position"]["cash"] == 10000.0
assert result["starting_position"]["portfolio_value"] == 10000.0
assert result["starting_position"]["holdings"] == [] # First day
# Daily metrics
assert "daily_metrics" in result
assert result["daily_metrics"]["profit"] == 0.0
assert result["daily_metrics"]["return_pct"] == 0.0
# Trades
assert "trades" in result
assert len(result["trades"]) == 1
assert result["trades"][0]["action_type"] == "buy"
assert result["trades"][0]["symbol"] == "AAPL"
# Final position
assert "final_position" in result
assert result["final_position"]["cash"] == 8500.0
assert result["final_position"]["portfolio_value"] == 10000.0
assert len(result["final_position"]["holdings"]) == 1
assert result["final_position"]["holdings"][0]["symbol"] == "AAPL"
# Metadata
assert "metadata" in result
assert result["metadata"]["total_actions"] == 1
def test_results_filtering_by_date(self, client, db):
"""Test filtering results by date."""
response = client.get("/results?date=2025-01-15")
results = response.json()["results"]
assert all(r["date"] == "2025-01-15" for r in results)
def test_results_filtering_by_model(self, client, db):
"""Test filtering results by model."""
response = client.get("/results?model=gpt-4")
results = response.json()["results"]
assert all(r["model"] == "gpt-4" for r in results)