| Overall Statistics |
|
Total Trades 9 Average Win 2.23% Average Loss -1.98% Compounding Annual Return 5.265% Drawdown 8.500% Expectancy 0.594 Net Profit 4.836% Sharpe Ratio 0.522 Probabilistic Sharpe Ratio 30.467% Loss Rate 25% Win Rate 75% Profit-Loss Ratio 1.13 Alpha 0.081 Beta -0.189 Annual Standard Deviation 0.09 Annual Variance 0.008 Information Ratio -1.137 Tracking Error 0.115 Treynor Ratio -0.25 Total Fees $9.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(2017, 1, 1) # the starting date for our backtest
self.SetEndDate(2017, 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
# add multiple stocks to the portfolio
self.UniverseSettings.Resolution = Resolution.Daily # daily resolution data is emitted at midnight
ticker_list = ['AAPL']#['SPY', 'QQQ','AAPL','MSFT']
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.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)
# 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]
# 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 < 40
buy_signal = RSIdown #BBdown and SMAdown # downSMA20 and downBB
sell_signal = RSIup #BBup 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)# 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