Overall Statistics
Total Trades
3533
Average Win
0.49%
Average Loss
-0.45%
Compounding Annual Return
2.728%
Drawdown
48.400%
Expectancy
0.078
Net Profit
57.387%
Sharpe Ratio
0.217
Probabilistic Sharpe Ratio
0.029%
Loss Rate
49%
Win Rate
51%
Profit-Loss Ratio
1.10
Alpha
0.015
Beta
0.286
Annual Standard Deviation
0.14
Annual Variance
0.02
Information Ratio
-0.139
Tracking Error
0.177
Treynor Ratio
0.106
Total Fees
$0.00
Estimated Strategy Capacity
$22000000.00
Lowest Capacity Asset
CAT R735QTJ8XC9X
from AlgorithmImports import *
import math 

class PortfolioModelJGG(PortfolioConstructionModel):

    def __init__(self, rebalance = Resolution.Daily, portfolioBias = PortfolioBias.LongShort):
        self.portfolioBias = portfolioBias
        self.insightTargets = {}
        self.percentRisk = 1.0
        self.marginCallPercentBuffer = 25
        # If the argument is an instance of Resolution or Timedelta
        # Redefine rebalancingFunc
        rebalancingFunc = rebalance
        if isinstance(rebalance, int):
            rebalance = Extensions.ToTimeSpan(rebalance)
        if isinstance(rebalance, timedelta):
            rebalancingFunc = lambda dt: dt + rebalance
        if rebalancingFunc:
            self.SetRebalancingFunc(rebalancingFunc)

    def CreateTargets(self, algorithm, newInsights):
        '''Will determine the target percent for each insight
        Args:
            activeInsights: The active insights to generate a target for'''
        timeDateString = format(algorithm.Time)
        timeString = timeDateString.split()
        unbufferedTargets = {}
        portfolioTargets = []
        equity=algorithm.Portfolio.TotalPortfolioValue
        dollarRisk = equity*self.percentRisk/100
        #algorithm.Log(timeDateString)
        
        #try:
        #algorithm.Log('Prior Insight Targets before deleting expired')     
        for existingSymbol in self.insightTargets:
            for insightIndex in reversed(range(len(self.insightTargets[existingSymbol][1]))):
                algorithm.Log(str(existingSymbol)+' '+str(self.insightTargets[existingSymbol][0][insightIndex])+' '+str(self.insightTargets[existingSymbol][1][insightIndex]))
                if self.insightTargets[existingSymbol][1][insightIndex] < algorithm.UtcTime: 
                    del(self.insightTargets[existingSymbol][0][insightIndex])
                    del(self.insightTargets[existingSymbol][1][insightIndex])
                        
        #algorithm.Log('Prior Insight Targets after deleting expired')            
        #for existingSymbol in self.insightTargets:
            #for insightIndex in reversed(range(len(self.insightTargets[existingSymbol][1]))):            
                #algorithm.Log(str(existingSymbol)+' '+str(self.insightTargets[existingSymbol][0][insightIndex])+' '+str(self.insightTargets[existingSymbol][1][insightIndex]))
                        
        #algorithm.Log('New Insights')
        for insight in newInsights:
            #algorithm.Log(str(insight.Symbol)+' '+str(insight.CloseTimeUtc))
            volatilityRisk = insight.Magnitude/(1 + insight.Magnitude)                
            biasMultiplier = 1 if self.RespectPortfolioBias(insight) else 0
            # The block of code below only works for long positions!
            insightTarget = math.floor(biasMultiplier*dollarRisk/volatilityRisk/algorithm.Securities[insight.Symbol].Price) #simplify this calculation
            #algorithm.Log(str(biasMultiplier)+' '+str(volatilityRisk)+' '+str(algorithm.Securities[insight.Symbol].Price)+' '+str(insightTarget))
            if insight.Symbol not in self.insightTargets:
                self.insightTargets[insight.Symbol] = [[insightTarget],[insight.CloseTimeUtc]]
            elif len(self.insightTargets[insight.Symbol]) == 0:
                self.insightTargets[insight.Symbol] = [[insightTarget],[insight.CloseTimeUtc]]
            else:    
                self.insightTargets[insight.Symbol][0].append(insightTarget)
                self.insightTargets[insight.Symbol][1].append(insight.CloseTimeUtc) # add if statement in case insight.CloseTimeUtc doesn't exist
                    
        #algorithm.Log('Updated Insight Targets')
        #algorithm.Log('length: '+str(len(self.insightTargets)))
        #for symbol in self.insightTargets:
            #algorithm.Log(str(symbol)+' '+str(self.insightTargets[symbol][0])+' '+str(self.insightTargets[symbol][1]))
                    
        predictedMarginUsed = 0
        #algorithm.Log('unbuffered Portfolio Targets')
        for portfolioSymbol in self.insightTargets:
            if len(self.insightTargets[portfolioSymbol][0]) == 0:
                unbufferedTargets[portfolioSymbol] = 0
            else:
                unbufferedTargets[portfolioSymbol] = max(self.insightTargets[portfolioSymbol][0])
            #algorithm.Log(str(portfolioSymbol)+' '+str(unbufferedTargets[portfolioSymbol]))
            predictedMarginUsed += unbufferedTargets[portfolioSymbol]*algorithm.Securities[portfolioSymbol].Price/2
                
        #algorithm.Log('Buffered Portfolio Targets')
        bufferedMarginLimit = (100 - self.marginCallPercentBuffer)/100*(algorithm.Portfolio.TotalMarginUsed + algorithm.Portfolio.MarginRemaining)        
        for unbufferedSymbol in unbufferedTargets:
            if predictedMarginUsed > bufferedMarginLimit: 
                bufferedPositionSize = bufferedMarginLimit/predictedMarginUsed*unbufferedTargets[unbufferedSymbol]
            else:
                bufferedPositionSize = unbufferedTargets[unbufferedSymbol]
            portfolioTargets.append(PortfolioTarget(unbufferedSymbol,bufferedPositionSize))
            #algorithm.Log(str(unbufferedSymbol)+' '+str(bufferedPositionSize))
                
        for key in [key for key in self.insightTargets if self.insightTargets[key] == [[],[]]]: del self.insightTargets[key]
        #algorithm.Log('Insight Targets after clearing empty symbols')
        #for symbol in self.insightTargets:
            #algorithm.Log(str(symbol)+' '+str(self.insightTargets[symbol][0])+' '+str(self.insightTargets[symbol][1]))
        
        #except:
            #algorithm.Debug('Portfolio model error') #for '+str(insight.Symbol))
        return portfolioTargets

    def RespectPortfolioBias(self, insight):
        '''Method that will determine if a given insight respects the portfolio bias
        Args:
            insight: The insight to create a target for
        '''
        return self.portfolioBias == PortfolioBias.LongShort or insight.Direction == self.portfolioBias
