Add 28 research scripts covering DCA simulation, momentum evaluation, Sharpe optimization, trend rider analysis, and US fundamentals exploration.
115 lines
4.2 KiB
Python
115 lines
4.2 KiB
Python
"""
|
|
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()
|