from __future__ import annotations from datetime import datetime import pandas as pd import pytest from app.models.portfolio import LombardPortfolio from app.strategies.base import StrategyConfig @pytest.fixture def sample_portfolio() -> LombardPortfolio: """Research-paper baseline portfolio: 1M collateral, 600k loan, 460 spot, 75% LTV trigger.""" gold_ounces = 1_000_000.0 / 460.0 return LombardPortfolio( gold_ounces=gold_ounces, gold_price_per_ounce=460.0, loan_amount=600_000.0, initial_ltv=0.60, margin_call_ltv=0.75, ) @pytest.fixture def sample_strategy_config(sample_portfolio: LombardPortfolio) -> StrategyConfig: return StrategyConfig( portfolio=sample_portfolio, spot_price=sample_portfolio.gold_price_per_ounce, volatility=0.16, risk_free_rate=0.045, ) @pytest.fixture def sample_option_chain(sample_portfolio: LombardPortfolio) -> dict[str, object]: """Deterministic mock option chain around a 460 GLD reference price.""" spot = sample_portfolio.gold_price_per_ounce return { "symbol": "GLD", "updated_at": datetime(2026, 3, 21, 0, 0).isoformat(), "source": "mock", "calls": [ {"strike": round(spot * 1.05, 2), "premium": round(spot * 0.03, 2), "expiry": "2026-06-19"}, {"strike": round(spot * 1.10, 2), "premium": round(spot * 0.02, 2), "expiry": "2026-09-18"}, ], "puts": [ {"strike": round(spot * 0.95, 2), "premium": round(spot * 0.028, 2), "expiry": "2026-06-19"}, {"strike": round(spot * 0.90, 2), "premium": round(spot * 0.018, 2), "expiry": "2026-09-18"}, ], } @pytest.fixture def mock_yfinance_data(monkeypatch): """Patch yfinance in the data layer with deterministic historical close data.""" # Lazy import here to avoid side effects when the environment lacks Python 3.11's # datetime.UTC symbol used in the data_service module. from app.services import data_service as data_service_module history = pd.DataFrame({"Close": [458.0, 460.0]}, index=pd.date_range("2026-03-20", periods=2, freq="D")) class FakeTicker: def __init__(self, symbol: str) -> None: self.symbol = symbol def history(self, period: str, interval: str): return history.copy() class FakeYFinance: Ticker = FakeTicker monkeypatch.setattr(data_service_module, "yf", FakeYFinance()) return { "symbol": "GLD", "history": history, "last_price": 460.0, "previous_price": 458.0, } @pytest.fixture def mock_yfinance(mock_yfinance_data): """Compatibility alias for tests that request a yfinance fixture name.""" return mock_yfinance_data