| Overall Statistics |
|
Total Orders 331 Average Win 0.75% Average Loss -0.22% Compounding Annual Return 8.256% Drawdown 25.100% Expectancy 0.813 Start Equity 10000 End Equity 12981.62 Net Profit 29.816% Sharpe Ratio 0.17 Sortino Ratio 0.136 Probabilistic Sharpe Ratio 12.572% Loss Rate 59% Win Rate 41% Profit-Loss Ratio 3.41 Alpha 0.021 Beta 0.424 Annual Standard Deviation 0.136 Annual Variance 0.019 Information Ratio 0.113 Tracking Error 0.149 Treynor Ratio 0.055 Total Fees $330.00 Estimated Strategy Capacity $6000000.00 Lowest Capacity Asset IJJ RWQR2INKP0TH Portfolio Turnover 2.50% |
#region imports
from AlgorithmImports import *
#endregion
from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Common")
from System import *
from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Data import *
from datetime import timedelta
import numpy as np
class ScheduledCAPM(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2022,1,1)
self.SetEndDate(2025,4,15)
self._cash = 10000
self.SetCash(self._cash)
self._tickers = ['IJR', 'IWM', 'IWF', 'IJH', 'IWD', 'ITOT', 'IVW',
'IWR', 'IWB', 'IVE', 'IWN' ,'IWP', 'IWS', 'IWO',
'IWV', 'IUSG','IBB', 'IUSV','IHI', 'IJS', 'IJJ',
'IJK', 'IYW', 'OEF']
self.symbols = [self.AddEquity(ticker).Symbol for ticker in self._tickers ]
self.spy = self.AddEquity("SPY", Resolution.Daily).Symbol
self.ivv = self.AddEquity("IVV", Resolution.Daily).Symbol
self._bench = self.AddEquity("IVV", Resolution.Daily).Symbol
self.lookback = 30
self.SetWarmup(31)
self.counter = 0
self._counter=0
self.reference = self.History(self.ivv, 5, Resolution.Daily)['close']
self._initialValue = self.reference.iloc[0]
self.Schedule.On(self.DateRules.MonthStart("SPY"), self.TimeRules.AfterMarketOpen(self.spy, 10), self.Rebalance)
self._portfolioValue =[self._cash]
self._stopLoss=float(self.GetParameter("stop_loss"))
self._num_alpha=int(self.GetParameter("number_of_stocks"))
self._num_beta = int(self.GetParameter("number_of_stocks_beta"))
self._numStocks= self._num_alpha + self._num_beta
self._weight = (1/(2*self._numStocks))
def Rebalance(self):
self.counter += 1
self.Debug(f"Counter : {self.counter}")
if self.IsWarmingUp:
return
if self.counter % 3 == 0:
history = self.History(
self.symbols + [self._bench],
self.lookback,
Resolution.Daily).close.unstack(level=0)
self.symbols_alpha = self.SelectSymbols_alphas(history)
self.symbols_beta = self.SelectSymbols_betas(history)
for holdings in self.Portfolio.Values:
symbol = holdings.Symbol
if symbol not in self.symbols_alpha and symbol not in self.symbols_beta and holdings.Invested:
self.Liquidate(symbol)
for symbol in self.symbols_alpha:
self.SetHoldings(symbol, self._weight)
for symbol in self.symbols_beta:
self.SetHoldings(symbol, self._weight)
self.SetHoldings("SPY", 1-self._weight)
def OnData(self,data):
self._portfolioValue.append(self.Portfolio.TotalPortfolioValue)
if self.Portfolio.TotalPortfolioValue - self._portfolioValue[self._counter] < -self._cash*self._stopLoss:
self.Liquidate()
self._counter += 1
def SelectSymbols_alphas(self, history):
alphas = dict()
_bench = history[self._bench].pct_change().dropna()
for symbol in self.symbols:
returns = history[symbol].pct_change().dropna()
bla = np.vstack([_bench, np.ones(len(returns))]).T
result = np.linalg.lstsq(bla , returns)
alphas[symbol] = result[0][1]
selected_alphas = sorted(alphas.items(), key=lambda x: x[1], reverse=True)[:self._numStocks-1]
return [x[0] for x in selected_alphas]
def SelectSymbols_betas(self, history):
betas = dict()
_bench = history[self._bench].pct_change().dropna()
for symbol in self.symbols:
returns = history[symbol].pct_change().dropna()
bla = np.vstack([_bench, np.ones(len(returns))]).T
result = np.linalg.lstsq(bla , returns)
betas[symbol]= result[0][0]
selected_betas = sorted(betas.items(), key=lambda x: x[1], reverse=True)[:self._numStocks-1]
return [x[0] for x in selected_betas]