Overall Statistics
Total Trades
9897
Average Win
0.13%
Average Loss
-0.01%
Compounding Annual Return
-6.806%
Drawdown
35.400%
Expectancy
-0.827
Net Profit
-35.371%
Sharpe Ratio
-2.876
Loss Rate
99%
Win Rate
1%
Profit-Loss Ratio
12.18
Alpha
-0.049
Beta
0.003
Annual Standard Deviation
0.017
Annual Variance
0
Information Ratio
-0.562
Tracking Error
0.197
Treynor Ratio
-18.428
Total Fees
$0.00
from QuantConnect.Data.UniverseSelection import *
from Risk.TrailingStopRiskManagementModel import TrailingStopRiskManagementModel
import pandas as pd
import numpy as np
import pytz


class EURUSDMeanReversionAlgorithm(QCAlgorithm):

    def Initialize(self):
        self.SetTimeZone("Europe/London")

        self.WL = 10
        self.SetStartDate(2008, 1, 1)
        self.SetEndDate(2019, 1,1)
        self.SetCash(50000)
        
        self.holdHours = 0

        self.UniverseSettings.Resolution = Resolution.Hour
        self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction)
        self.AddForex("EURUSD", Resolution.Daily) 
        # rebalance the universe selection once a month
        
        self.rebalence_flag = 0
        # make sure to run the universe selection at the start of the algorithm even it's not the manth start
        self.first_month_trade_flag = 1
        self.trade_flag = 0  
        # Number of quantiles for sorting returns for mean reversion
        self.nq = 5
        # Number of quantiles for sorting volatility over five-day mean reversion period
        self.nq_vol = 3
        # the symbol list after the coarse and fine universe selection
        self.universe = None
        self.entryprice = 0
        
        self.AddForex("EURUSD", Resolution.Daily).Symbol

        self.macd = self.MACD("EURUSD", 12, 26, 9, MovingAverageType.Exponential, Resolution.Daily)
        self.PlotIndicator("MACD", True, self.macd, self.macd.Signal)
        self.PlotIndicator("EURUSD", self.macd.Fast, self.macd.Slow)
        
        
        
        # Indicators
        self.emafifty = self.EMA("EURUSD", 50, Resolution.Daily)
        

            
        
        self.bb = self.BB("EURUSD", 50, 2, MovingAverageType.Simple, Resolution.Daily)
        
   

        self.Schedule.On(self.DateRules.MonthStart("EURUSD"), self.TimeRules.At(8, 00), Action(self.monthly_rebalance))
        self.Schedule.On(self.DateRules.EveryDay("EURUSD"), self.TimeRules.BeforeMarketClose("EURUSD", 303), Action(self.get_prices))
        self.Schedule.On(self.DateRules.EveryDay("EURUSD"), self.TimeRules.BeforeMarketClose("EURUSD", 302), Action(self.daily_rebalance))
        # self.Schedule.On(self.DateRules.EveryDay("EURUSD"), self.TimeRules.BeforeMarketClose("EURUSD", 301), Action(self.short))
        # self.Schedule.On(self.DateRules.EveryDay("EURUSD"), self.TimeRules.BeforeMarketClose("EURUSD", 300), Action(self.long))
        
        
        
        
        # Gets or sets the maximum order quantity as a percentage of the current bar's volume.
        # This defaults to 0.01m = 1%. For example, if the current bar's volume is 100,
        # then the maximum order size would equal 1 share.
        self.MaximumOrderQuantityPercentVolume = 0.01
    
    def monthly_rebalance(self):
        # rebalance the universe every month
        self.rebalence_flag = 1
 
    def CoarseSelectionFunction(self, coarse):
        
        if self.rebalence_flag or self.first_month_trade_flag:
            
            # drop stocks which have no fundamental data or have too low prices
            selected = [x for x in coarse if (x.HasFundamentalData) and (float(x.Price) > 5)]
            # rank the stocks by dollar volume and choose the top 50
            filtered = sorted(selected, key=lambda x: x.DollarVolume, reverse=True) 

            return [ x.Symbol for x in filtered[:50]]
        else:
            return self.universe

    def FineSelectionFunction(self, fine):

        if self.rebalence_flag or self.first_month_trade_flag:
            # filter the stocks which have positive EV To EBITDA
            filtered_fine = [x for x in fine if x.ValuationRatios.EVToEBITDA > 0]
            self.universe = [x.Symbol for x in filtered_fine]
            
            self.rebalence_flag = 0
            self.first_month_trade_flag = 0
            self.trade_flag = 1
            
        return self.universe
        

    def OnData(self, data):

        
        if data.ContainsKey("EURUSD") == False:
            return
        

        
        
        self.close = data["EURUSD"].Close
        self.OpenTrade = True

        if  self.Time.hour == 8:
            self.OpenTrade = True
        elif self.Time.hour == 13:
            self.OpenTrade = False

            
        #UNCOMMENT FOR BOT IMPLEMENTATION
        
        # tz_ln = pytz.timezone('Europe/London')
        # london_now = datetime.now(tz_ln)
        # fmt = '%H'
        # self.timenowLondon  = london_now.strftime(fmt)
        # self.timenowLondon = int(self.timenowLondon)
  
        # tz_ny = pytz.timezone('America/New_York')
        # ny_now = datetime.now(tz_ny)
        # fmt = '%H'
        # self.timenowNYC  = ny_now.strftime(fmt)
        # self.timenowNYC = int(self.timenowNYC)
  

        
        # if  self.timenowLondon == 8:
        #     self.OpenTrade = True
        # else:
        #     self.OpenTrade = False

        # if self.timenowNYC == 8:
        #     self.OpenTrade = False
        # else:
        #     self.OpenTrade = True
        
        #Start opening trades (if signal appears) at 8:00am London time. Close the trade at 8:00am NYC time
        
        
        
        #Enter a trade when the candlestick pattern goes beyond the upper bollinger bands and closes above the 50EMA.
        
        symbol = "EURUSD"
        

        if self.close < self.bb.LowerBand.Current.Value:
            if self.close < self.emafifty.Current.Value:
                
                if self.OpenTrade:
                    self.SetHoldings(symbol, 1)
                    self.entryprice = self.close
   
        if self.close > self.bb.UpperBand.Current.Value:
            if self.close > self.emafifty.Current.Value:
                
                if self.OpenTrade:
                    self.SetHoldings(symbol, -1)
                    self.entryprice = self.close
                
        #EXIT
        if self.Portfolio.Invested:
            
            if self.Portfolio["EURUSD"].IsLong:
                #Stop loss is 40 points (not pips) above the high of the day.
                #T/P is the EMA50 value of the entry candlestick signal.
                if self.emafifty.Current.Value  > self.entryprice: #TAKE PROFIT
                    self.Debug(self.emafifty.Current.Value)
                    self.Debug(self.entryprice)
                    self.Liquidate("EURUSD")
                
                if self.emafifty.Current.Value  < self.entryprice: #STOP LOSS
                    self.Debug(self.emafifty.Current.Value)
                    self.Debug(self.entryprice)
                    self.Liquidate("EURUSD")

            if self.Portfolio["EURUSD"].IsShort:
                
                #Stop loss is 40 points (not pips) above the high of the day.
                #T/P is the EMA50 value of the entry candlestick signal.
                
                if self.emafifty.Current.Value  < self.entryprice: # T/P is the EMA50 value of the entry candlestick signal
                    self.Debug(self.emafifty.Current.Value)
                    self.Debug(self.entryprice)
                    self.Liquidate("EURUSD")                
                
                if self.emafifty.Current.Value  > self.entryprice: # STOP LOSS
                    self.Debug(self.emafifty.Current.Value)
                    self.Debug(self.entryprice)
                    self.Liquidate("EURUSD")
            
            self.holdHours+=1
            if self.holdHours > 5:
                self.Liquidate("EURUSD")                
                    
                    
    def short(self):
        
        self.SetRiskManagement(TrailingStopRiskManagementModel(0.03))
        
        #Stop loss is 40 points (not pips) above the high of the day.
        #T/P is the EMA50 value of the entry candlestick signal.
        #Open a trade once the candlestick signal closes. Don’t enter trade one hour before the American session opens.  
        
        if self.universe is None: return
        SPY_Velocity = 0
        self.long_leverage = 0
        self.short_leverage = 0
        
        # request the history of benchmark
        pri = self.History(["EURUSD"], 200, Resolution.Daily)
        pos_one = (pri.loc["EURUSD"]['close'][-1])
        pos_six = (pri.loc["EURUSD"]['close'][-75:].mean())
        
        # calculate velocity of the benchmark 
        velocity_stop = (pos_one - pos_six)/100.0
        SPY_Velocity = velocity_stop
        if SPY_Velocity > 0.0:
            self.long_leverage = 1.8
            self.short_leverage = -0.0
        else:
            self.long_leverage = 1.1
            self.short_leverage = -0.7
        for symbol in self.shorts:
            if len(self.shorts) + self.existing_shorts == 0: return
            self.AddEquity(symbol, Resolution.Daily)
            self.SetHoldings(symbol, self.short_leverage/(len(self.shorts) + self.existing_shorts))                                
 
 
 
 
    def long(self):
        #Risk-reward ratio should be a minimum of 1:1.
        self.SetRiskManagement(TrailingStopRiskManagementModel(0.03))
        if self.universe is None: return
        for symbol in self.longs:
            if len(self.longs) + self.existing_longs == 0: return
            self.AddEquity(symbol, Resolution.Daily)
            self.SetHoldings(symbol, self.long_leverage/(len(self.longs) + self.existing_longs))                                
       
    def get_prices(self):
        if self.universe is None: return
        # Get the last 6 days of prices for every stock in our universe
        prices = {}
        hist = self.History(self.universe, 6, Resolution.Daily)
        for i in self.universe:
            if str(i) in hist.index.levels[0]:
                prices[i.Value] = hist.loc[str(i)]['close']
        df_prices = pd.DataFrame(prices, columns = prices.keys()) 
        # calculate the daily log return
        daily_rets = np.log(df_prices/df_prices.shift(1))
        # calculate the latest return but skip the most recent price
        rets = (df_prices.iloc[-2] - df_prices.iloc[0]) / df_prices.iloc[0]
        # standard deviation of the daily return
        stdevs = daily_rets.std(axis = 0)
        self.ret_qt = pd.qcut(rets, 5, labels=False) + 1
        self.stdev_qt = pd.qcut(stdevs, 3, labels=False) + 1
        self.longs = list((self.ret_qt[self.ret_qt == 1].index) & (self.stdev_qt[self.stdev_qt < 3].index))
        self.shorts = list((self.ret_qt[self.ret_qt == self.nq].index) & (self.stdev_qt[self.stdev_qt < 3].index))

 
    def daily_rebalance(self):
        # rebalance the position in portfolio every day           
        if self.universe is None: return
        self.existing_longs = 0
        self.existing_shorts = 0
       
        for symbol in self.Portfolio.Keys:
            if (symbol.Value != 'EURUSD') and (symbol.Value in self.ret_qt.index):
                current_quantile = self.ret_qt.loc[symbol.Value]
                if self.Portfolio[symbol].Quantity > 0:
                    if (current_quantile == 1) and (symbol not in self.longs):
                        self.existing_longs += 1
                    elif  (current_quantile > 1) and (symbol not in self.shorts): 
                        self.SetHoldings(symbol, 0)
                elif self.Portfolio[symbol].Quantity < 0:
                    if (current_quantile == self.nq) and (symbol not in self.shorts):
                        self.existing_shorts += 1
                    elif (current_quantile < self.nq) and (symbol not in self.longs): 
                        self.SetHoldings(symbol, 0)