| 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