from AlgorithmImports import *

class RsiDivergenceAlphaModelJGG(AlphaModel):

    def __init__(self, period = 14, resolution = Resolution.Daily):
        self.period = period
        self.resolution = resolution
        self.symbolDataBySymbol = {}
        self.openWindows = {}
        self.highWindows = {}
        self.lowWindows = {}
        self.closeWindows = {}
        self.rsiWindows = {}
        resolutionString = Extensions.GetEnumString(resolution, Resolution)
        self.Name = '{}({},{})'.format(self.__class__.__name__, period, resolutionString)

    def Update(self, algorithm, data):
        self.rsiOversold = float(algorithm.GetParameter("rsiOversold"))
        self.rsiOverbought = float(algorithm.GetParameter("rsiOverbought"))
        self.minHiccup = float(algorithm.GetParameter("minHiccup"))
        self.periodLength = int(algorithm.GetParameter("periodLength"))
        self.minRsiRetrace = float(algorithm.GetParameter("minRsiRetrace"))
        self.minRetracementRatio = float(algorithm.GetParameter("minRetracementRatio"))
        self.rsiPivotDelta = float(algorithm.GetParameter("rsiPivotDelta"))
        insights = []
        for symbol, symbolData in self.symbolDataBySymbol.items():
            timeDateString = format(algorithm.Time)
            timeString = timeDateString.split()            
            rsi = symbolData.RSI
            if data.ContainsKey(symbol) and data[symbol] is not None: # and timeString[1] == "16:00:00":
                self.openWindows[symbol].Add(data[symbol].Open)
                self.highWindows[symbol].Add(data[symbol].High)
                self.lowWindows[symbol].Add(data[symbol].Low)
                self.closeWindows[symbol].Add(data[symbol].Close)
                self.rsiWindows[symbol].Add(rsi.Current.Value)
            if rsi.IsReady: #and timeString[1] == "16:00:00":
                try:
                    self.rsiHiccup = self.rsiWindows[symbol][0] - self.rsiWindows[symbol][1]
                    if self.rsiWindows[symbol][1] < self.rsiOversold and self.rsiHiccup >= self.minHiccup:
                        for periodIter in [self.periodLength]:     #move this line!!!!!
                            highList = list(self.highWindows[symbol])
                            lowList = list(self.lowWindows[symbol])
                            closeList = list(self.closeWindows[symbol])
                            rsiList = list(self.rsiWindows[symbol])
                            recentMax = max(highList[0:periodIter-1])
                            recentMin = min(lowList[0:periodIter-1])
                            Mag = (recentMax - recentMin)/recentMin
                            #
                            Pivot1RSI = min(rsiList[3:self.divergenceLookback-1])
                            Pivot1Index = rsiList.index(Pivot1RSI,3,self.divergenceLookback-1)
                            Pivot2Index = 1  
                            if Pivot1Index - Pivot2Index >= self.minLength:
                                Pivot1Price = closeList[Pivot1Index]
                                rsiRetrace = max(rsiList[2:Pivot1Index-1]) - Pivot1RSI
                                rsiRetraceIndex = rsiList.index(rsiRetrace+Pivot1RSI,2,Pivot1Index-1)
                                retracePrice = closeList[rsiRetraceIndex]
                                Pivot2RSI = rsiList[Pivot2Index]
                                Pivot2Price = closeList[Pivot2Index]
                                RetracementRatio = (retracePrice-Pivot1Price)/(retracePrice-Pivot2Price)
                                #if symbol == "BEN R735QTJ8XC9X":
                                    #algorithm.Debug(str(Pivot1RSI) + ' ' + str(Pivot1Index) + ' ' + str(Pivot1Price) + ' ' + str(rsiRetrace) + ' ' + str(Pivot2RSI) + ' ' + str(Pivot2Price) + ' ' + str(RetracementRatio))
                                    #algorithm.Debug(str(len(rsiList)))
                                    #algorithm.Debug(rsiList[46])
                                    #algorithm.Debug(rsiList[56])
                                    #algorithm.Debug(closeList[46])
                                    #algorithm.Debug(closeList[56])
                                    #for testIndex in range(len(rsiList)):
                                        #algorithm.Debug(rsiList[testIndex])
                                if Pivot1RSI < Pivot2RSI - self.rsiPivotDelta and rsiRetrace > self.minRsiRetrace and Pivot2Price < Pivot1Price and RetracementRatio > self.minRetracementRatio:
                                    self.insightPeriod = Time.Multiply(Extensions.ToTimeSpan(self.resolution), periodIter)
                                    insights.append(Insight.Price(symbol, self.insightPeriod, InsightDirection.Up,Mag,None,None,None))
                                highList = []
                                lowList = []
                                closeList = []
                                rsiList = []
                    #if self.rsiWindows[symbol][1] > self.rsiOverbought and self.rsiHiccup <= -1*self.minHiccup:
                     #   for periodIter in [self.periodLength]:
                      #      highList = list(self.highWindows[symbol])
                       #     lowList = list(self.lowWindows[symbol])
                        #    recentMax = max(highList[0:periodIter-1])
                         #   recentMin = min(lowList[0:periodIter-1])
                          #  Mag = (recentMax - recentMin)/recentMin 
                           # #Mag = (recentMin - recentMax)/recentMax
                            #self.insightPeriod = Time.Multiply(Extensions.ToTimeSpan(self.resolution), periodIter)
                            #insights.append(Insight.Price(symbol, self.insightPeriod, InsightDirection.Up,Mag,None,None,None))
                            ##insights.append(Insight.Price(symbol, self.insightPeriod, InsightDirection.Down,Mag,None,None,None))
                            #highList = []
                            #lowList = []    
                except:
                    algorithm.Log('Alpha model error for '+str(symbol))
        return insights

    def OnSecuritiesChanged(self, algorithm, changes):
        
        self.divergenceLookback = 90 #int(algorithm.GetParameter("divergenceLookback"))
        self.minLength = int(algorithm.GetParameter("minLength"))
        # clean up data for removed securities
        symbols = [ x.Symbol for x in changes.RemovedSecurities ]
        if len(symbols) > 0:
            for subscription in algorithm.SubscriptionManager.Subscriptions:
                if subscription.Symbol in symbols:
                    self.symbolDataBySymbol.pop(subscription.Symbol, None)
                    subscription.Consolidators.Clear()
                
        # initialize data for added securities
        
        addedSymbols = [ x.Symbol for x in changes.AddedSecurities if x.Symbol not in self.symbolDataBySymbol]
        if len(addedSymbols) == 0: return
        
        #self.windowLength = max(self.divergenceLookback,self.period)
        history = algorithm.History(addedSymbols, self.period + 20, self.resolution) #self.windowLength + 20, self.resolution)
        
        for symbol in addedSymbols:
            algorithm.Securities[symbol].FeeModel = ConstantFeeModel(0)
            #algorithm.Securities[symbol].SetSlippageModel(ConstantSlippageModel(0))
            rsi = algorithm.RSI(symbol, self.period, MovingAverageType.Wilders, self.resolution)
            self.rsiWindows[symbol] = RollingWindow[float](20+self.divergenceLookback) #self.windowLength)
            self.openWindows[symbol] = RollingWindow[float](20+self.divergenceLookback) #self.windowLength)
            self.highWindows[symbol] = RollingWindow[float](20+self.divergenceLookback) #self.windowLength)
            self.lowWindows[symbol] = RollingWindow[float](20+self.divergenceLookback) #self.windowLength)
            self.closeWindows[symbol] = RollingWindow[float](20+self.divergenceLookback) #self.windowLength)
            for tuple in history.loc[symbol].itertuples():
                self.openWindows[symbol].Add(tuple.open)
                self.highWindows[symbol].Add(tuple.high)
                self.lowWindows[symbol].Add(tuple.low)
                self.closeWindows[symbol].Add(tuple.close)
                rsi.Update(tuple.Index, tuple.close)
                if rsi.IsReady:
                    self.rsiWindows[symbol].Add(rsi.Current.Value)
            self.symbolDataBySymbol[symbol] = SymbolData(symbol, rsi)

