Overall Statistics
Total Trades
33
Average Win
31.62%
Average Loss
-8.17%
Compounding Annual Return
24.110%
Drawdown
51.900%
Expectancy
2.043
Net Profit
937.398%
Sharpe Ratio
0.763
Probabilistic Sharpe Ratio
11.968%
Loss Rate
38%
Win Rate
62%
Profit-Loss Ratio
3.87
Alpha
0.285
Beta
-0.083
Annual Standard Deviation
0.361
Annual Variance
0.13
Information Ratio
0.39
Tracking Error
0.398
Treynor Ratio
-3.329
Total Fees
$3901.95
import pandas as pd
import numpy as np


class BasicRSItrading(QCAlgorithm):
    '''Basic template algorithm simply initializes the date range and cash'''

    def Initialize(self):
        self.SetStartDate(2010,1, 1)  #Set Start Date
        self.SetEndDate(2020,12,25)    #Set End Date
        self.SetCash(100000)           #Set Strategy Cash
        
        # Symbols to Trade
        self.equity_symbols = ['TQQQ', 'DJI', '^RUT', 'DUSL', "NDAQ"]
        
        # Store SymbolData objects with symbols as key
        self.Data = {}
        
        # window size
        self.window_size = 14
        
        # warm up period of size window_size before we start analysis
        self.SetWarmUp(self.window_size)
        
        # for each symbol create a new SymbolData object and add symbol to portfolio
        for symbol in self.equity_symbols:
            self.AddEquity(symbol, Resolution.Daily)
            self.Data[symbol] = SymbolData(symbol, self)
        
        # Keep track of symbol we are currently invested in and the number of max symbols it had (Need to revisit probs should not be max)
        self.current_invested_symbol = None
        self.max_num_signals = float('-inf')
        
        # define number of indicators in order to buy
        self.NUM_INDICATORS = 1


    def OnData(self, data):
        
        # dont do anything if were in the warming up period
        if self.IsWarmingUp:
            return
        
        # update indicator data for symbol we are invested in
        if self.current_invested_symbol:
            self.max_num_signals = self.Data[self.current_invested_symbol].Update()
        
        # go through the equities and try to buy it if it beats the current num signals seen
        temp_symbol = self.current_invested_symbol
        temp_num_signal = self.max_num_signals
        
        for symbol in self.equity_symbols:
            
            # dont need to update current invested symbol
            if symbol == self.current_invested_symbol:
                continue
            
            # get current symbol indicator count
            indicator_count = self.Data[symbol].Update()
            
            # if its bigger then we want to buy that symbol and sell our current symbol
            if indicator_count >= self.NUM_INDICATORS and indicator_count > self.max_num_signals:
                temp_symbol = symbol
                temp_num_signal =  indicator_count
        
        # if we found a different symbol to buy       
        if temp_symbol != self.current_invested_symbol and temp_num_signal >= self.NUM_INDICATORS:
            # sell previous symbol if we have one
            if temp_symbol:
                self.Log(f"Sold {temp_symbol} {self.Portfolio[temp_symbol].Quantity}, Indicators {temp_num_signal}")
                self.Liquidate(temp_symbol)
                
            # buy current one if we aren't already invested
            if self.Portfolio.Invested  == False:
                self.SetHoldings(temp_symbol, 1.0)
                self.Log(f"Bought {temp_symbol} {self.Portfolio[temp_symbol].Quantity}, Indicators {temp_num_signal}")
                self.current_invested_symbol = temp_symbol
                self.max_num_signals = temp_num_signal
                
        elif self.max_num_signals < self.NUM_INDICATORS:
            self.Liquidate(self.current_invested_symbol)

    # def OnEndOfAlgorithm(self):
    #     if self.Portfolio[self.current_invested_symbol].Invested:
    #         self.Liquidate(self.current_invested_symbol)
            
class SymbolData:
    
    # initialize SymbolData class
    def __init__(self, symbol, algorithm):
        # save symbol name and reference to 
        self.Symbol = symbol
        self.algorithm = algorithm
        
        # future implimentation 
        self.stop_loss = None
        self.window_size = 14
        
        # initialize indicators and data trackers
        self.close = algorithm.Identity(symbol)
        self.RSI_ind  = algorithm.RSI(symbol, self.window_size)
        self.ROC_ind  = algorithm.ROC(symbol, self.window_size)
        self.ADX_ind  = algorithm.ADX(symbol, self.window_size)
        self.AROON_ind = algorithm.AROON(symbol, self.window_size)
        
        # flag for when all indicators are ready to be read from
        self.isReady = self.RSI_ind.IsReady and self.ROC_ind.IsReady and self.ADX_ind.IsReady and self.AROON_ind.IsReady and self.close.IsReady
        
    # function to update isReady flag and return number of indicators
    def Update(self):
        self.isReady = self.RSI_ind.IsReady and self.ROC_ind.IsReady and self.ADX_ind.IsReady and self.AROON_ind.IsReady and self.close.IsReady
        return self.TryEnter()
        
    # function to calculate number of indicators and return them
    def TryEnter(self):
        if self.algorithm.Portfolio[self.Symbol].Quantity > 0:
            return False
            
        # self.algorithm.Log(f"Calculating Indicators....")
        num_buy_singals = 0
        
        if self.RSI_ind.Current.Value > 30:
            num_buy_singals += 1
        
        if self.ROC_ind.Current.Value > .10:
            num_buy_singals += 1
            
        if self.ADX_ind.Current.Value > 25 and self.ADX_ind.PositiveDirectionalIndex > self.ADX_ind.NegativeDirectionalIndex:
            num_buy_singals += 1
            
        if self.AROON_ind.Current.Value > 10:
            num_buy_singals += 1
        
        # self.algorithm.Log(f"Calculated Indicators -> {num_buy_singals}\n")
        
        return num_buy_singals