Overall Statistics
Total Orders
663
Average Win
0.82%
Average Loss
-0.46%
Compounding Annual Return
21.126%
Drawdown
33.000%
Expectancy
0.539
Start Equity
10000
End Equity
21890.02
Net Profit
118.900%
Sharpe Ratio
0.679
Sortino Ratio
0.71
Probabilistic Sharpe Ratio
24.637%
Loss Rate
44%
Win Rate
56%
Profit-Loss Ratio
1.77
Alpha
0.081
Beta
0.847
Annual Standard Deviation
0.22
Annual Variance
0.048
Information Ratio
0.438
Tracking Error
0.155
Treynor Ratio
0.176
Total Fees
$480.00
Estimated Strategy Capacity
$620000.00
Lowest Capacity Asset
IAU T5QHGBE8K3TX
Portfolio Turnover
3.76%
#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(2020,1,1)
        self.SetEndDate(2024,1,31)  
        self._cash = 10000
        self.SetCash(self._cash)                    
        
        self._tickers =  ['AGG',
                        'IWM',
                        'IAU', 
                        'COMT',
                        'USMV',
                        'DGRO', 
                        'QUAL', 
                        'DVY', 
                        'MTUM',
                        'VLUE', 
                        'EFAV',
                        'EEMV', 
                        'IDV',
                        'IQLT', 
                        'IYW',
                        'IGF', 
                        'IYH']
         
       
        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
        
        self.lookback = 30
        self.SetWarmup(31)
        self.counter = 0
        
        self.reference = self.History(self.ivv, 5, Resolution.Daily)['close']
        self._initialValue = self.reference.iloc[0]
        
        self.Schedule.On(self.DateRules.Every(DayOfWeek.Monday), self.TimeRules.AfterMarketOpen(self.spy, 10), self.Rebalance)    
        
        
        
        self._portfolioValue = [self._cash]
        self._drawdown = -0.03
        
        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
                
            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, 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):
        self._portfolioValue.append(self.Portfolio.TotalPortfolioValue)
        
        if (self._portfolioValue[-1]-self._portfolioValue[-2])/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)
        
        
   
    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=True)[:5]
        return [x[0] for x in selected_betas]