class SymbolData:
    def __init__(self, symbol, rsi):
        self.Symbol = symbol
        self.RSI = rsi
from Execution.ImmediateExecutionModel import ImmediateExecutionModel
from Execution.NullExecutionModel import NullExecutionModel
from Portfolio.EqualWeightingPortfolioConstructionModel import EqualWeightingPortfolioConstructionModel
from Portfolio.NullPortfolioConstructionModel import NullPortfolioConstructionModel
from Risk.MaximumDrawdownPercentPerSecurity import MaximumDrawdownPercentPerSecurity
from Selection.QC500UniverseSelectionModel import QC500UniverseSelectionModel
from RsiDivergenceAlphaModelJGG import RsiDivergenceAlphaModelJGG
from PortfolioModelJGG import PortfolioModelJGG
from AlgorithmImports import *

class SimpleRSITestQC500Universe(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(1998,3,1) # Set Start Date
        self.SetEndDate(2014,12,31) # Set End Date
        self.SetCash(100000) # Set Strategy Cash
        self.SetBenchmark("SPY")
        self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage)
        self.SetExecution(ImmediateExecutionModel())
        self.SetPortfolioConstruction(PortfolioModelJGG(Time.Multiply(Extensions.ToTimeSpan(Resolution.Daily), 1)))
        self.SetRiskManagement(NullRiskManagementModel())
        #symbols = [ Symbol.Create("SPY", SecurityType.Equity, Market.USA), Symbol.Create("GE", SecurityType.Equity, Market.USA), Symbol.Create("BA", SecurityType.Equity, Market.USA) ]
        #self.SetUniverseSelection(ManualUniverseSelectionModel(symbols))
        self.UniverseSettings.Resolution = Resolution.Daily
        self.AddUniverse(self.Universe.QC500)
        self.AddAlpha(RsiDivergenceAlphaModelJGG(resolution = Resolution.Daily))