Overall Statistics
Total Trades
48
Average Win
8.18%
Average Loss
-2.82%
Compounding Annual Return
35.529%
Drawdown
26.000%
Expectancy
1.276
Net Profit
119.694%
Sharpe Ratio
1.755
Probabilistic Sharpe Ratio
82.357%
Loss Rate
42%
Win Rate
58%
Profit-Loss Ratio
2.90
Alpha
0.309
Beta
-0.063
Annual Standard Deviation
0.172
Annual Variance
0.03
Information Ratio
0.674
Tracking Error
0.288
Treynor Ratio
-4.788
Total Fees
$48.00
from System.Drawing import Color
import numpy as np
import math as mt

class BB_SMA_MultiAsset_classic(QCAlgorithm):
    
    def Initialize(self):
        self.SetStartDate(2018, 1, 1)                       # the starting date for our backtest
        #self.SetEndDate(2019, 12, 1)                        # the ending date for our backtest (if no date, then it will test up until today)
        self.start_value = 5000
        self.SetCash(self.start_value)                      # the starting amount of our portfolio
        
        # create dictionaries for storing all of our indicators
        self.bb = {}                                        # create a bollingerband indicator for each stock and save it in the dictionary
        self.sma = {}                                       # create an SMA indicator
        self.rsi = {}                                       # create an RSI indicator
        self.vol = {}                                       # create a volume indicator
        self.vol_hist = {}                                  # create a dictionary for storing the historical volume for averaging
        self.hist_days = 7                                  # number of days to use for the SMA on the volume
        self.vol_avg = {}                                   # moving average of the volume
        self.starting_price = {}                            # for storing the starting price of each stock in our universe
        self.sto = {}                                       # for storing the stochastics data
        self.macd = {}                                      # for storing the MACD
        #self.macd_std = {}                                  # for storing the standard deviation of the MACD
         
        # add multiple stocks to the portfolio
        self.UniverseSettings.Resolution = Resolution.Daily                     # daily resolution data is emitted at midnight
        ticker_list = ['AAPL']#, 'QQQ','MSFT','AAPL']
        
        for ticker in ticker_list:
            symbol = self.AddEquity(ticker, Resolution.Daily).Symbol            # subscribe to the data for that stock
            
            # create our indicators
            self.bb[symbol] = self.BB(symbol, 5, 1, Resolution.Daily) 
            self.sma[symbol] = self.SMA(symbol, 20, Resolution.Daily)
            self.rsi[symbol] = self.RSI(symbol, 10, MovingAverageType.Simple)
            self.vol[symbol] = self.OBV(symbol, Resolution.Daily)
            self.vol_avg[symbol] = IndicatorExtensions.SMA(self.vol[symbol], 90)
            self.vol_hist[symbol] = []
            self.sto[symbol] = self.STO(symbol, 10, Resolution.Daily)
            self.macd[symbol] = self.MACD(symbol, 12, 26, 9, MovingAverageType.Exponential, Resolution.Daily)
            #self.macd_std[symbol] = IndicatorExtensions.Of(self.STD(9),self.macd[symbol])
            
        self.SetWarmUp(90)

        # set up a chart to display our buy and sell dates
        stockPlot = Chart('Equity')
        stockPlot.AddSeries(Series('Buy', SeriesType.Scatter, '$', Color.Green, ScatterMarkerSymbol.Triangle))
        stockPlot.AddSeries(Series('Sell', SeriesType.Scatter, '$', Color.Red, ScatterMarkerSymbol.TriangleDown))
        self.AddChart(stockPlot)
        
        stockPlot = Chart('MACD')
        stockPlot.AddSeries(Series('Histogram', SeriesType.Bar))
        self.AddChart(stockPlot)
        
    # this is for plotting the relative change of each stock and our portfolio value   
    def OnEndOfDay(self):
        current_portfolio = self.Portfolio.TotalPortfolioValue
        change_portfolio = (current_portfolio - self.start_value )/ self.start_value
        self.Plot("Percent Change","Portfolio Change", change_portfolio)
        
        for symbol in self.starting_price:
            start_price = self.starting_price[symbol]
            price = self.Securities[symbol].Price
            change_in_price = (price - start_price)/ start_price
            self.Plot("Percent Change", f"{symbol} Change", change_in_price)        
  

    def OnData(self, data):        
        
        if self.IsWarmingUp:
            return
        
        size_of_universe = len(self.bb)
        
        # record the starting price and invest in each equity equally
        for symbol in self.bb:
            # did we record the starting price yet?
            if symbol not in self.starting_price:
                # if we haven't gotten a starting price yet
                price = self.Securities[symbol].Price
                self.starting_price[symbol] = price
                self.SetHoldings(symbol, 1/size_of_universe)            # get invested
        

        for symbol in self.bb:
            
            bb = self.bb[symbol]
            sma = self.sma[symbol]
            rsi = self.rsi[symbol]
            vol = self.vol[symbol]
            price = self.Securities[symbol].Price
            volAvg_long = self.vol_avg[symbol]
            sto = self.sto[symbol]
            macd = self.macd[symbol]
            #macd_std = self.macd_std[symbol]
            
            # create a moving average of the volume
            self.vol_hist[symbol].append(vol.Current.Value)
            #self.Log(self.vol_hist[symbol])
            avg_vol = np.sum(self.vol_hist[symbol][-self.hist_days:])/len(self.vol_hist[symbol][-self.hist_days:])                  # calculate the moving average of the volume up to today
            avg_vol2 = np.sum(self.vol_hist[symbol][-(self.hist_days+1):-1])/len(self.vol_hist[symbol][-(self.hist_days+1):-1])      # calculate the moving average of the volume up to the day before
            
            
            if mt.isnan(avg_vol2):
                delta_avg_vol = 100
            else:
                delta_avg_vol = avg_vol - avg_vol2
                
            #self.Log(delta_avg_vol)

            # determine where the price is relative to the bollinger bands
            BBup = price > bb.UpperBand.Current.Value
            BBdown = price < bb.LowerBand.Current.Value
            
            # determine where the price is relative to the sma20
            SMAup = price > sma.Current.Value
            SMAdown = price < sma.Current.Value
            
            # determine where the volume is relative to the long moving average
            volUp = vol.Current.Value > volAvg_long.Current.Value
            delta_vol = vol.Current.Value - volAvg_long.Current.Value
            
            # determine where the volume is relative to the short moving average
            volUpShort = vol.Current.Value > avg_vol
            
            # find the zero crossing of the volume derivative
            volCross = (1000000 > delta_avg_vol) and (delta_avg_vol > -1000000) 
            
            # RSI indicates overbought or oversold
            RSIup = rsi.Current.Value > 80
            RSIdown = rsi.Current.Value < 50
            
            MACDup = macd.Histogram.Current.Value > 0.05
            MACDdown =  macd.Histogram.Current.Value < -0.05
            
            
            ######################################################################
            # This is the place to create logic for trading signals
            
            buy_signal = MACDup # and RSIdown #and SMAdown #and BBdown #and RSIdown and SMAdown     # downSMA20 and downBB
            sell_signal = MACDdown # and RSIup #and SMAup #and BBup #and RSIup and SMAup        # upSMA20 and upBB
            
            
            #self.Log(f"symbol: {symbol}, price: {price}, upper_bb: {bb.UpperBand.Current.Value}, lower_bb: {bb.LowerBand.Current.Value}, sma: {sma.Current.Value} ")
            #self.Log(f"volDerivative: {delta_avg_vol}")
            
            if buy_signal:
                if not self.Portfolio[symbol].Invested:
                    self.SetHoldings(symbol, 1/size_of_universe)        # if the price is below the 20-day moving average and it's below the lower bollinger band, buy the stock
                    self.Plot("Equity", 'Buy', price)
            if sell_signal:
                if self.Portfolio[symbol].Invested:
                    self.SetHoldings(symbol, 0)                         # if the price is above the upper SMA20 and above the upper bollinger band, sell it
                    self.Plot("Equity", 'Sell', price)
                    
                    
            self.Plot("Equity",'Price', price)
            #self.Plot("Equity",'SMA', sma.Current.Value)
            #self.Plot("Equity",'BB down', bb.LowerBand.Current.Value)
            #self.Plot("Equity",'BB up', bb.UpperBand.Current.Value)
            self.Plot("RSI",f"{symbol}",rsi.Current.Value)
            #self.Plot("Volume",f"{symbol}",vol.Current.Value)
            #self.Plot("Volume",f"{symbol}"+'avg_vol',avg_vol)
            #self.Plot("Volume",'long avg vol',volAvg_long.Current.Value)
            #self.Plot("Volume derivative",'delta',delta_avg_vol)
            #self.Plot("Volume delta",'volume delta',delta_vol)
            #self.Plot("Stochastics",'S',sto.FastStoch.Current.Value)
            #self.Plot("Stochastics",'K',sto.StochK.Current.Value)
            #self.Plot("Stochastics",'D',sto.StochD.Current.Value)
            #self.Plot("MACD","fast",macd.Fast.Current.Value)
            #self.Plot("MACD","slow",macd.Slow.Current.Value)
            self.Plot("MACD","signal",macd.Signal.Current.Value)
            self.Plot("MACD",'MACD',macd.Current.Value)
            self.Plot("MACD",'Histogram',macd.Histogram.Current.Value)
            #self.Plot("MACD",'STD',self.macd_std.Current.Value)
