Overall Statistics
Total Trades
304
Average Win
0.80%
Average Loss
-0.41%
Compounding Annual Return
1.661%
Drawdown
8.300%
Expectancy
0.059
Net Profit
3.489%
Sharpe Ratio
0.285
Probabilistic Sharpe Ratio
10.525%
Loss Rate
64%
Win Rate
36%
Profit-Loss Ratio
1.93
Alpha
0.008
Beta
0.034
Annual Standard Deviation
0.044
Annual Variance
0.002
Information Ratio
-0.602
Tracking Error
0.208
Treynor Ratio
0.365
Total Fees
$0.00
Estimated Strategy Capacity
$11000000.00
Lowest Capacity Asset
EURUSD 8G
from datetime import timedelta
from AlgorithmImports import *
#from params import tp,sl

STOCK = "EURUSD"

class DayOpenEma(QCAlgorithm):
    
    def Initialize(self):
        
        self.SetStartDate(2020, 2, 1)  # Set Start Date
        self.SetEndDate(2022, 3, 1)  # Set End Date
        
        self.SetCash(10000)  # Set Strategy Cash
        
        #self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)
        
        self.AddForex(STOCK, Resolution.Minute)
        
        #constructing 3h candles
        self.Consolidate(STOCK,timedelta(hours=3),self.OnThreeHourCandle)
        
        self.ema = self.EMA(STOCK,72,Resolution.Hour)

        # warming up the ema
        self.SetWarmup(72, Resolution.Hour)
            
        
        overlayPlot = Chart("Overlay Plot")
        self.AddChart(overlayPlot)
        overlayPlot.AddSeries(Series(STOCK, SeriesType.Line, 0))
        overlayPlot.AddSeries(Series("EMA",SeriesType.Line, 0))
        overlayPlot.AddSeries(Series("Day Open", SeriesType.Line, 0))
        overlayPlot.AddSeries(Series("BUY", SeriesType.Scatter, "$", Color.Green, ScatterMarkerSymbol.Triangle))
        overlayPlot.AddSeries(Series("SELL", SeriesType.Scatter, "$", Color.Red, ScatterMarkerSymbol.TriangleDown))
        
        #self.tp = float(self.GetParameter("tp",0.5))
        #self.sl = float(self.GetParameter("sl",0.25))
        #self.Log(self.tp)
        #self.Log(self.sl)
        self.tp = 80
        self.sl = 40

        # used to create daily open
        self.previous_day = 0
        self.day_open = 0
        
        # used to create bracket orders
        self.entry_ticket = None
        self.tp_order = None
        self.sl_order = None
    
        # used to cancel the limit order after 24h
        self.entry_ticket_time = None
        self.order_timer = False
        
        self.previous_close = None
        
    
    # this thing is here because quantconnect gets angry if the price
    # has more than 6 numbers in it
    # when it gets angry it does a manual conversion and somehow that 
    # conversion creates a bug when placing tp and sl orders
    @staticmethod
    def round_price(num):
        return round(num, 6-(len(str(num).split(".")[0])))

    # handles the orders
    def OnOrderEvent(self, orderEvent):
        if orderEvent.Status != OrderStatus.Filled:
            return
        
        if self.entry_ticket is not None:
    
            # if main order is filled
            if orderEvent.OrderId == self.entry_ticket.OrderId:
                
                # cancelling the 24h order expiration
                self.order_timer = False
                self.entry_ticket_time = None
                
                # getting price and quantity of the fill
                price = self.round_price(orderEvent.FillPrice)
                quantity = self.entry_ticket.Quantity
                #self.Log(str(self.Time))
                #self.Log("Fill Price: "+str(price))
                #self.Log("Price: "+str(self.Securities[STOCK].Price))

                
               
                self.Plot("Overlay Plot", "BUY", price-0.005)
                # placing TP and SL
                self.tp_order = self.LimitOrder(STOCK, -quantity, self.round_price(price*(1+self.tp/10000)))
                self.sl_order = self.StopMarketOrder(STOCK, -quantity, self.round_price(price*(1-self.sl/10000)))


            # if TP or SL order is filled
            else:
                price = orderEvent.FillPrice
                
                if orderEvent.FillQuantity > 0:
                    self.Plot("Overlay Plot", "BUY", price-0.005)
                else:
                    self.Plot("Overlay Plot", "SELL", price+0.005)
                
                self.Transactions.CancelOpenOrders(STOCK)
                self.entry_ticket = None          
        
        
    # cancels orders, closes positions and resets bools
    def ResetEverything(self):
    
        # cancelling the orders
        self.Transactions.CancelOpenOrders(STOCK)
        
        # resetting the orders
        self.entry_ticket = None
        self.tp_order = None
        self.sl_order = None

        
        # liquidating position
        if self.Portfolio.Invested:
            self.Liquidate(STOCK)
        
        # timer is stopped
        self.entry_ticket_time = None
        self.order_timer = False
        

    # every 3h candle
    def OnThreeHourCandle(self,data):
        
        
        # getting the data
        candleOpen = self.previous_close
        candleClose = round(self.Securities[STOCK].Price,5)
        self.previous_close = candleClose
        #self.Log("Price: "+str(self.Securities[STOCK].Price))

        # warming up the ema indicator
        if self.IsWarmingUp:
            return
        

        candleTime = self.Time
        ema = round(self.ema.Current.Value,5)
        dayOpen = self.day_open
        # position is 1 when we are long, 0 when nothing and -1 when short
        position = 0
        if self.entry_ticket is not None: 
            if self.entry_ticket.Quantity > 0:
                position = 1
            if self.entry_ticket.Quantity < 0:
                position = -1
        

        # creating day open
        if candleTime.day != self.previous_day:
            self.previous_day = candleTime.day
            self.day_open = candleOpen
            dayOpen = candleOpen


        # showing the data
        self.Plot("Overlay Plot", STOCK, candleClose)
        self.Plot("Overlay Plot", "EMA", ema)    
        self.Plot("Overlay Plot", "Day Open", dayOpen)
        #self.Plot("Overlay Plot", "Position", position)
        #self.Log("Candle Open:" + str(candleOpen))
        #self.Log("Candle Close:" + str(candleClose))
        #self.Log("EMA:" + str(ema))
        #self.Log("Day Open:" + str(self.dayOpen))


        # handles the order expiration
        if self.order_timer:
            # if todays day is greater than the day of the order
            # if today hour is equal or bigger than the hour of the order
            # means that 24 hours have passed from the order submission to now
            if candleTime.day > self.entry_ticket_time.day:
                if candleTime.hour >= self.entry_ticket_time.hour:
                    # the order is cancelled 
                    self.Transactions.CancelOpenOrders(STOCK)
                    # order is removed
                    self.entry_ticket = None
                    # order timer is cancelled
                    self.order_timer = False
                    self.entry_ticket_time = None
        

        
        if self.entry_ticket is None or position == -1:
            
            # short signal
            if candleClose > dayOpen and candleClose > ema:
                
                # SIGNAL CANDLE SHORT TRIGGERED
                
                self.ResetEverything()
                
                candleRange = round(Math.Abs(candleClose - candleOpen),5)
                limitPrice = self.round_price(candleClose - 0.25 * candleRange)
                quantity = self.CalculateOrderQuantity(STOCK, 1)
                
                self.entry_ticket = self.LimitOrder(STOCK, quantity, limitPrice)
                self.entry_ticket_time = candleTime
                self.order_timer = True
               
                
                #self.Log("Open: "+str(candleOpen))
                #self.Log("Close: "+str(candleClose))
                #self.Log("Candle Range: "+ str(candleRange))
                #self.Log("Long opened with limit: " + str(limitPrice))