portfolio_opt / tests /test_simulate.py
engineportf's picture
Upload folder using huggingface_hub
f624f5b verified
Raw
History Blame Contribute Delete
3.15 kB
import os # noqa: E402
import pandas as pd # noqa: E402
import numpy as np # noqa: E402
import unittest.mock as mock # noqa: E402
from core_engine import run_engine # noqa: E402
overrides = {
'setup_complete': True,
'tickers': ['AAPL', 'MSFT', 'GOOGL', 'TLT', 'GLD'],
'capital': 1000000.0,
'risk_input': 5,
'model': 1, # CAPM for fast execution
'allocation_engine': 1,
'tax_lt': 0.15,
'tax_st': 0.35,
'current_weights': {},
'live_execution_enabled': False,
'report_open_browser': False,
'headless': True,
'target_beta': (-1.0, 2.0)
}
def mock_read_sql(*args, **kwargs):
query = str(args[0]) if args else str(kwargs.get('sql', ''))
dates = pd.date_range(end=pd.Timestamp.today().normalize(), periods=1000, freq='B')
if "MAX(date)" in query:
return pd.DataFrame({'max_date': [pd.Timestamp.today().strftime('%Y-%m-%d')]})
if "daily_yields" in query:
yields = np.random.uniform(0.01, 0.05, size=1000)
return pd.DataFrame({'date': dates, 'yield_pct': yields})
seed = hash(str(args) + str(kwargs)) % (2**31)
rng = np.random.default_rng(seed)
# Inject market component so betas are around 0.5 - 1.0, satisfying constraints
market_rng = np.random.default_rng(42)
market_returns = market_rng.normal(0.0002, 0.01, len(dates))
ticker_returns = 0.8 * market_returns + rng.normal(0, 0.005, len(dates))
prices = np.exp(ticker_returns.cumsum()) * 100.0
# We must construct a DataFrame covering all requested tickers from params
params = kwargs.get('params', args[2] if len(args) > 2 else [])
if isinstance(params, dict):
tks = params.values()
else:
tks = params
tks = list(tks) if tks else ['AAPL', 'MSFT', 'GOOGL', 'TLT', 'GLD']
dfs = []
for tk in tks:
df_tk = pd.DataFrame({'date': dates, 'close_price': prices, 'ticker': tk})
dfs.append(df_tk)
return pd.concat(dfs) if dfs else pd.DataFrame(columns=['date', 'close_price', 'ticker'])
@mock.patch('cvxpy_engine.CVXPYOptimizationEngine._solution_violations', return_value=[])
@mock.patch('core_engine.fetch_data')
@mock.patch('core_engine.fetch_risk_free_rate', return_value=0.04)
@mock.patch('data.fetch_risk_free_series')
@mock.patch('pandas.read_sql', side_effect=mock_read_sql)
@mock.patch('core_engine.serve_report')
def test_simulate(mock_serve, mock_sql, mock_rfr_series, mock_rfr, mock_fetch, mock_violations):
"""End-to-end integration test with synthetic data"""
mock_fetch.return_value = overrides['tickers']
mock_rfr_series.return_value = pd.Series(dtype=float)
run_engine(overrides=overrides)
# If we reached this point without exceptions, the simulation pipeline executed successfully.
# Validate HTML report generation was triggered with valid content
mock_serve.assert_called_once()
# verify the output file exists instead since serve_report is called with no args
assert os.path.exists(os.path.join("output", "portfolio_report.html"))