Overall Statistics
Total Trades
1322
Average Win
0.28%
Average Loss
-0.47%
Compounding Annual Return
10.867%
Drawdown
27.100%
Expectancy
0.355
Net Profit
237.945%
Sharpe Ratio
0.857
Loss Rate
15%
Win Rate
85%
Profit-Loss Ratio
0.60
Alpha
0.065
Beta
0.49
Annual Standard Deviation
0.13
Annual Variance
0.017
Information Ratio
0.118
Tracking Error
0.133
Treynor Ratio
0.228
Total Fees
$13859.87
import pandas as pd
import numpy as np
import scipy.optimize
import talib

### <summary>
### Basic template algorithm simply initializes the date range and cash. This is a skeleton
### framework you can use for designing an algorithm.
### </summary>
class BasicTemplateAlgorithm(QCAlgorithm):

    def Initialize(self):

        self.SetStartDate(2007,1,1)  #Set Start Date
        self.SetCash(1000000)           #Set Strategy Cash
        # Find more symbols here: http://quantconnect.com/data
        self.AddEquity("SPY")
        self.SetBenchmark('SPY')
        
        self.stocks = [ 'SPY',  # S&P 500
                        'EZU',  # MSCI EMU
                        'EWJ',  # Asia Pacifc ex-Japan
                        'EEM',  # Emerging Markets
                        'VNQ',  # US REITs
                        'RWX',  # International REITs
                        'IEF',  # US 10 Year Treasury
                        'TLT',  # US 30 Year Treasury
                        'DBC',  # Commodities
                        'GLD']  # Gold
        
        self.additional = [ 'QQQ',
                            'IWM',
                            'IJH',
                            'IJR',
                            'AGG',
                            'LQD',
                            'PSP'
                            ]
        
        self.stocks = self.stocks + self.additional                
                        
        for x in self.stocks:
            self.AddEquity(x, Resolution.Minute)
            #self.Securities[x].SetDataNormalizationMode(DataNormalizationMode.SplitAdjusted)

        self.Schedule.On(self.DateRules.MonthEnd("SPY"), self.TimeRules.BeforeMarketClose("SPY", 15), Action(self.allocate))
        
        self.Portfolio.MarginCallModel = MarginCallModel.Null  
        
        self.momentum_lookback = 126
        self.correlation_lookback = 126
        self.volatility_lookback = 21
        
        self.leverage = 1
    
    def calculate_portfolio_var(self, w, V):
        w = np.matrix(w)
        return (w*V*w.T)[0,0]    
        
    def allocate(self):
        
        # Find the recent returns generated by each symbol
        hist = self.History(self.stocks, 200, Resolution.Daily).unstack(level=0).close
        assets = []
        for x in self.stocks:
            ret = ((hist[x][-1]/hist[x][-self.momentum_lookback])) - 1
            assets.append([x, ret])
            
        sort = sorted(assets, key=lambda x: x[1], reverse=True)
        
        
        # Select the top half of best performing symbols
        symbols = []
        for sym in range(int(len(sort) / 2)):
            symbols.append(sort[sym][0])
        
        
        # Create a weighted covariance matrix using the previous 
        # 126 days for correlation and 21 days for volatility
        hist = hist[symbols].pct_change()[1:]
        vol = hist[-self.volatility_lookback:].std().values
        vol = vol.reshape(1, len(symbols)) * vol.reshape(len(symbols), 1)
        corr = hist[-self.correlation_lookback:].corr()
        covar_matrix = np.matrix(vol * corr)
        
        
        # Create an equally weighted portfolio as the initial allocation to optimise
        w0 = []
        for w in symbols:
            w0.append(1 / len(symbols))
        
            
        # Find the Minimum Variance Portfolio    
        cons = ({'type': 'eq', 'fun': lambda x:  np.sum(x) - 1.0})
        res = scipy.optimize.minimize(self.calculate_portfolio_var, w0, args=covar_matrix, method='SLSQP', constraints=cons)
        
        allocate = pd.Series(res.x, index=corr.index)
        
        
        # Liquidate any holdings that are currently invested and do not form part of the new 
        # monthly portfolio to ensure we don't exceed buying power on rebalance
        for stock in self.stocks:
            if stock not in symbols and self.Securities[stock].Invested:
                self.Liquidate(stock)
          
          
        # Purchase new Minimum Variance Portfolio  
        for stock in allocate.index.tolist():
            self.SetHoldings(stock, allocate.tolist()[allocate.index.tolist().index(stock)] * self.leverage)