""" DCA simulation: $10,000 initial + $5,000 every Feb & Aug from 2017. Uses SharpeBoostedEnsembleStrategy daily returns. """ from __future__ import annotations import os, sys import numpy as np import pandas as pd sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from strategies.ensemble_alpha import SharpeBoostedEnsembleStrategy import data_manager from universe import get_sp500 def main(): # Load data and generate daily returns tickers = get_sp500() data_manager.update("us", tickers) data = data_manager.load("us") strat = SharpeBoostedEnsembleStrategy() weights = strat.generate_signals(data) daily_rets = (weights * data.pct_change().fillna(0.0)).sum(axis=1) # Also compute SPY buy-and-hold for comparison spy_rets = data["SPY"].pct_change().fillna(0.0) # Trim to evaluation period start = "2016-04-01" end = "2026-05-13" daily_rets = daily_rets.loc[start:end] spy_rets = spy_rets.loc[start:end] # --- DCA simulation --- # Initial: $10,000 at start # Contributions: $5,000 on first trading day of Feb and Aug, starting 2017 # Find contribution dates (first trading day of each Feb and Aug from 2017) contrib_dates = [] for year in range(2017, 2027): for month in [2, 8]: target = pd.Timestamp(f"{year}-{month:02d}-01") # Find first trading day on or after target mask = daily_rets.index >= target if mask.any(): contrib_dates.append(daily_rets.index[mask][0]) # Filter to only dates within our data range contrib_dates = [d for d in contrib_dates if d <= daily_rets.index[-1]] print("=" * 70) print("DCA SIMULATION: SharpeBoostedEnsembleStrategy") print("=" * 70) print(f"Initial investment: $10,000 on {daily_rets.index[0].strftime('%Y-%m-%d')}") print(f"Contributions: $5,000 on first trading day of Feb & Aug (from 2017)") print(f"End date: {daily_rets.index[-1].strftime('%Y-%m-%d')}") print(f"Total contribution dates: {len(contrib_dates)}") print() # Simulate for both strategy and SPY for label, rets in [("Strategy", daily_rets), ("SPY (Buy & Hold)", spy_rets)]: portfolio_value = 10000.0 total_contributed = 10000.0 contrib_idx = 0 # Track milestones yearly_values = {} for i, date in enumerate(rets.index): # Apply daily return portfolio_value *= (1 + rets.iloc[i]) # Check if today is a contribution date if contrib_idx < len(contrib_dates) and date >= contrib_dates[contrib_idx]: portfolio_value += 5000.0 total_contributed += 5000.0 contrib_idx += 1 # Record year-end values if i == len(rets.index) - 1 or rets.index[i].year != rets.index[i + 1].year if i < len(rets.index) - 1 else True: yearly_values[date.year] = portfolio_value profit = portfolio_value - total_contributed roi = profit / total_contributed * 100 print(f"--- {label} ---") print(f" Total contributed: ${total_contributed:,.0f}") print(f" Final portfolio: ${portfolio_value:,.0f}") print(f" Total profit: ${profit:,.0f}") print(f" ROI on contributions: {roi:.1f}%") print(f" Multiple on capital: {portfolio_value/total_contributed:.2f}x") print() # Year-end snapshots print(f" Year-end portfolio values:") for year, val in sorted(yearly_values.items()): # How much contributed by that year contribs_by_year = 10000 + 5000 * len([d for d in contrib_dates if d.year <= year]) print(f" {year}: ${val:>12,.0f} (contributed: ${contribs_by_year:>8,.0f}, " f"gain: ${val - contribs_by_year:>+10,.0f})") print() # --- Monthly detail of contributions --- print("--- Contribution schedule ---") for i, d in enumerate(contrib_dates): print(f" {i+1:2d}. {d.strftime('%Y-%m-%d')} (${5000:,})") print(f" Total contributions (excl. initial): ${5000 * len(contrib_dates):,}") print(f" Total capital deployed: ${10000 + 5000 * len(contrib_dates):,}") if __name__ == "__main__": main()