Overall Statistics |
Total Trades 14 Average Win 0.07% Average Loss -0.13% Compounding Annual Return -2.029% Drawdown 0.700% Expectancy -0.774 Net Profit -0.688% Sharpe Ratio -3.238 Probabilistic Sharpe Ratio 0.020% Loss Rate 86% Win Rate 14% Profit-Loss Ratio 0.59 Alpha -0.016 Beta -0.003 Annual Standard Deviation 0.005 Annual Variance 0 Information Ratio -1.557 Tracking Error 0.128 Treynor Ratio 5.092 Total Fees $14.00 |
OrderTypeKeys = [ 'Market', 'Limit', 'StopMarket', 'StopLimit', 'MarketOnOpen', 'MarketOnClose', 'OptionExercise', ] OrderTypeCodes = dict(zip(range(len(OrderTypeKeys)), OrderTypeKeys))
from orderTypes import OrderTypeCodes from System.Drawing import Color class BollingerBand(QCAlgorithm): def Initialize(self): self.SetStartDate(2019, 6, 1) # Set Start Date self.SetEndDate(2019,10,1) self.SetCash(10000) # Set Strategy Cash # self.AddEquity("SPY", Resolution.Minute) self.ticker = 'MSFT' self.symbol = self.AddEquity(self.ticker, Resolution.Daily) self.tradeBarWindow = RollingWindow[TradeBar](2) # Initialize the Bollinger Band Indicator with 20 periods and 2 standard deviation parameters self.bollingerBand = self.BB(self.ticker,20,2) # Define flags variables to manage trades and control orders self.buyOrder = None self.profitOrderLong = None self.lossOrderLong = None self.sellOrder = None self.profitOrderShort = None self.lossOrderShort = None self.orderTicketLong = None self.orderTicketShort = None self.profitTicketLong = None self.profitTicketShort = None self.lossTicketLong = None self.lossTicketShort = None self.SetWarmUp(timedelta(20)) # Buy and sells order filled are marked with black diamond(buys) and green light square(sells) stockPlot = Chart('Stock Plot') #stockPlot.AddSeries(Series("Price", SeriesType.Line,0)) #stockPlot.AddSeries(Series("Buy", SeriesType.Scatter, 0)) #stockPlot.AddSeries(Series("Sell", SeriesType.Scatter, 0)) stockPlot.AddSeries(Series('Price', SeriesType.Line, '$', Color.Green)) stockPlot.AddSeries(Series('Buy', SeriesType.Scatter, '$', Color.Red, ScatterMarkerSymbol.Triangle)) stockPlot.AddSeries(Series('Sell', SeriesType.Scatter, '$', Color.Blue, ScatterMarkerSymbol.TriangleDown)) self.AddChart(stockPlot) self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.At(10, 0), self.trackOrder) def OnData(self, data): '''OnData event is the primary entry point for your algorithm. Each new data point will be pumped here. Arguments: data: Slice object keyed by symbol containing the stock data ''' if not self.bollingerBand.IsReady: return lowerBB = round(self.bollingerBand.LowerBand.Current.Value,2) upperBB = round(self.bollingerBand.UpperBand.Current.Value,2) price = self.Securities[self.ticker].Price self.tradeBarWindow.Add(data[self.ticker]) if data.ContainsKey(self.ticker): self.Plot('Stock Plot', 'Price', self.Securities[self.ticker].Price) if self.tradeBarWindow.IsReady and data.ContainsKey(self.ticker): todayBar = self.tradeBarWindow[0] yesterdayBar = self.tradeBarWindow[1] if todayBar and yesterdayBar: todayclose = round(todayBar.Close,2) todayhigh = round(todayBar.High,2) todayopen_ = round(todayBar.Open,2) todaylow = round(todayBar.Low,2) todayVolume = todayBar.Volume yesterdayclose = round(yesterdayBar.Close,2) yesterdayhigh = round(yesterdayBar.High,2) yesterdayopen_ = round(yesterdayBar.Open,2) yesterdaylow = round(yesterdayBar.Low,2) yesterdayVolume = yesterdayBar.Volume todayDate = todayBar.Time.date() yesterdayDate = yesterdayBar.Time.date() # Condition to buy: yesterday low should be above bolling band low, and today low should be below # bollinger band low. This means that the low price cross over the lower band of the bollinger band if (yesterdaylow < lowerBB) and (todaylow > lowerBB) and not self.Portfolio.Invested: price = round(self.Securities[self.ticker].Price,2) self.Debug('At {} send Market Order to buy {}'.format(self.Time.date(),self.ticker)) # self.Debug('Date {} TodayOpen: {} TodayHigh: {} TodayLow: {} TodayClose: {} Volume: {}'.format(todayDate,todayopen_,todayhigh,todaylow,todayclose,todayVolume)) # self.Debug('Yesterday {} YesterdayOpen: {} YesterdayHigh: {} YesterdayLow: {} YesterdayClose: {} Volume: {}'.format(yesterdayDate,yesterdayopen_,yesterdayhigh,yesterdaylow,yesterdayclose,yesterdayVolume)) # self.Debug('Low BB: {} High BB: {}'.format(lowerBB,upperBB)) tradeSize = 1000 quantity = int(round(tradeSize / self.Securities[self.ticker].Price,0)) tradePrice = round(self.Securities[self.ticker].Price * 0.99,2) self.orderTicketLong = self.MarketOrder(self.ticker,quantity) #,tradePrice) self.buyOrder = True if (yesterdayhigh < upperBB) and (todayhigh > upperBB) and not self.Portfolio.Invested: price = round(self.Securities[self.ticker].Price,2) self.Debug('At {} send Market Order to sell {}'.format(self.Time.date(),self.ticker)) # self.Debug('Date {} TodayOpen: {} TodayHigh: {} TodayLow: {} TodayClose: {} Volume: {}'.format(todayDate,todayopen_,todayhigh,todaylow,todayclose,todayVolume)) # self.Debug('Yesterday {} YesterdayOpen: {} YesterdayHigh: {} YesterdayLow: {} YesterdayClose: {} Volume: {}'.format(yesterdayDate,yesterdayopen_,yesterdayhigh,yesterdaylow,yesterdayclose,yesterdayVolume)) # self.Debug('Low BB: {} High BB: {}'.format(lowerBB,upperBB)) tradeSize = 1000 quantity = int(tradeSize / self.Portfolio[self.ticker].Price) tradePrice = round(self.Securities[self.ticker].Price * 0.99,2) self.orderTicketShort = self.MarketOrder(self.ticker,-quantity) #,tradePrice) self.sellOrder = True if self.orderTicketLong and self.Portfolio[self.ticker].IsLong and self.Portfolio[self.ticker].Invested: if self.orderTicketLong.Status == 3: #if self.OrderTicket.QuantityFilled > 0: self.Debug('At {} Long order for {} filled at price {}'.format(self.Time.date(),self.ticker,round(self.orderTicketLong.AverageFillPrice,2))) # Send limit order for profit and stop loss for long position quantity = self.orderTicketLong.QuantityFilled profitPrice = round(self.orderTicketLong.AverageFillPrice * 1.1,2) stopPrice = round(self.orderTicketLong.AverageFillPrice * 0.90,2) if todayhigh > upperBB and not self.profitOrderLong: self.Debug('At {} Today High {} is higher than upper Bollinger Band {}'.format(self.Time.date(),todayhigh,upperBB)) self.profitTicketLong = self.MarketOrder(self.ticker,-quantity,self.Time,'ProfitExitLong') self.profitOrderLong = True self.Debug('At {} Cancel stopOrder because exit profit is reached. Order price was {}'.format(self.Time.date(),self.orderTicketLong.AverageFillPrice)) #self.lossOrder.Cancel() #self.lossOrder = None #self.BuyOrder = None if not self.lossOrderLong: self.lossTicketLong = self.LimitOrder(self.ticker,-quantity,stopPrice,'StopLossLong') self.lossOrderLong = True if self.orderTicketShort and self.Portfolio[self.ticker].IsShort and self.Portfolio[self.ticker].Invested: if self.orderTicketShort.Status == 3: self.Debug('At {} Short order for {} filled at price {}'.format(self.Time.date(),self.ticker,round(self.orderTicketShort.AverageFillPrice,2))) # Send limit order for profit and stop loss for short position quantity = self.orderTicketShort.QuantityFilled profitPrice = round(self.orderTicketShort.AverageFillPrice * 0.9,2) stopPrice = round(self.orderTicketShort.AverageFillPrice * 1.1,2) if todaylow < lowerBB and not self.profitOrderShort: self.Debug('At {} Today Low {} is lower than lower Bollinger Band {}'.format(self.Time.date(),todaylow,lowerBB)) self.profitTicketShort = self.MarketOrder(self.ticker,-quantity,self.Time,'ProfitExitShort') self.profitOrderShort = True self.Debug('At {} Cancel stopOrder because exit profit is reached. Order price was {}'.format(self.Time.date(),self.orderTicketShort.AverageFillPrice)) if not self.lossOrderShort: quantity = self.orderTicketShort.QuantityFilled profitPrice = round(self.orderTicketShort.AverageFillPrice * 0.9,2) stopPrice = round((self.orderTicketShort.AverageFillPrice * 1.1),2) self.lossTicketShort = self.LimitOrder(self.ticker,-quantity,stopPrice,'StopLossShort') self.Debug('entra aca. Stop Price is {}'.format(stopPrice)) self.lossOrderShort = True def trackOrder(self): ''' This function track the order that was submitted(one order at a time) and cancel it if the order has more than 4 days in the market without being filled. This can take place when the asset starts trending and for long time or never, return to the limit order price that the buy order initially has ''' if self.buyOrder : if OrderTypeCodes[self.orderTicketLong.OrderType] == 'Limit' and (self.orderTicketLong.Time.date() + timedelta(days=4)) < self.Time.date(): self.Debug('Cancel Order because has more than 4 days in the market') self.orderTicketLong.Cancel() self.buyOrder = None if self.sellOrder : if OrderTypeCodes[self.orderTicketShort.OrderType] == 'Limit' and (self.orderTicketShort.Time.date() + timedelta(days=4)) < self.Time.date(): self.Debug('Cancel Order because has more than 4 days in the market') self.orderTicketShort.Cancel() self.sellOrder = None def OnEndOfDay(self): #self.Plot("Stock Plot", "MiddleBand", self.bollingerBand.MiddleBand.Current.Value) self.Plot("Stock Plot", "UpperBand", self.bollingerBand.UpperBand.Current.Value) self.Plot("Stock Plot", "LowerBand", self.bollingerBand.LowerBand.Current.Value) #overlayPlot = Chart("OverlayPlot") def OnOrderEvent(self, orderEvent): ''' Each time an order is submitted or filled, this function is triggered. ''' order = self.Transactions.GetOrderById(orderEvent.OrderId) # self.Debug("{0}: {1}: {2}: {3}: {4}".format(self.Time, OrderTypeCodes[order.Type], order.Quantity,order.Price,orderEvent)) #OrderTypeCodes[order.Type] == 'Limit' if self.orderTicketLong: if order.Status == 3 and order.Id == self.orderTicketLong.OrderId and (OrderTypeCodes[order.Type] == 'MarketOnOpen'): # and order.Quantity > 0: # self.BuyOrder = None self.Plot("Stock Plot", "Buy", order.Price) if self.orderTicketShort: if order.Status == 3 and order.Id == self.orderTicketShort.OrderId and (OrderTypeCodes[order.Type] == 'MarketOnOpen'): # and order.Quantity < 0: # self.BuyOrder = None self.Plot("Stock Plot", "Sell", order.Price) if self.profitOrderLong: if order.Status == 3 and order.Id == self.profitTicketLong.OrderId: # and order.Quantity < 0: #and ((OrderTypeCodes[order.Type] == 'Market') or (OrderTypeCodes[order.Type] == 'MarketOnOpen')) and self.Securities[self.ticker].Price > self.OrderTicket.AverageFillPrice : # Cancel stoploss order because profit order is filled self.Debug('At {} Cancel long stopOrder because profit is filled at Price {} greater than buy Price of {}'.format(self.Time.date(),order.Price,self.orderTicketLong.AverageFillPrice)) self.lossTicketLong.Cancel() self.Plot("Stock Plot", "Sell", order.Price) self.profitOrderLong = None self.lossOrderLong = None self.buyOrder = None if self.profitOrderShort: if order.Status == 3 and order.Id == self.profitTicketShort.OrderId: # and order.Quantity > 0: #and ((OrderTypeCodes[order.Type] == 'Market') or (OrderTypeCodes[order.Type] == 'MarketOnOpen')) and self.Securities[self.ticker].Price > self.OrderTicket.AverageFillPrice : # Cancel stoploss order because profit order is filled self.Debug('At {} Cancel short stopOrder because profit is filled at Price {} greater than buy Price of {}'.format(self.Time.date(),order.Price,self.orderTicketShort.AverageFillPrice)) self.lossTicketShort.Cancel() self.Plot("Stock Plot", "Sell", order.Price) self.lossOrderShort = None self.profitOrderShort = None self.sellOrder = None if self.lossOrderLong: if order.Status == 3 and order.Id == self.lossTicketLong.OrderId and (OrderTypeCodes[order.Type] == 'Limit') and order.Quantity < 0:# and ((OrderTypeCodes[order.Type] == 'StopMarket') or (OrderTypeCodes[order.Type] == 'MarketOnOpen')) and self.Securities[self.ticker].Price < self.OrderTicket.AverageFillPrice: self.Debug('At {} Cancel long profit Order because loss Order is filled at Price {} lower than buy Price of {}'.format(self.Time.date(),order.Price,self.orderTicketLong.AverageFillPrice)) self.lossOrderLong = None self.profitOrderLong = None self.buyOrder = None if self.profitTicketLong: self.profitTicketLong.Cancel() self.Plot("Stock Plot", "Sell", order.Price) if self.lossOrderShort: if order.Status == 3 and order.Id == self.lossTicketShort.OrderId and (OrderTypeCodes[order.Type] == 'Limit') and order.Quantity > 0 : # and ((OrderTypeCodes[order.Type] == 'StopMarket') or (OrderTypeCodes[order.Type] == 'MarketOnOpen')) and self.Securities[self.ticker].Price < self.OrderTicket.AverageFillPrice: self.Debug('At {} Cancel short profit Order because loss Order is filled at Price {} lower than buy Price of {}'.format(self.Time.date(),order.Price,self.orderTicketShort.AverageFillPrice)) self.Debug('Tag is {}'.format(self.lossTicketShort.Tag)) self.Debug("{}: {}: {}: {}: {}".format(self.Time, OrderTypeCodes[order.Type], order.Quantity,order.Price,orderEvent)) self.Debug('Fill Price {}'.format(self.lossTicketShort.AverageFillPrice)) self.lossOrderShort = None self.profitOrderShort = None self.sellOrder = None if self.profitTicketShort: self.profitTicketShort.Cancel() self.Plot("Stock Plot", "Buy", order.Price) #if self.profitOrder: # self.profitTicket.Cancel() # self.profitOrder = None