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
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
OrderTypeKeys = [
    'Market', 'Limit', 'StopMarket', 'StopLimit', 'MarketOnOpen',
    'MarketOnClose', 'OptionExercise',
]

OrderTypeCodes = dict(zip(range(len(OrderTypeKeys)), OrderTypeKeys))