| 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)