Overall Statistics |
Total Trades 9956 Average Win 0.22% Average Loss -0.01% Compounding Annual Return -38.253% Drawdown 32.300% Expectancy -0.799 Net Profit -32.220% Sharpe Ratio -7.048 Loss Rate 99% Win Rate 1% Profit-Loss Ratio 22.81 Alpha -0.335 Beta 0.057 Annual Standard Deviation 0.047 Annual Variance 0.002 Information Ratio -3.324 Tracking Error 0.118 Treynor Ratio -5.779 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(2016, 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.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 #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.UpperBand.Current.Value: if self.OpenTrade: self.SetHoldings(symbol, 1) self.entryprice = self.close if self.close < self.bb.UpperBand.Current.Value: if self.OpenTrade: self.SetHoldings(symbol, -1) self.entryprice = self.close # 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 # self.SetHoldings(symbol, self.short_leverage) 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.close > self.entryprice +0.00080: #TAKE PROFIT self.Liquidate("EURUSD") if self.close < self.entryprice - 0.00080: #STOP LOSS 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.close < self.entryprice - 0.00080: # TAKE PROFIT self.Liquidate("EURUSD") if self.close > self.entryprice + 0.00180: # STOP LOSS self.Liquidate("EURUSD") self.holdHours+=1 if self.holdHours > 5: self.Liquidate("EURUSD") def short(self): #Risk-reward ratio should be a minimum of 1:1. self.SetRiskManagement(TrailingStopRiskManagementModel(0.50)) #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.50)) 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)