| Overall Statistics |
|
Total Trades 1432 Average Win 0.23% Average Loss -0.17% Compounding Annual Return 8.397% Drawdown 6.500% Expectancy 0.457 Net Profit 76.840% Sharpe Ratio 1.216 Probabilistic Sharpe Ratio 72.310% Loss Rate 38% Win Rate 62% Profit-Loss Ratio 1.36 Alpha 0.056 Beta 0.025 Annual Standard Deviation 0.048 Annual Variance 0.002 Information Ratio -0.291 Tracking Error 0.151 Treynor Ratio 2.333 Total Fees $0.00 Estimated Strategy Capacity $12000.00 Lowest Capacity Asset XMRUSD E3 |
# https://quantpedia.com/strategies/time-series-momentum-factor-in-cryptocurrencies/
#
# The investment universe consists of 11 cryptocurrencies (the full list can be found in the paper). Momentum factor is the prior week’s return
# for each currency and therefore can be calculated as a sum of returns for the last week (the data are avalaible at coinmetrics.io). After that,
# the momentum factor is standardized with z-scoring the variable longitudinally – de-meaning it and dividing by its standard deviation to create
# a normalized variable with a zero mean and unit standard deviation, separately for each currency. Portfolio is equally weighted, where the
# absolute weight is 10% divided by n, where 10% is the gross exposure limit (only 10% of portfolio is invested in cryptocurrencies) and n is
# the number of currencies available for investment. The weight is positive when the longitudinally standardized momentum factor is above zero
# and negative when this factor is below zero, this allows portfolios that can be net long or short the market. However, it is not possible to
# short cryptocurrencies and the practical application would require for example the long only strategy. Portfolio is rebalanced weekly. Last
# but not least, there are two weighting schemes, the second one is risk based and more information about it is in the paper, we have chosen
# equally-weighted strategy for a representative purposes.
import numpy as np
class TimeSeriesMomentumCryptocurrencies(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2015, 1, 1)
self.SetCash(100000)
self.symbols = ['BTCUSD', 'ETCUSD', 'ETHUSD', 'LTCUSD', 'XMRUSD', 'ZECUSD']
self.data = {}
for symbol in self.symbols:
self.AddCrypto(symbol, Resolution.Daily, Market.Bitfinex)
self.data[symbol] = RollingWindow[float](5)
self.Schedule.On(self.DateRules.Every(DayOfWeek.Monday), self.TimeRules.AfterMarketOpen(self.symbols[0]), self.Rebalance)
def OnData(self, data):
for symbol in self.data:
symbol_obj = self.Symbol(symbol)
if symbol_obj in data.Bars:
if data[symbol_obj]:
price = data[symbol_obj].Value
if price != 0:
self.data[symbol].Add(price)
def Rebalance(self):
perf_vol = {}
for symbol in self.symbols:
if self.data[symbol].IsReady:
prices = np.array([x for x in self.data[symbol]])
perf = prices[0] / prices[-1] - 1
daily_returns = prices[:-1] / prices[1:] - 1
vol = np.std(daily_returns)
perf_vol[symbol] = (perf, vol)
# Volatility weighting
total_vol = sum([1 / x[1][1] for x in perf_vol.items()])
if total_vol == 0: return
weight = {}
for symbol in perf_vol:
vol = perf_vol[symbol][1]
if vol != 0:
weight[symbol] = (1.0 / vol) / total_vol
else:
weight[symbol] = 0
# Trade execution.
long = [x[0] for x in perf_vol.items() if x[1][0] > 0]
invested = [x.Key.Value for x in self.Portfolio if x.Value.Invested]
for symbol in invested:
if symbol not in long:
self.Liquidate(symbol)
for symbol in long:
self.SetHoldings(symbol, 0.1 * weight[symbol])