# class BB_SMA_DayTrading(QCAlgorithm):
    
#     def Initialize(self):
#         self.SetStartDate(2019, 6, 1)                       # the starting date for our backtest
#         #self.SetEndDate(2020, 6, 1)                        # the ending date for our backtest (if no date, then it will test up until today)
#         self.SetCash(5000)                                  # the starting amount of our portfolio
        
#         # add multiple stocks to the portfolio
#         self.UniverseSettings.Resolution = Resolution.Daily     # daily resolution data is emitted at midnight
#         ticker_list = ['SPY', 'QQQ','AAPL','MSFT']
#         for ticker in ticker_list:
#             symbols = [ Symbol.Create(ticker, SecurityType.Equity, Market.USA) ]
#         self.AddUniverseSelection( ManualUniverseSelectionModel(symbols) )
        
#         self.AddAlpha(PairsTradingAlphaModel())
#         self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel())
#         self.SetExecution(ImmediateExecutionModel())
        
#         # self.MarketOrder()
        
#         # self.mySma = self.SMA("", 12, )

# class PairsTradingAlphaModel(AlphaModel):           

#     def __init__(self):                  # only runs once at the beginning of the algorithm 
#         # self.mvg_avg = self.SMA(self.EQY, 20, Resolution.Daily)
#         # self.bolband = self.BB(self.EQY, 5, 1, MovingAverageType.Exponential, Resolution.Daily)
        
