| Overall Statistics |
|
Total Trades
1593
Average Win
0.59%
Average Loss
-0.77%
Compounding Annual Return
-0.161%
Drawdown
39.600%
Expectancy
0.007
Net Profit
-3.248%
Sharpe Ratio
0.021
Probabilistic Sharpe Ratio
0.000%
Loss Rate
43%
Win Rate
57%
Profit-Loss Ratio
0.77
Alpha
0.002
Beta
-0.013
Annual Standard Deviation
0.077
Annual Variance
0.006
Information Ratio
-0.31
Tracking Error
0.197
Treynor Ratio
-0.129
Total Fees
$1746.39
|
# https://quantpedia.com/strategies/currency-momentum-factor/
#
# Create an investment universe consisting of several currencies (10-20). Go long three currencies with the highest 12-month momentum against USD
# and go short three currencies with the lowest 12-month momentum against USD. Cash not used as margin invest on overnight rates. Rebalance monthly.
from collections import deque
import fk_tools
from math import sqrt
class Currency_Momentum_Factor(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2000, 1, 1)
self.SetCash(100000)
# Daily close prices.
self.data = {}
self.period = 12 * 21
self.targeted_volatility = 0.10
self.symbols = [
"CME_AD1", # Australian Dollar Futures, Continuous Contract #1
"CME_BP1", # British Pound Futures, Continuous Contract #1
"CME_CD1", # Canadian Dollar Futures, Continuous Contract #1
"CME_EC1", # Euro FX Futures, Continuous Contract #1
"CME_JY1", # Japanese Yen Futures, Continuous Contract #1
"CME_MP1", # Mexican Peso Futures, Continuous Contract #1
"CME_NE1", # New Zealand Dollar Futures, Continuous Contract #1
"CME_SF1" # Swiss Franc Futures, Continuous Contract #1
]
for symbol in self.symbols:
data = self.AddData(fk_tools.QuantpediaFutures, symbol, Resolution.Daily)
data.SetFeeModel(fk_tools.CustomFeeModel(self))
data.SetLeverage(20)
self.data[symbol] = deque(maxlen = self.period)
self.Schedule.On(self.DateRules.MonthStart(self.symbols[0]), self.TimeRules.AfterMarketOpen(self.symbols[0]), self.Rebalance)
def OnData(self, data):
for symbol in self.symbols:
if self.Securities.ContainsKey(symbol):
price = self.Securities[symbol].Price
if price != 0:
self.data[symbol].append(price)
else:
# Append latest price as a next one in case there's 0 as price.
if len(self.data[symbol]) > 0:
last_price = self.data[symbol][-1]
self.data[symbol].append(last_price)
def Rebalance(self):
# Store monthly closes and calculate momentum.
momentum = {}
volatility = {}
for symbol in self.symbols:
if len(self.data[symbol]) == self.data[symbol].maxlen:
prices = [x for x in [x for x in self.data[symbol]]]
momentum[symbol] = fk_tools.Return(prices)
volatility[symbol] = fk_tools.Volatility(prices[-21:]) * sqrt(252)
if len(momentum) == 0 or len(volatility) == 0:
self.Liquidate()
return
sorted_by_momentum = sorted(momentum.items(), key = lambda x: x[1], reverse = True)
long = [x[0] for x in sorted_by_momentum[:3]]
short = [x[0] for x in sorted_by_momentum[-3:]]
# Volatility targeting.
count = len(long + short)
portfolio_volatility = sum([((volatility[x]) / count) for x in long + short])
volatility_target_leverage = self.targeted_volatility / portfolio_volatility
# Trade execution.
invested = [x.Key.Value for x in self.Portfolio if x.Value.Invested]
for symbol in invested:
if symbol not in long + short:
self.Liquidate(symbol)
for symbol in long:
self.SetHoldings(symbol, volatility_target_leverage / len(long))
for symbol in short:
self.SetHoldings(symbol, -volatility_target_leverage / len(short))
import numpy as np
def Return(values):
return (values[-1] - values[0]) / values[0]
def Volatility(values):
values = np.array(values)
returns = (values[1:] - values[:-1]) / values[:-1]
return np.std(returns)
# Custom fee model
class CustomFeeModel(FeeModel):
def GetOrderFee(self, parameters):
fee = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.00005
return OrderFee(CashAmount(fee, "USD"))
# Quandl free data
class QuandlFutures(PythonQuandl):
def __init__(self):
self.ValueColumnName = "settle"
# Quantpedia data
# NOTE: IMPORTANT: Data order must be ascending (datewise)
class QuantpediaFutures(PythonData):
def GetSource(self, config, date, isLiveMode):
return SubscriptionDataSource("data.quantpedia.com/backtesting_data/futures/{0}.csv".format(config.Symbol.Value), SubscriptionTransportMedium.RemoteFile, FileFormat.Csv)
def Reader(self, config, line, date, isLiveMode):
data = QuantpediaFutures()
data.Symbol = config.Symbol
if not line[0].isdigit(): return None
split = line.split(';')
data.Time = datetime.strptime(split[0], "%d.%m.%Y") + timedelta(days=1)
data['settle'] = float(split[1])
data.Value = float(split[1])
return data