Overall Statistics |
Total Orders 231 Average Win 0.32% Average Loss -0.25% Compounding Annual Return 42.161% Drawdown 16.800% Expectancy 0.744 Start Equity 1000000 End Equity 1740534.70 Net Profit 74.053% Sharpe Ratio 1.26 Sortino Ratio 1.755 Probabilistic Sharpe Ratio 68.910% Loss Rate 24% Win Rate 76% Profit-Loss Ratio 1.28 Alpha 0.107 Beta 1.39 Annual Standard Deviation 0.2 Annual Variance 0.04 Information Ratio 1.624 Tracking Error 0.091 Treynor Ratio 0.181 Total Fees $827.90 Estimated Strategy Capacity $990000.00 Lowest Capacity Asset IDV TTK3TR0978MD Portfolio Turnover 2.02% |
#region imports from AlgorithmImports import * #endregion # 0. LOADING OF THE LIBRARIES 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): # 1. INITIALIZATION def Initialize(self): self.SetStartDate(2022,9,1) self.SetEndDate(2024,3,31) self._cash = 1000000 self.SetCash(self._cash) # 2. PORTFOLIO CONSTRUCTION MODEL self._tickers = ['AGG', 'IWM', 'IAU', 'COMT', 'USMV', 'DGRO', 'QUAL', 'DVY', 'MTUM', 'VLUE', 'EFAV', 'EEMV', 'IDV', 'IQLT', 'IYW', 'IGF', 'IYH'] # 3. _benchING FOR REGRESSIONS FOR THE ALPHA MODEL 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.spy # 4. PARAMETRIZATION OF CLOCKS FOR ALPHA IMPLEMENTATION AND RISK MANAGEMENT self.lookback = 30 self.SetWarmup(31) self.counter = 0 self._secondCounter =0 # 5. REFERENCE FOR PLOTTING self.reference = self.History(self.ivv, 5, Resolution.Daily)['close'] self._initialValue = self.reference.iloc[0] # 6. EXECUTION MODEL (EVERY MONDAY) self.Schedule.On(self.DateRules.Every(DayOfWeek.Monday), self.TimeRules.AfterMarketOpen(self.spy, 10), self.Rebalance) # 7. PARAMETERS FOR RISK MANAGEMENT self._portfolioValue = [self._cash] self._drawdown = -0.03 # 8. _benchING FOR PLOTTING self.reference = self.History(self.spy, 10, Resolution.Daily)['close'] self._initialValue = self.reference.iloc[0] 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 # 9. ALPHA MODEL if symbol not in self.symbols_alpha and symbol not in self.symbols_beta and holdings.Invested: self.Liquidate(symbol) # 10. PORTFOLIO CONSTRUCTION MODEL for symbol in self.symbols_alpha: self.SetHoldings(symbol, 0.1) for symbol in self.symbols_beta: if symbol in self.symbols_alpha: self.SetHoldings(symbol, 0.2) else: self.SetHoldings(symbol, 0.1) self.SetHoldings("SPY", 1.0) def OnData(self,data): # 11. RISK MANAGEMENT self._secondCounter +=1 self._portfolioValue.append(self.Portfolio.TotalPortfolioValue) if self._secondCounter % 5 == 0: if (self._portfolioValue[-1]-self._portfolioValue[-5])/self._portfolioValue[-1] < self._drawdown: self.Liquidate() self.Plot("Relative Performance", "_bench", self._cash*self.Securities["SPY"].Close/self._initialValue) self.Plot("Relative Performance", "Total Portfolio Value", self.Portfolio.TotalPortfolioValue) # 12. AUXILIARY METHODS FOR CALCULATING ALPHAS AND BETAS 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)[:5] 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=False)[:5] return [x[0] for x in selected_betas]