Overall Statistics
Total Trades
8
Average Win
0.31%
Average Loss
-2.34%
Compounding Annual Return
-0.198%
Drawdown
2.400%
Expectancy
-0.093
Net Profit
-1.126%
Sharpe Ratio
-0.279
Probabilistic Sharpe Ratio
0.034%
Loss Rate
20%
Win Rate
80%
Profit-Loss Ratio
0.13
Alpha
-0.002
Beta
0
Annual Standard Deviation
0.006
Annual Variance
0
Information Ratio
-0.68
Tracking Error
0.166
Treynor Ratio
-38.874
Total Fees
$0.00
import decimal as d
from System.Drawing import Color
class CalibratedVentralShield(QCAlgorithm):
    
#FINDINGS: Mondays tend to be bad for this technique. Skip trading mondays.
#FINDINGS2: Pinbars definitely add alpha.
#CHECK CURRENT SL LOGIC AND SEE IF IT CAN BE IMPROVED -- DONE
    #MAKE ENGULFING BARS DOUBLE AS WELL -- DONE
    #MAKE THE STOP LOSS TRAIL AND ADD A TP 
        #TRY ADDING MULTIPLE TP FIRST
    #CHECK IF PROFIT TARGET HAS A MINIMUM SUM OF X
    
    #NEXT THING TO DO: MAKE THE TP ORDERS UPDATE UPON THE FIRST ONE BEING HIT
    
    #if you dont have the two None below assigned to these tickets, the first iteration when checking for these two tickets you'll have an error
    stoplossOrderTicket = None
    stoplossOrderTicket_Two = None
    takeprofitOrderTicket_ForBuy = None
    takeprofitOrderTicket_Two_ForBuy = None
    takeprofitOrderTicket_ForSell = None
    takeprofitOrderTicket_Two_ForSell = None
    EntryMarketOrder = None
    
    def Initialize(self):
        self.SetStartDate(2014,12,10)  # Set Start Date
        self.SetEndDate(2020,8,27)
        self.SetCash(1000000)  # Set Strategy Cash
        
        #AddForex merely adds the security, but you cant refer to it later.
        self.eurusd = self.AddForex("EURUSD", Resolution.Daily, Market.Oanda).Symbol #But doing this you now can refer to self.symbol
        self.SetBrokerageModel(BrokerageName.OandaBrokerage)
        self.quant_filled = 0
        self.BollinBand = self.BB("EURUSD",20,2,MovingAverageType.Simple, Resolution.Daily)
        self.BBupper = RollingWindow[float](7)
        self.BBmid = RollingWindow[float](7)
        self.BBlower = RollingWindow[float](7)
        self.Bars = RollingWindow[QuoteBar](7) #FX need to use quotebar
        self.HammerWindow = RollingWindow[float](5)
        self.InvertedHammerWindow = RollingWindow[float](5)
        self.SetWarmUp(100)
        self.ATRindy = self.ATR("EURUSD",20,MovingAverageType.Simple, Resolution.Daily)
        self.ADXindy = self.ADX("EURUSD",20, Resolution.Daily)
        self.StaphLoss = 0
        self.NewStopPrice = 0
        
        self.engulfingcount = 0
        self.Hammer = self.CandlestickPatterns.Hammer(self.eurusd, Resolution.Daily)
        self.InvertedHammer = self.CandlestickPatterns.InvertedHammer(self.eurusd, Resolution.Daily)
        
        #initializing charts
        #Chart.Stacked: This means the chart will appear as a separate chart below the main equity chart.
        #Chart.Overlay: This means that the chart will be overlayed on top of the main equity chart.
        FXPlot = Chart("FX Plot", ChartType.Stacked)
        # On the Trade Plotter Chart we want 3 series: trades and price:
        FXPlot.AddSeries(Series("PriceOpen", SeriesType.Line, 0))
        FXPlot.AddSeries(Series("PriceClose", SeriesType.Line, 0))
        FXPlot.AddSeries(Series("Buy", SeriesType.Scatter, '$', Color.Green, ScatterMarkerSymbol.Triangle))
        FXPlot.AddSeries(Series("Sell", SeriesType.Scatter, '$', Color.Red, ScatterMarkerSymbol.TriangleDown))
        FXPlot.AddSeries(Series("BBUp",SeriesType.Line, '$'))
        FXPlot.AddSeries(Series("BBMid",SeriesType.Line, '$'))
        FXPlot.AddSeries(Series("BBLow",SeriesType.Line, '$'))
        self.AddChart(FXPlot)
        
        
    def OnData(self, data):
        '''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
            Arguments:
                data: Slice object keyed by symbol containing the stock data
        '''
        
        # Creates an indicator and adds to a rolling window when it is updated
        self.BBupper.Add(self.BollinBand.UpperBand.Current.Value)
        self.BBmid.Add(self.BollinBand.MiddleBand.Current.Value)
        self.BBlower.Add(self.BollinBand.LowerBand.Current.Value)
        self.HammerWindow.Add(self.Hammer.Current.Value)
        self.InvertedHammerWindow.Add(self.InvertedHammer.Current.Value)
        self.Bars.Add(data["EURUSD"]) #will store all quotebar related properties in Bars. U can use (var = self.Bars[0].Close) to find close or anything else
        self.ATR_Now = self.ATRindy.Current.Value
        self.ADX_Now = self.ADXindy.Current.Value

        if not self.ATRindy.IsReady:
            return
        if not self.BollinBand.IsReady:
            return
        if self.IsWarmingUp:
            return
        if self.Time.weekday()== 6 or self.Time.weekday()==0:
            return
        if self.ADX_Now > 29.99:
            return
        self.TradeSize = self.CalculateTradeSize()
        

            
        self.StaphLoss = max(self.Bars[0].High, self.Bars[1].High)
        
        #if close above BB and next bar is bearish, short
        if self.Portfolio[self.eurusd].Invested == False and (self.IsPrevBearish(self.Bars) and self.PrevClosedAboveBB(self.Bars , self.BBupper)):
            if self.IsBearishPinBar(self.Bars):
                self.TradeSize = self.TradeSize * 1.5
            if self.IsBearishEngulfing(self.Bars):
                self.TradeSize = self.TradeSize * 1.5
                self.Log("Bearish engulfing Occured On {}  {}".format(self.Time, self.engulfingcount))
            self.EntryMarketOrder = self.MarketOrder(self.eurusd, -self.TradeSize, True)
            if self.EntryMarketOrder.Status != OrderStatus.Filled:
                return
            #below OrderTickets return an OrderTicket object which can be used to update an order's properties, request that it is cancelled, or fetch its OrderId.
            self.stoplossOrderTicket = self.StopMarketOrder(self.eurusd, self.TradeSize, self.StaphLoss+ (self.ATR_Now/2))  
            self.takeprofitOrderTicket_ForSell = self.LimitOrder(self.eurusd, self.TradeSize/2 , self.BBmid[0])
            self.takeprofitOrderTicket_Two_ForSell = self.LimitOrder(self.eurusd, self.TradeSize/2  , self.BBlower[0])

            
        #if close below BB and next bar is bullish, long
        if self.Portfolio[self.eurusd].Invested == False and (self.IsPrevBullish(self.Bars) and self.PrevClosedBelowBB(self.Bars, self.BBlower)):
            if self.IsBullishPinBar(self.Bars):
                self.TradeSize = self.TradeSize * 1.5
            if self.IsBullishEngulfing(self.Bars):
                self.TradeSize = self.TradeSize * 1.5
                self.Log("Bullish engulfing Occured On {}  {}".format(self.Time, self.engulfingcount))
            self.EntryMarketOrder = self.MarketOrder(self.eurusd, self.TradeSize, True)
            if self.EntryMarketOrder.Status != OrderStatus.Filled:
                return
            self.stoplossOrderTicket = self.StopMarketOrder(self.eurusd, -self.TradeSize, self.Bars[1].Low - (self.ATR_Now/2))  
            self.takeprofitOrderTicket_ForBuy = self.LimitOrder(self.eurusd, -self.TradeSize/2, self.BBmid[0])
            self.takeprofitOrderTicket_Two_ForBuy = self.LimitOrder(self.eurusd, -self.TradeSize/2 , self.BBupper[0])
        
        #code for changing TP
        if self.Portfolio[self.eurusd].Invested == True:
            self.UpdateTakeProfits()
        
        
        #below is all for the charting

        self.Plot("FX Plot","PriceOpen", self.Bars[0].Open)
        self.Plot("FX Plot","PriceClose", self.Bars[0].Close)
        self.Plot("FX Plot", "BBUp", self.BollinBand.UpperBand.Current.Value)
        self.Plot("FX Plot", "BBMid",self.BollinBand.MiddleBand.Current.Value)
        self.Plot("FX Plot", "BBLow",self.BollinBand.LowerBand.Current.Value)
 
            
    #below are event handlers, as well as functions to determine the status of the EMA
    #-------------------------------------------------------------------------------
    def UpdateTakeProfits(self):
        if self.takeprofitOrderTicket_ForBuy is not None:
            if (self.takeprofitOrderTicket_ForBuy.Status == OrderStatus.Submitted) or \
            (self.takeprofitOrderTicket_ForBuy.Status == OrderStatus.UpdateSubmitted):
                self.updateFields = UpdateOrderFields()
                self.updateFields.LimitPrice = self.BBmid[0]
                self.takeprofitOrderTicket_ForBuy.Update(self.updateFields)
        if self.takeprofitOrderTicket_Two_ForBuy is not None:    
            if (self.takeprofitOrderTicket_Two_ForBuy.Status == OrderStatus.Submitted) or \
            (self.takeprofitOrderTicket_Two_ForBuy.Status == OrderStatus.UpdateSubmitted):
                self.updateFields = UpdateOrderFields()
                self.updateFields.LimitPrice = self.BBupper[0]
                self.takeprofitOrderTicket_Two_ForBuy.Update(self.updateFields)
        if self.takeprofitOrderTicket_ForSell is not None:        
            if (self.takeprofitOrderTicket_ForSell.Status == OrderStatus.Submitted) or \
            (self.takeprofitOrderTicket_ForSell.Status == OrderStatus.UpdateSubmitted):
                self.updateFields = UpdateOrderFields()
                self.updateFields.LimitPrice = self.BBmid[0]
                self.takeprofitOrderTicket_ForSell.Update(self.updateFields)
        if self.takeprofitOrderTicket_Two_ForSell is not None:        
            if (self.takeprofitOrderTicket_Two_ForSell.Status == OrderStatus.Submitted) or \
            (self.takeprofitOrderTicket_Two_ForSell.Status == OrderStatus.UpdateSubmitted):
                self.updateFields = UpdateOrderFields()
                self.updateFields.LimitPrice = self.BBlower[0]
                self.takeprofitOrderTicket_Two_ForSell.Update(self.updateFields)
        return
        
    def CalculateTradeSize(self):
        self.MaxLossAbsolute = self.Portfolio.Cash * 0.01
        return self.MaxLossAbsolute / (5 * self.ATR_Now)
        #use the below if trading a XXXJPY currency
        #return (self.MaxLossAbsolute / (5 * self.ATR_Now)) *100
    
    #IsPrevBearish and PrevClosedAbove BB are for checking if the prev bar is bearish, then the previous previous one has to poke thru and close above/below the BB    
    def IsPrevBearish(self, bar):
        return bar[0].Close < bar[0].Open
        
    def PrevClosedAboveBB(self, bar, upperBB):
        return bar[1].Close > upperBB[1]
        
    def IsPrevBullish(self,bar):
        return bar[0].Close > bar[0].Open
        
    def PrevClosedBelowBB(self, bar, lowerBB):
        return bar[1].Close < lowerBB[1]
        
    def IsBullishPinBar(self,bar):
        #total bar length has to be greater than 50 pips, total bar size more than 4x the body, and whole body is in the upper 30 percentile
        if bar[0].Open == bar[0].Close:
            return
        if ((bar[0].High - bar[0].Low) > 0.0049) and \
        ((bar[0].High - bar[0].Low)/abs(bar[0].Open - bar[0].Close) > 3.99) and \
        ((bar[0].Open - bar[0].Low)/(bar[0].High - bar[0].Low)) > 0.69 and \
        ((bar[0].Close - bar[0].Low)/(bar[0].High - bar[0].Low)) > 0.69:
            return True
        
    def IsBearishPinBar(self,bar):
        #total bar length has to be greater than 50 pips, total bar size more than 4x the body, and whole body is in the upper 30 percentile
        if bar[0].Open == bar[0].Close:
            return
        if ((bar[0].High - bar[0].Low) > 0.0049) and \
        ((bar[0].High - bar[0].Low)/abs(bar[0].Open - bar[0].Close) > 3.99) and \
        ((bar[0].Open - bar[0].Low)/(bar[0].High - bar[0].Low)) < 0.31 and \
        ((bar[0].Close - bar[0].Low)/(bar[0].High - bar[0].Low)) < 0.31:
            return True
        
    def IsBullishEngulfing(self,bar):
        #most recent bar has to be bigger only, the open doesnt matter, but close must engulf. Reason is sometimes open doesnt necessarily gap to cover
        #the previous bar. Also the first bar must have a minimum size of 35 pips
        if bar[0].Open == bar[0].Close:
            return
        if (bar[0].Close - bar[0].Open) > (bar[1].Open - bar[1].Close) and \
        (bar[0].Close > bar[1].Open) and \
        abs(bar[0].Open - bar[0].Close) > 0.0035:
            return True
        
    def IsBearishEngulfing(self,bar):
        if bar[0].Open == bar[0].Close:
            return
        if (bar[0].Open - bar[0].Close) > (bar[1].Close - bar[1].Open) and \
        (bar[0].Close < bar[1].Open) and\
        abs(bar[0].Open - bar[0].Close) > 0.0035:
            return True
        
    def OnOrderEvent(self, orderEvent):
        self.order_tick = self.Transactions.GetOrderById(orderEvent.OrderId)
        if orderEvent.Status != OrderStatus.Filled:
            return
        
        #below is for charting. Charting pottion has to come first before the check below for market orders to get plotted
        self.quant_filled = orderEvent.FillQuantity
        self.fill_price = orderEvent.FillPrice
        if self.fill_price > 0:
            if self.quant_filled > 0:
                self.Plot("FX Plot","Buy",self.fill_price)
            elif self.quant_filled < 0:
                self.Plot("FX Plot","Sell",self.fill_price)
        #charting ends
        

        self.NewStopPrice = self.EntryMarketOrder.AverageFillPrice



            
        #self.quant_filled = orderEvent.FillQuantity
        #self.fill_price = orderEvent.FillPrice