Overall Statistics
Total Trades
7224
Average Win
0.15%
Average Loss
-0.11%
Compounding Annual Return
9.071%
Drawdown
8.500%
Expectancy
0.215
Net Profit
133.095%
Sharpe Ratio
1.213
Loss Rate
48%
Win Rate
52%
Profit-Loss Ratio
1.35
Alpha
0.089
Beta
-0.004
Annual Standard Deviation
0.073
Annual Variance
0.005
Information Ratio
-0.032
Tracking Error
0.215
Treynor Ratio
-22.726
Total Fees
$8825.34
import statsmodels.api as sm
import numpy

MAX_LEVERAGE = 2.0

beta_samples = 252

alpha_samples = 15

volatility_samples = 5

MAX_VOLATILITY = 0.05

class BasicTemplateAlgorithm(QCAlgorithm):
    
    def __init__(self):
        
        pass
    
    def Initialize(self):
    
        self.SetCash(100000)
        
        self.SetStartDate(2008,1,1)
    
        #self.SetEndDate(2007,12,31)
        
        self.SPY = self.AddEquity("SPY", Resolution.Minute).Symbol
        self.QQQ = self.AddEquity("QQQ", Resolution.Minute).Symbol
        self.TLT = self.AddEquity("TLT", Resolution.Minute).Symbol
        
        #self.SetBrokerageModel(BrokerageName.InterativeBrokersBrokerage, AccountType.Margin)
        
        self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.AfterMarketOpen("SPY", 15), Action(self.Rebalance))
    
    def OnData(self, slice):
        
        pass
    
    def min_variance(self, returns):
    
        cov = numpy.cov(returns)
        vu = numpy.ones(cov.shape[0])
        if (numpy.linalg.det(cov) != 0):
            num = numpy.linalg.solve(cov,vu)
            den = numpy.dot(vu, num)
            w = num / den
            return w / numpy.sum(numpy.absolute(w))
        else:
            return None
            
    def Rebalance(self):
        
        try:

            self.spy_price = float(self.Securities["SPY"].Price)
            if (self.spy_price == 0):
                self.Log("Missing SPY price")
                return
        
            self.qqq_price = float(self.Securities["QQQ"].Price)
            if (self.qqq_price == 0):
                self.Log("Missing QQQ price")
                return
        
            self.tlt_price = float(self.Securities["TLT"].Price)
            if (self.tlt_price == 0):
                self.Log("Missing TLT price")
                return
    
            spy_prices = numpy.asarray([float(x.Price) for x in self.History("SPY",beta_samples,Resolution.Daily)] + [self.spy_price])
            spy_returns = numpy.nan_to_num(spy_prices[1:] / spy_prices[:-1] - 1.0)
            
            qqq_prices = numpy.asarray([float(x.Price) for x in self.History("QQQ",beta_samples,Resolution.Daily)] + [self.qqq_price])
            qqq_returns = numpy.nan_to_num(qqq_prices[1:] / qqq_prices[:-1] - 1.0)
    
            tlt_prices = numpy.asarray([float(x.Price) for x in self.History("TLT",beta_samples,Resolution.Daily)] + [self.tlt_price])
            tlt_returns = numpy.nan_to_num(tlt_prices[1:] / tlt_prices[:-1] - 1.0)
            
            if (len(spy_returns) == len(qqq_returns) == len(tlt_returns)):
                security_returns = numpy.vstack([qqq_returns,tlt_returns])
            else:
                self.Log("Missing data")    
                return
                    
            pfolio_weights = self.min_variance(security_returns[:,-alpha_samples:])
            if (pfolio_weights is None):
                self.Log("Min_variance error")    
                return
        
            pfolio_returns = numpy.nan_to_num((pfolio_weights[:,None]*security_returns).sum(axis=0))
        
            pfolio_beta = numpy.nan_to_num(sm.OLS(pfolio_returns[-beta_samples:],sm.add_constant(spy_returns[-beta_samples:], prepend=True)).fit().params[1])
        
            benchmark_split = -pfolio_beta / (1.0 + abs(pfolio_beta))
    
            pfolio_split = 1.0 / (1.0 + abs(pfolio_beta))
        
            pfolio_std = numpy.std(pfolio_split * pfolio_returns[-volatility_samples:] + benchmark_split * spy_returns[-volatility_samples:] )
        
            leverage = numpy.clip(MAX_VOLATILITY / (pfolio_std * numpy.sqrt(252)),0.0,MAX_LEVERAGE)
        
            self.SetHoldings("SPY", benchmark_split * leverage)
            self.Log("SPY  {0: 4.2f} % {1: 4.2f} ".format((benchmark_split * leverage) / MAX_LEVERAGE * 100.0, self.spy_price))
        
            self.SetHoldings("QQQ", pfolio_split * pfolio_weights[0] * leverage)
            self.Log("QQQ  {0: 4.2f} % {1: 4.2f} ".format((pfolio_split * pfolio_weights[0] * leverage) / MAX_LEVERAGE * 100.0, self.qqq_price))
        
            self.SetHoldings("TLT", pfolio_split * pfolio_weights[1] * leverage)
            self.Log("TLT {0: 4.2f} % {1: 4.2f} ".format((pfolio_split * pfolio_weights[1] * leverage) / MAX_LEVERAGE * 100.0, self.tlt_price))

            self.Log("CASH {0: 4.2f} %".format((MAX_LEVERAGE - leverage) / MAX_LEVERAGE * 100.0))
        
        except Exception as e: pass