The strategy invests in 11 cryptocurrencies, using momentum factors based on prior weekly returns, normalized with z-scores. The portfolio is equally weighted, rebalanced weekly, and typically long-only due to shorting restrictions.

I. STRATEGY IN A NUTSHELL

The strategy trades 11 cryptocurrencies using a weekly momentum signal based on prior returns, standardized by z-scores. Positions are equally weighted with up to 10% total exposure, focusing on long-only implementation due to shorting limits. Portfolios are rebalanced weekly, with optional risk-based weighting.

II. ECONOMIC RATIONALE

Weekly momentum captures short-term trends in volatile crypto markets, producing strong positive alphas. Results show momentum signals effectively predict returns, confirming that even in high-volatility environments, disciplined momentum strategies can deliver superior performance.

III. SOURCE PAPER

‘Know When to Hodl ‘Em, Know When to Fodl ‘Em’: An Investigation of Factor Based Investing in the Cryptocurrency Space [Click to Open PDF]

Stefan Hubrich. T.Rowe Price

<Abstract>

It has been known since at least the groundbreaking work of Fama and French (1992) that there are specific attributes, so called factors, that can help predict the returns of individual assets above the return of the broader market. Since these predictive characteristics arise out of sample (with currently observable factor values predicting future returns), investors can earn excess returns with portfolios that are constructed to align with the factors. First introduced in the cross section of returns and focusing on individual equity securities, the efficacy of such factors has since been demonstrated at the asset class level as well, and found to work not only in the cross section but also longitudinally (for individual assets, through time). Factors like value, momentum, and carry have been found to work so broadly across different asset classes, security universes, countries, and time periods, that Asness et al. simply titled their influential 2013 Journal of Finance paper “Value and Momentum Everywhere”. Our paper provides a first application of momentum, value, and carry based factor investing to the cryptocurrencies. We show that these same factors are effective in this relatively new and unexplored asset class, permitting the construction of portfolios that can earn excess returns over the cryptocurrency “market” as a whole.

IV. BACKTEST PERFORMANCE

Annualised Return6.3%
Volatility8.1%
Beta0.035
Sharpe Ratio0.78
Sortino Ratio0.487
Maximum DrawdownN/A
Win Rate52%

V. FULL PYTHON CODE

from AlgorithmImports import *
import numpy as np
#endregion
class TimeSeriesMomentumCryptocurrencies(QCAlgorithm):
    def Initialize(self):
        self.SetStartDate(2015, 1, 1)
        self.SetCash(100000)
        self.symbols = ['BTCUSD', 'ETCUSD', 'ETHUSD', 'LTCUSD', 'XMRUSD', 'ZECUSD']
        self.data = {}
        self.percentage_traded = 0.1
        
        for symbol in self.symbols:
            data = self.AddCrypto(symbol, Resolution.Daily, Market.Bitfinex)
            data.SetFeeModel(CustomFeeModel())
            data.SetLeverage(10)
            self.data[symbol] = RollingWindow[float](5)
        
    def OnData(self, data):
        for symbol in self.data:
            symbol_obj = self.Symbol(symbol)
            if symbol_obj in data.Bars and data[symbol_obj]:
                self.data[symbol].Add(data[symbol_obj].Value)
        if self.Time.date().weekday() != 0:
            return
        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:
            if symbol in data and data[symbol]:
                self.SetHoldings(symbol, self.percentage_traded * weight[symbol])
# Custom fee model.
class CustomFeeModel(FeeModel):
    def GetOrderFee(self, parameters):
        fee = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.00005
        return OrderFee(CashAmount(fee, "USD"))

VI. Backtest Performance

Leave a Reply

Discover more from Quant Buffet

Subscribe now to keep reading and get access to the full archive.

Continue reading