| Overall Statistics |
|
Total Trades 102 Average Win 0.31% Average Loss -0.47% Compounding Annual Return -5.581% Drawdown 5.500% Expectancy -0.166 Net Profit -3.766% Sharpe Ratio -0.763 Loss Rate 50% Win Rate 50% Profit-Loss Ratio 0.67 Alpha -0.027 Beta -0.063 Annual Standard Deviation 0.05 Annual Variance 0.003 Information Ratio -1.641 Tracking Error 0.132 Treynor Ratio 0.607 Total Fees $0.00 |
class RiskPremiaForexAlgorithm(QCAlgorithm):
'''
Asymmetric Tail Risks and Excess Returns in Forex Market
Paper: https://arxiv.org/pdf/1409.7720.pdf
'''
def Initialize(self):
self.SetStartDate(2019, 1, 1) # Set Start Date
self.SetEndDate(2019, 9, 1) # Set End Date
self.SetCash(100000) # Set Strategy Cash
# Add forex data of the following symbols
forexs = ["EURUSD", "AUDUSD", "USDCAD", "USDJPY"]
self.symbols = [self.AddForex(forex, Resolution.Hour, Market.FXCM).Symbol for forex in forexs]
self.lookback = 30 # Length(days) of historical data
self.nextRebalance = self.Time # Next time to rebalance
self.rebalanceDays = 7 # Rebalance every 7 days (weekly)
self.longSkewLevel = -0.6 # If the skewness of a forex is less than this level, enter a long postion
self.shortSkewLevel = 0.6 # If the skewness of a froex is larger than this level, enter a short position
def OnData(self, data):
'''
Rebalance weekly for each forex pair
'''
# Do nothing until next rebalance
if self.Time < self.nextRebalance:
return
# Liquidate the holdings when the rebalance day comes
for holding in self.Portfolio.Values:
if holding.Invested:
self.Liquidate(holding.Symbol,"Liquidate")
# Get historical close data for the symbols
history = self.History(self.symbols, self.lookback, Resolution.Daily).close.unstack(level=0)
# The lists containing symbols for long & short
longSymbols = []
shortSymbols = []
# Get skewness for each symbol
for forex in self.symbols:
skewness = self.GetSkewness(history[str(forex)].values)
# If the skewness of a forex is less than the longSkew level,
# put this symbol into the longSymbols list
if skewness < self.longSkewLevel:
longSymbols.append(forex)
# If the skewness of a forex is larger than the shortSkew level,
# put this symbol into the shortSymbols list
if skewness > self.shortSkewLevel:
shortSymbols.append(forex)
# Open positions for the symbols with equal weights
count = len(longSymbols) + len(shortSymbols)
# If no suitable symbols, return
if count == 0:
return
# Long positions
for forex in longSymbols:
self.SetHoldings(forex, 1/count)
# Short postions
for forex in shortSymbols:
self.SetHoldings(forex, -1/count)
# Set next rebalance time
self.nextRebalance += timedelta(self.rebalanceDays)
def GetSkewness(self, values):
'''
Get the skewness for a forex symbol based on its historical data
Ref: https://www.itl.nist.gov/div898/handbook/eda/section3/eda35b.htm
'''
# Get standard deviation of the values
sd = values.std()
# Get the numerator of the skewness
numer = ((values - values.mean()) ** 3).sum()
# Get the denominator of the skewness
denom = self.lookback * sd ** 3
# Return the skewness
return numer/denom