#         # {SPY : SPYSMA, AAPL: AAPLSMA}
#         # SPY.Price > SPYSMA?
        
#         # Dictionary keyed by symbol that holds SymbolData objects
        
#         self.symbolDataBySymbol = {}
        
        
#     def Update(self, algorithm, data):
#         """
#         This where our predictions are emitted
        
#         We must return a list of Insight objects - an Insight is a prediciton about some security
#         """
        
        
        
#         insights = []
        
#         # refers to the length of time our prediction/insight is valid
#         timeperiod = timedelta(minutes=5), ...
        
        
#         Insight.Price(symbol, time_period, insightdirection)
        
#         InsightDirection.Up
#         InsightDirection.Down
#         InsightDirection.Flat # liquidate signal
        
#         # {key1:value1, key2:value2}
#         # loop1 : key1, value1
#         # loop2: leu
#         for symbol, symbolData in self.symbolDataBySymbol.items():
            
#             sma = symbolData.sma_20
#             bb = symbolData.bb
#             # Bar data for our symbol - access close of the previous bar
#             price = data.Bars[symbol].Close
            
#             # Long signal
#             if price > sma.Current.Value or price < bb.LowerBand.Current.Value:
#                 insight = Insight.Price(symbol, timedelta(days=14), InsightDirection.Up)
#                 insights.append(insight)
                
