Overall Statistics
Total Trades
36
Average Win
0.45%
Average Loss
-1.19%
Compounding Annual Return
-19.317%
Drawdown
4.400%
Expectancy
-0.083
Net Profit
-1.845%
Sharpe Ratio
-1.357
Probabilistic Sharpe Ratio
20.981%
Loss Rate
33%
Win Rate
67%
Profit-Loss Ratio
0.38
Alpha
-0.081
Beta
-0.183
Annual Standard Deviation
0.114
Annual Variance
0.013
Information Ratio
-3.176
Tracking Error
0.174
Treynor Ratio
0.843
Total Fees
$0.00
from datetime import datetime,timedelta
import numpy as np

class ReversalAlpha(QCAlgorithm):

    def Initialize(self):
        
        self.SetStartDate(2020, 11, 10)
        self.SetEndDate(2020, 12, 21)
        self.SetCash(20000)  # Set Strategy Cash
     
        tickers = ["EURUSD"]
        # Rolling Windows to hold bar close data keyed by symbol
        self.closingData = {}
        self.SMA45 = {}
        for ticker in tickers:
            symbol = self.AddForex(ticker, Resolution.Hour, Market.Oanda).Symbol
            self.closingData[symbol] = RollingWindow[float](100)
        # Warm up our rolling windows
            self.SMA45[symbol] = self.RC(symbol, 100, 2, Resolution.Hour) 
        self.SetWarmUp(50)
        self.previous=None
        self.current=None
        self.marketTicket=None
        self.stopLimitTicket=None
        self.stopMarketTicket=None
        self.Schedule.On(self.DateRules.Every(DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday), self.TimeRules.At(9,00), self.End)
        
    def OnData(self, data):
        
        for symbol, window in self.closingData.items():
            if data.ContainsKey(symbol) and data[symbol] is not None:
                window.Add(data[symbol].Close)
        
        if self.IsWarmingUp or not all([window.IsReady for window in self.closingData.values()]):
            return
        
        for symbol, sma in self.SMA45.items():
            self.Plot('SMA', symbol.Value, sma.Current.Value)
        
        for symbol, window in self.closingData.items():
            supports, resistances = self.GetPriceLevels(window)
            #self.Log(f"Symbol: {symbol.Value} , Supports: {supports} , Resistances: {resistances}")
            #self.Debug(self.SMA45[symbol].Slope.Current.Value)
            
            try:
                support=supports[0]
                resistance=resistances[0]
                #support2=supports[3]
                #resistance2=resistances[4]
                
                self.difference=abs(support-resistance)
                self.Debug(self.closingData[symbol][1])
                if self.previous is not None and self.previous == self.Time.date():
                    return
                
                if not self.Portfolio[symbol].Invested and self.Time.hour>0 and self.Time.hour<9 and self.difference>0.0040 and self.Securities[symbol].Price>support and self.Securities[symbol].Price<resistance and self.SMA45[symbol].Slope.Current.Value>0:
                    self.marketTicket=self.MarketOrder(symbol, 100000)
                    self.Debug(support)
                    self.Debug(resistance)
                    self.Debug(self.Time.date())
                    self.Debug(self.Time.hour)
                    self.Debug(f"Symbol: {symbol.Value} , Supports: {supports} , Resistances: {resistances}")
                    self.stopLimitTicket = self.LimitOrder(symbol, -100000, resistance)
                    self.stopMarketTicket = self.StopMarketOrder(symbol, -100000, support)
                    
            except IndexError:
                pass
                continue
    def GetPriceLevels(self, series, variation = 0.006, h = 5):
        
        supports = []
        resistances = []
        
        maxima = []
        minima = []
        
        # finding maxima and minima by looking for hills/troughs locally
        for i in range(h, series.Size-h):
            if series[i] > series[i-h] and series[i] > series[i+h]:
                maxima.append(series[i])
            elif series[i] < series[i-h] and series[i] < series[i+h]:
                minima.append(series[i])
       
        # identifying maximas which are resistances
        for m in maxima:
            r = m * variation
            # maxima which are near each other
            commonLevel = [x for x in maxima if x > m - r and x < m + r]
            # if 2 or more maxima are clustered near an area, it is a resistance
            if len(commonLevel) > 1:
                # we pick the highest maxima if the cluster as our resistance
                level = max(commonLevel)
                if level not in resistances:
                    resistances.append(level)
        
        # identify minima which are supports
        for l in minima:
            r = l * variation
            # minima which are near each other
            commonLevel = [x for x in minima if x > l - r and x < l + r]
            # if 2 or more minima are clustered near an area, it is a support
            if len(commonLevel) > 1:
                # We pick the lowest minima of the cluster as our support
                level = min(commonLevel)
                if level not in supports:
                    supports.append(level)
            
        
        return supports, resistances
    
    def OnOrderEvent(self, orderEvent):
        self.previous=self.Time.date()
        if self.stopLimitTicket !=None and self.stopLimitTicket.OrderId == orderEvent.OrderId:
            self.stopMarketTicket.Cancel()
            self.marketTicket=None
        if self.stopMarketTicket !=None and self.stopMarketTicket.OrderId == orderEvent.OrderId:
            self.stopLimitTicket.Cancel()
            self.marketTicket=None
            
    def End(self):
        self.Liquidate()