Testing stationary process and time-series in Python (using cryptos)
- ADF and KPSS tests for checking stationarity;
- Obtaining market data of cryptocurrencies;
- Discussion on statistical tests;
Hello, all!
![]() |
It is important to check stationarity! |
In quantitative finance, for instance, mean-reverting models assume that determined parameter (e.g. prices, volatility) follows a stationary process. In the following, I present a code used to generate a typical stationary process by means of a Ornstein-Uhlenbach model:
dx=(θ−αx)dt+σdW
where θ is the long-term average and W denotes a Brownian motion. Hence:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from math import sqrt | |
import numpy.random as nr | |
import matplotlib.pyplot as plt | |
nr.seed(1314) | |
#parameters | |
sigma = 60 | |
alpha = 50 | |
beta = 100 | |
S0 = 90 | |
days = [d for d in range(90)] | |
# contructing the random path | |
def mean_reverting(days, alpha, beta, sigma): | |
dW = nr.standard_normal(len(days)) | |
time_series=[] | |
for d in range(len(days)): | |
if d!=0: | |
time_series.append(vec_S[d-1]+alpha*(beta-vec_S[d-1])*1/365+sigma*sqrt(1/365)*dW[d-1]) | |
else: | |
time_series.append(S0) | |
return time_series | |
time_series = mean_reverting(days, alpha, beta, sigma) | |
#plot | |
def plot_(x, y, beta, label='Asset', label_vert_line='long-term average'): | |
plt.plot(x, y, label=label) | |
plt.axhline(beta, color='red', linestyle='dashed', label=label_vert_line) | |
plt.ylabel('Price') | |
plt.xlabel('Days') | |
plt.legend(loc='best') | |
plot_(days, time_series, beta) |
Notice that the series tends to return to the mean value. Of course, this is a very interesting characteristic for trades, for example, since one has an indication of the current average adopted by the time series. However, one should always remember that the analysis presented hereafter (as well as any other statistical model) does not predict a value in the future. This is because the current regime can change at any point in time, modifying completely the parameters of the stochastic model.
Getting crypto market data
In order to show the application of the stationary tests, I will use market data of three important cryptocurrencies: Bitcoin, Ethereum, and TrueUSD. I will use the API cryptoCMD which connects to the site CoinMarketCap to obtain the data we will evaluate:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import cryptocmd | |
from cryptocmd import CmcScraper | |
def getCoinData(list_coin,numeraire,date_min,date_max): | |
coin_data={} | |
coin_name=[] | |
if numeraire!='': | |
try: | |
scrapper = CmcScraper(numeraire, date_min, date_max) | |
numeraire_data = scrapper.get_dataframe() | |
except Exception as e: | |
print("[CryptoDataParse - Numeraire]: type error: " + str(e)) | |
else: | |
numeraire_data=[] | |
for coin in list_coin: | |
if coin != numeraire: | |
try: | |
scrapper=CmcScraper(coin,date_min, date_max) | |
coin_data[coin]=scrapper.get_dataframe() | |
coin_name.append(coin) | |
except Exception as e: | |
print("[CryptoDataParse]: type error: " + str(e)) | |
return coin_data, numeraire_data, coin_name |
Now that we already have the data we need, let´s move forward to the stationary tests.
Test statistics
I will show two different statistical tests. The first is the Augmented Dick-Fuller test (ADF) whose null hypothesis is the presence of a unit root in the sample of the time-series against the alternative of stationarity or trend-stationarity. The second test is the Kwiatkowski-Phillips-Schmidt-Shin test (KPSS) whose null hypothesis is the stationarity (or trend-stationarity) against the alternative of the presence of unit root. In fact, as recommended in the must-read paper of Kwiatkowski et al. published in 1992, "Testing the null hypothesis of stationarity against alternative of unit root", one should perform both kinds of test in order to fully characterize a time-series. In the case of the methods chosen in this post, a high likelihood of stationarity in the sample of a time-series will outcome from the cases in which null hypothesis is rejected in the ADF test but accepted in the KPSS test.
Quote from Kwiatkowski´s paper. |
ADF test
The ADF in Python is within the powerful package statsmodel and the implementation is quite easy:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from statsmodels.tsa.stattools import adfuller | |
def perform_ADF_test(X): | |
ADF_test = adfuller(X) | |
print('ADF Statistic: %f' % ADF_test[0]) | |
print('p-value: %f' % ADF_test[1]) | |
print('Critical Values:') | |
for key, value in ADF_test[4].items(): | |
print('\t%s: %.3f' % (key, value)) | |
perform_ADF_test(vec_S) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Coin: BTC | |
ADF Statistic: -0.822876 | |
p-value: 0.812310 | |
Critical Values: | |
1%: -3.454 | |
5%: -2.872 | |
10%: -2.572 | |
Coin: ETH | |
ADF Statistic: -1.447629 | |
p-value: 0.559242 | |
Critical Values: | |
1%: -3.456 | |
5%: -2.873 | |
10%: -2.573 | |
Coin: TUSD | |
ADF Statistic: -4.618161 | |
p-value: 0.000120 | |
Critical Values: | |
1%: -3.462 | |
5%: -2.876 | |
10%: -2.574 |
Intuitively, the result is coherent, considering that BTC and ETH behaves more similarly to random walks whereas TUSD oscillates around the value of one dollar as initially proposed in the corresponding white paper.
KPSS test
Using KPSS we directly check the null hypothesis of stationarity in the time-series, which allows to complement the indication of the ADF test. The implementation is simple:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from statsmodels.tsa.stattools import kpss | |
def perform_kpss_test(X): | |
kpss_test = kpss(X) | |
print('KPSS Statistic: %f' % kpss_test[0]) | |
print('p-value: %f' % kpss_test[1]) | |
print('lags: %f' % kpss_test[2]) | |
print('Critical Values:') | |
for key, value in kpss_test[3].items(): | |
print('\t%s: %.3f' % (key, value)) |
providing us the following results:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Coin: BTC | |
KPSS Statistic: 1.219533 | |
p-value: 0.010000 | |
lags: 16.000000 | |
Critical Values: | |
10%: 0.347 | |
5%: 0.463 | |
2.5%: 0.574 | |
1%: 0.739 | |
Coin: ETH | |
KPSS Statistic: 1.349860 | |
p-value: 0.010000 | |
lags: 16.000000 | |
Critical Values: | |
10%: 0.347 | |
5%: 0.463 | |
2.5%: 0.574 | |
1%: 0.739 | |
Coin: TUSD | |
KPSS Statistic: 0.127700 | |
p-value: 0.100000 | |
lags: 15.000000 | |
Critical Values: | |
10%: 0.347 | |
5%: 0.463 | |
2.5%: 0.574 | |
1%: 0.739 |
Once again, for both BTC and ETH the statistics of the test is higher than the critical values and for TUSD the kpss statistics is lower than the critical values. These results lead us to accept the null hypothesis of stationarity for the TUSD and reject it for BTC and ETH. In order to be more certain about our analysis, I can also test BTC and ETH once again, this time with respect to the possibility of trend-stationarity, in which stationarity might be related to a mean value which decreases (or increases) linearly with time. This can be done by simply introducing the argument regression='ct' in the kpss test and the results are presented below:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Coin: BTC | |
KPSS Statistic: 0.186164 | |
p-value: 0.021188 | |
lags: 16.000000 | |
Critical Values: | |
10%: 0.119 | |
5%: 0.146 | |
2.5%: 0.176 | |
1%: 0.216 | |
Coin: ETH | |
KPSS Statistic: 0.136235 | |
p-value: 0.068084 | |
lags: 16.000000 | |
Critical Values: | |
10%: 0.119 | |
5%: 0.146 | |
2.5%: 0.176 | |
1%: 0.216 | |
Coin: TUSD | |
KPSS Statistic: 0.082340 | |
p-value: 0.100000 | |
lags: 15.000000 | |
Critical Values: | |
10%: 0.119 | |
5%: 0.146 | |
2.5%: 0.176 | |
1%: 0.216 |
In the example present above, the results of the ADF and KPSS complement themselves allowing to us to characterize the time-series with respect to unit root and stationarity. As mentioned before, stationarity is a worthful property for quantitative trading strategies. It is worth mentioning that even if a determined asset does not behaves suitably to the strategy, one can always try different portfolios that might behave as a stationary process, even for a short duration.
I hope you enjoyed the post. Leave your comments and share.
May the Force be with you.
#=================================
Diogo de Moura Pedroso
LinkedIn: www.linkedin.com/in/diogomourapedroso
#=================================
Diogo de Moura Pedroso
LinkedIn: www.linkedin.com/in/diogomourapedroso
Comments
Post a Comment