Introduction
There are two approaches to trading: discretionary and systematic. Systematic trading is based on rules and is more quant-oriented. Trading signals are generated based on rules derived from historical data. Conversely, discretionary trading is based on fundamental research. Entry and exit points, holding period, position size and other parameters are at the discretion of a trader.
I don’t think one is necessarily better than another. Both trading styles have their own advantages and flaws. Discretionary trading is more flexible and allows a trader to quickly adjust his position to new information. However, this kind of trading is highly subjective and can be affected by emotional biases. Systematic trading attempts to remove impact of these subjective factors. In this sense, it is more objective because it’s built on predefined and backtested rules and criteria. But this is not always a good thing. Systematic trading strategies may suffer from overfitting, that is underperforming in comparison to how the strategy performed on historical data. Another challenge of systematic trading is that it can be slow in reacting new market information. If market regime changes significantly from the one on which the strategy has been developed, the result can be disastrous.
Systematic crypto trading
In this article I’ll show how to develop systematic trading strategies for crypto market. There are a few themes upon which most if not all systematic trading strategies are built. I’ll choose two of them, momentum and statistical arbitrage, and teach how to develop trading strategies based on these ideas. Both of them have been tested by millions of traders all over the world successfully in different financial markets. Thus, we can assume that they are robust. I’ll explain how one can adjust those strategies to cryptocurrencies.
Momentum
Momentum is a simple idea. You buy the assets that outperformed in the recent past, and you short sell those ones which underperformed. Momentum has been shown to work in various markets from commodities to currencies over 200 years. Let’s look how one can build a crypto momentum trading strategy from scratch in Python. Python is a programming language known for its simplicity and widely used in quantitative trading.
How it works?
To develop a systematic trading strategy the first thing to do is to get historical price data. For this project we’ll use Binance, the largest crypto exchange. Once we fetch Binance data, we check whether return of Bitcoin, the largest cryptocurrency was greater than the predefined threshold. This is because Bitcoin is the bellwether of the crypto market and it precedes the price move of the entire market. If BTC price surged, we may expect that momentum is in play in the digital assets sector and we can bet on it. We can play with different threshold parameters to optimize the strategy; for this article I choose 0.05. This means that if BTC price change was over 5%, we continue with the steps below; otherwise, we skip that week.
If return of BTC price has increased more than 5% within the lookback period, we calculate a momentum metric that gauges which cryptocurrencies performed best within that period. The momentum metric will be explained below.
After we have computed the momentum index of all cryptocurrencies, we buy the 5 coins with the highest momentum. We bet that these coins will continue to outperform the market. We rebalance our portfolio every 7 days. That is, after 7 days we repeat the process. We calculate the momentum index again. If a coin remains in the top-5, we hold it; otherwise, we get rid of it. We add the coins to our portfolio with the highest level of momentum.
import requests
import json
import pandas as pd
import datetime as dt
from heapq import nlargest
from functools import reduce
import numpy as np
from scipy import stats
import matplotlib.pyplot as plt
lst = [“BTC”, “ETH”, “CAKE”, “DOT”, “MANA”, “SAND”, “AVAX”, “ALGO”, “ATOM”, “MATIC”, “BNB”,
“SNX”, “THETA”, “GRT”, “LINK”, “SHIB”, “DOGE”, “VET”, “AXS”, “SOL”, “FIL”, “TRX”,
“FTM”, “FARM”, “LTC”, “ETC”, “NEAR”, “ALICE”, “ICP”, “EGLD”, “UNI”, “ADA”, “XRP”,
“ZEC”, “QUICK”, “BAT”, “ENJ”, “GALA”, “1INCH”, “SLP”, “COMP”, “ROSE”, “ONT”, “AAVE”,
“ANKR”, “NEO”, “XTZ”, “WTC”, “OCEAN”, “IOTA”, “IOTX”, “COTI”, “XLM”, “QTUM”, “AR”,
“LINA”, “BETA”, “CELO”, “ZIL”, “HBAR”, “OGN”, “ILV”, “ALPHA”, “RVN”, “KAVA”,”YGG”,
“AUDIO”, “STORJ”, “ATA”, “DODO”, “POND”, “CHZ”, “YFI”, “SUPER”, “NKN”, “INJ”, “EOS”,
“LRC”, “ARPA”, “LPT”, “XVS”, “KLAY”, “CRV”, “LTO”, “MKR”, “ONE”, “RNDR”,
“FOR”, “BICO”,”SYS”, “CELR”, “ALPACA”, “BLZ”, “DUSK”, “KNC”, “PAXG”, “DOCK”,
“MBOX”, “BADGER”, “ZRX”, “IDEX”, “FIDA”]
def create_df():
pair = “USDT”
root_url = ‘https://api.binance.com/api/v3/klines'
interval = ‘1d’
close_prices = pd.DataFrame()
for i in lst:
url = root_url + ‘?symbol=’ + i + pair + ‘&interval=’ + interval
data = json.loads(requests.get(url).text)
if ‘msg’ in data:
pass
else:
df = pd.DataFrame(data)
df.columns = [‘open_time’,
‘o’, ‘h’, ‘l’, ‘c’, ‘v’,
‘close_time’, ‘qav’, ‘num_trades’,
‘taker_base_vol’, ‘taker_quote_vol’, ‘ignore’]
df.index = [dt.datetime.fromtimestamp(x/1000.0) for x in df.close_time]
close_price = df[‘c’]
close_prices[i] = close_price
close_prices = close_prices.apply(pd.to_numeric)
return close_prices
close_prices = create_df()
btc_price = close_prices[‘BTC’]
# PARAMETERS
# how many days’ data we’ll look at?
lookback = 35
# ignoring last x days’ data
last_days = 7
# how many days will we hold the coins?
holding_days = 7
# if BTC return is below the threshold over a given period, we hold BTC; otherwise, we buy altcoins
threshold = 0.05
The script above implements what has been mentioned so far. It fetches price data from Binance and stores the result in a data frame called close_prices. Then, we define the parameters used in the model.
Momentum metric
For this strategy I’ll use the momentum metric developed by Andreas Clenow in his book “Trading Evolved”. I should first explain why we don’t just look at percent returns to measure momentum. Let’s take a highly volatile coin. The news about it was just released and the coin is skyrocketing. This doesn’t mean the cryptocurrency in question is actually strong; it just reacted to the news after which it can give back most of its return to the market. Now you see the problem. If we choose percent return as a metric, our portfolio will be dominated by highly volatile digital assets. But this is not what momentum is about. We have to think of not only performance but also the path of this performance.
Clenow’s idea is to gauge momentum with exponential regression and then multiply it by R2, the coefficient of determination. Regression is a statistical approach to quantify the relationship between a dependent variable and one or more independent variables. Linear regression, which is the most widely used form of regression analysis, seeks to find a straight line that best fits to the data which in our case is prices of cryptocurrency traded at Binance. The slope of the line indicates the trend and, in our model, it will be in USDT if we choose to employ linear regression. What the slope tells us is if the current trend continues, how much, i.e., how many USDT we can earn or lose.
But here is the problem. Take two coins with a high and a low price, e.g., $MKR which is trading at a 4-figure price and $SHIBA which is trading at such a tiny price that the number of zeros in its price seems infinite. Let’s say, we run our model and both cryptocurrencies show the regression slope of $0.1 (or 0.1 USDT, it doesn’t matter for our discussion). Though the slope is the same for the tokens, it means different things for $MKR and $SHIBA. Since 0.1 is the miniscule percentage of $MKR price, this model implies that Maker is rising very slowly. Conversely, the slope of $SHIBA is large relatively to its price which would mean that $SHIBA is trading at a greater momentum right now. So, using a model which produces the slope in absolute USD or USDT numbers is not useful and can result in wrong investment decisions.
The slope of exponential regression, on the other hand, gives information in percentages, i.e., how many percent up or down the line is sloping. This is much more useful than linear regression for our purposes because it allows to compare tokens without regard to their prices. We’ll annualize it because the slope of exponential regression tends to be a very small number. Say, for example, the slope is 0.000634. We annualize the number:
(1+0.00634)365–1 = 0.2603
This means that we’re dealing with the digital asset with the positive trend of approximately 26% per year. The reason why we 365 is that since our data is daily, result of the model will also be daily. Therefore, to annualize it we raise to the power of 365 because crypto markets are open 24/7, 365 days per year.
Now about the coefficient of determination part. This metric, more succinctly called R2, measures how well the regression line fits the data. In other words, it shows how well the variation in our independent variable(s) explains the variation in the dependent variable. The higher R2 is, the better. We add the coefficient of determination to punish more volatile tokens. Digital assets rising gradually will have a smoother trend and thus a higher R2.
This is the code for the momentum index:
def slope(ts):
ts = ts.dropna()
x = np.arange(len(ts))
log_ts = np.log(ts)
slope, intercept, r_value, p_value, std_err = stats.linregress(x, log_ts)
annualized_slope = (np.power(np.exp(slope), 365) -1) *100
return annualized_slope * (r_value ** 2)
You’d think it will take many lines to implement this metric. What took me a lot of words to explain in English has been written in Python in only 7 lines. That’s why I love Python.
Momentum in a nutshell
We can summarize the momentum strategy in these steps:
- Fetch price data from Binance
- Check whether BTC price change has been over the preset threshold during the lookback period. If not, do nothing. If yes, continue with the steps below.
- Compute the momentum metric for all coins.
- Buy the five coins with the highest metric.
- Hold 1 week.
- Rebalance the portfolio every 7 days. If a token continues to remain in the top-5, hold it; otherwise, sell it, and buy the coins with the highest momentum metric.
Pairs trading
Pairs trading is a market-neutral strategy where you buy one asset and sell another betting on that their ratio will revert to its long-term mean. How do you choose which asset(s) should be bought and which ones should be shorted? The framework of pairs trading strategies typically follows these steps:
- Get data.
- Calculate returns of securities in your investment universe.
- Compute correlations between these returns.
- Narrow your universe to highly correlated asset pairs.
- Do a cointegration test on selected pairs.
- Select asset pairs that passed the test.
- Generate signals on these pairs.
We already got historical data from Binance so no need to do that part again. As the next step, we calculate returns of coins and correlations between these returns. To narrow the investment universe, we select only coin pairs with the correlation coefficient over the predefined threshold.
Stationarity and Cointegration
Cointegration is a technique used to check whether time series display correlation over the long run. Why do we have to check for cointegration? Are correlations not sufficient? Correlation, which shows only a short-term relationship between two variables, is very unstable, and can change dramatically. Two coins may be correlated in the short-term but can diverge in the long run. That’s why we employ a statistical test to select only those cryptocurrency pairs which are cointegrated, i.e., show long-term correlation.
Stationarity is another important concept used in quant trading. According to Wikipedia, “a stationary process is a stochastic process whose unconditional joint probability distribution does not change when shifted in time.” That is a bunch of smart-sounding words. Put it simply, a stationary time series doesn’t trend, it is flat-looking, and its variance doesn’t change significantly over time.
Look at the following chart. Time series depicted below reverts to the mean and doesn’t diverge over time.
stationary time series
Now consider the chart below. We can see that the variable displayed on that chart shows trend; its mean and perhaps other statistical parameters, such as standard deviation doesn’t revert to its long-term average. It’s a non-stationary time series.
non-stationary time series
Non-stationarity can distort statistical anaylsis because parameters of data generating processes keep changing. You cannot assume that the ratio of two cryptocurrencies will converge to its historical average if the time series of the ratio is non-stationary.
For this strategy we use Augmented Dickey-Fuller (ADF) test, on highly correlated cryptocurrency pairs. Once thing should be noted here. If the ratio of two variables is stationary, it implies that there exists a stable, long-term relationship between them, which is a characteristic of cointegration. However, it’s important to remember that passing the ADF test for the ratio of two variables is not sufficient evidence on its own to confirm cointegration. Nonetheless, trying to keep things simple, we’ll do only ADF test. Pairs failing ADF test will be discarded. We’ll continue with the pairs passing the test.
Generating signals
Trading signals will be generated based on z-scores. This is because we are not interested in absolute levels of the ratios. Let’s say, ETH to BTC ratio tends to trade between 0.04 and 0.14. Other two coins can and do probably trade at different levels. It means that we cannot generate signals based on ratio levels. Instead, we’ll use z-scores. Z-score is a metric measuring how far from the mean a data point is. Z-scores are expressed in standard deviations. The data point with the z-score of 3 is much higher than that one with the z-score of 0.2. For this model our trading rule is as follows. If z-score is over the preset threshold, which is 2, we short that pair. If z-score is below -2, we long it. If z-score is between -2 and 2, we do nothing.
Get your hands dirty
If you’d like to build these strategies from scratch in Python, I wrote two books which are good and practical for beginners. If you want to deep dive in crypto pairs trading, read this book at fmiren.gumroad.com/l/dbtak. I also wrote an ebook on crypto momentum trading — fmiren.gumroad.com/l/zwnkt.
Conclusion
In this article I show how quant trading strategies, namely momentum and pairs trading work in crypto and how one can build these trading systems from scratch in Python. Momentum is buying the strength and selling the weakness. You go long the assets that outperformed in the recent past and short ones with lackluster performance. Pairs trading is the exact opposite: buying the weakness and selling the strength. Use this article as a guide to develop your crypto trading strategies and exploit the inefficiencies in the markets.
