Overall Statistics
Total Trades
1714
Average Win
0.50%
Average Loss
-0.44%
Compounding Annual Return
1.734%
Drawdown
29.500%
Expectancy
0.083
Net Profit
33.591%
Sharpe Ratio
0.202
Probabilistic Sharpe Ratio
0.004%
Loss Rate
50%
Win Rate
50%
Profit-Loss Ratio
1.15
Alpha
0.006
Beta
0.162
Annual Standard Deviation
0.072
Annual Variance
0.005
Information Ratio
-0.262
Tracking Error
0.154
Treynor Ratio
0.09
Total Fees
$0.00
Estimated Strategy Capacity
$65000000.00
Lowest Capacity Asset
DVN RNB1U9GCAQG5
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]
            if unbufferedSymbol == 'NYT R735QTJ8XC9X' and timeDateString == '2004-10-04 00:00:00':
                bufferedPositionSize = 0
            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]))
        #algorithm.Log(str(timeDateString)+' '+str(algorithm.Securities['NYT R735QTJ8XC9X'].Price))
        #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"))  # make this proportional to formation length?
        self.minRsiRetrace = float(algorithm.GetParameter("minRsiRetrace"))
        self.minRetracementRatio = float(algorithm.GetParameter("minRetracementRatio"))
        self.rsiPivotDelta = float(algorithm.GetParameter("rsiPivotDelta"))
        self.minLength = int(algorithm.GetParameter("minLength"))
        insights = []
        for symbol, symbolData in self.symbolDataBySymbol.items():
            timeDateString = format(algorithm.Time)
            timeString = timeDateString.split()            
            rsi = symbolData.RSI
            #minLow = symbolData.MINLOW
            #maxHigh = symbolData.MAXHIGH
            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:self.volatilitySample-1])
                            recentMin = min(lowList[0:self.volatilitySample-1])
                            Mag = (recentMax - recentMin)/recentMin*math.sqrt(periodIter/min(self.volatilitySample,len(highList)))
                            #Mag = math.sqrt(periodIter/20)*(maxHigh.Current.Value - minLow.Current.Value)/minLow.Current.Value
                            #
                            Pivot1RSI = min(rsiList[3:self.strategyLookback-1])
                            Pivot1Index = rsiList.index(Pivot1RSI,3,self.strategyLookback-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.strategyLookback = int(algorithm.GetParameter("strategyLookback"))
        self.volatilitySample = int(algorithm.GetParameter("volatilitySample"))
        # 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.strategyLookback + 90,self.volatilitySample)
        history = algorithm.History(addedSymbols, self.windowLength, 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)
            #minLow = algorithm.MIN(symbol, 20, self.resolution)
            #maxHigh = algorithm.MAX(symbol, 20, self.resolution)
            self.rsiWindows[symbol] = RollingWindow[float](self.windowLength)
            self.openWindows[symbol] = RollingWindow[float](self.windowLength)
            self.highWindows[symbol] = RollingWindow[float](self.windowLength)
            self.lowWindows[symbol] = RollingWindow[float](self.windowLength)
            self.closeWindows[symbol] = RollingWindow[float](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)#, minLow, maxHigh)

class SymbolData:
    def __init__(self, symbol, rsi):#, minLow, maxHigh):
        self.Symbol = symbol
        self.RSI = rsi
        #self.MINLOW = minLow
        #self.MAXHIGH = maxHigh
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))