#             else:
#                 # Liquidate insight
#                 insight = Insight.Price(symbol, timedelta(days=14), InsightDirection.Flat)
#                 insights.append(insight)
            
        
        
        
#         return insights

        
        
#     # Predefined function - it      
#     # It is fired each time a security is added or removed from our universe
#     # 
#     def OnSecuritiesChanged(self, algorithm, changes):
        
#         for security in changes.AddedSecurities:
#             symbol = security.Symbol
#             if symbol not in self.symbolDataBySymbol:
                
#                 self.symbolDataBySymbol[symbol] = SymbolData(symbol, algorithm)
                
#         # We can clean up as securities leave our universe       
#         # for security in changes.RemovedSecurities:
            
        
        
        
        

        

# class SymbolData:
    
    
#     def __init__(self, symbol, algorithm):
        
#         self.symbol = symbol # label for which security/symbol this container belongs to
        
#         self.algorithm = algorithm # this is a refernce to algorithm that is running our sttrategy
        
#         self.sma_20 = algorithm.SMA(symbol, 20, Resolution.Daily)
        
#         self.bb = algorithm.BB(symbol, 14, 2, Resolution.daily)
    
    
    
    
    
    
        
#         # # set up a bollinger bands indicator
#         # #var bb = BB(Symbol symbol, int period, decimal k, MovingAverageType movingAverageType = null, Resolution resolution = null, Func`2[Data.IBaseData,Decimal] selector = null)
#         # self.Bolband = self.BB(self.EQY, 5, 1, MovingAverageType.Exponential, Resolution.Daily)
        
#         # # set up an SMA20 indicator
#         # #var sma = SMA(Symbol symbol, int period, Resolution resolution = null, Func`2[Data.IBaseData,Decimal] selector = null)
#         # self.sma20 = self.SMA(self.EQY, 20, Resolution.Daily) 

#         # # need to give the indicators data before running the algorithm
#         # self.SetWarmUp(20)



#     # def OnData(self, data):         # this function is where trades happen. it executes based on the freqeuncy of data in the self.AddEquity function above
        
#     #     # if the indicators aren't ready, don't do anything
#     #     if (not self.sma20.IsReady) or (not self.Bolband.IsReady): return
        
#     #     # extract the current value of each indicator
#     #     sma20 = self.sma20.Current.Value
#     #     bb_max = self.Bolband.UpperBand.Current.Value
#     #     bb_min = self.Bolband.LowerBand.Current.Value
#     #     bb_avg = self.Bolband.MiddleBand.Current.Value
        
#     #     # determine where the price is relative to the sma20
#     #     upSMA20 = self.Securities[self.EQY].Price > sma20
#     #     downSMA20 = self.Securities[self.EQY].Price < sma20
        
#     #     # determine where the price is relative to the bollinger bands
#     #     upBB = self.Securities[self.EQY].Price > bb_max
#     #     downBB = self.Securities[self.EQY].Price < bb_min
        
        
#     #     if (upSMA20 or downBB):
#     #         self.SetHoldings(self.EQY, 1)       # if the price is above the 20-day moving average, or it's below the lower bollinger band, buy the stock
#     #     elif (downSMA20 or upBB):
#     #         self.SetHoldings(self.EQY, 0)        # otherwise sell it!
#     #     else:
#     #         self.SetHoldings(self.EQY, 0)
            
#     #     # plotting stuff
#     #     self.Plot("Equity",self.EQY,self.Securities[self.EQY].Price)
#     #     self.Plot("Equity","SMA20",sma20)
#     #     self.Plot("Equity","BB max",bb_max)
#     #     self.Plot("Equity","BB min",bb_min) 
#     #     self.Plot("Equity","BB mid",bb_avg)
        
#     #     self.Log(self.Securities[self.EQY].Price)
    
    

# # myCar = Car("Honda")

# # myCar.wheels --> 4

# # myCar.name --> "Honda"


# # class Car:
    
# #     def __init__(self, name_parameter):
# #         self.wheels = 4
# #         self.name = name_parameter