If you’re just joining us, this post is part of a blog series that details my journey while trying to tackle the world of algorithmic trading using crypto. See the table of contents below for more.
Table of Contents:
- Prologue: The Sirens Beckon
- Chapter 0: The Gods’ Blessing
- Chapter 1: Moving Averages Crossover
- Chapter 2: The BumpRider
- Chapter 3 (TBD): Mean Reversion Theory as it applies to Crypto
- Chapter 4 (TBD): Momentum Based Trading, formally
- Chapter 5 (TBD): Using Recurrent Neural Networks
- Chapter 6 (TBD): Augmenting RNN’s using Sentiment Analysis and NLP
The phrase “between Scylla and Charybdis” is often used as an idiom for having to pick between two hard choices. In Greek Mythology, ships had to sail away from the six-headed sea monster Scylla while also not get to close to the nearby whirlpool, Charybdis. Therefore, navigating between these two monsters is a tricky task. Steer too far left, and you may find yourself being sucked down to the abyss. Steer too far to the right, and you may find yourself being flung up into a sea monster’s mouth.
Can a deviation from the expected path be used to forecast future trends?
The Moving Averages Crossover (MAC) strategy is a well-known trading technique in financial time series analysis. A moving average, specifically a Simple Moving Averages, is the average of the last n periods for a given time series (SMA-n). There are more complicated types of moving averages such as the Exponential Moving Average. We will be sticking with SMA’s for now.
The idea behind the MAC strategy is very simple: You start with two moving average lines, one that uses a short period, and one that uses a longer period. When the short period crosses over the long period, this can be used as a signal that there is a trend moving upwards, and vice-versa for when it crosses below. We will be working with minute-long period increments. Now let’s look at an example visually using the hour SMA (SMA-60) and the day SMA (SMA-1440):
The plot has quite a few crossovers here. When the orange line initially crosses below the yellow in Crossover #1, we see that we should sell. Then, we should buy back at Crossover #2. Then at Crossover #7, we should sell again.
Now had we bought at Crossovers #3,4,5, and 6 we wouldn’t have actually been catching a strong trend shift. So in order to account for situations where this might be the case, we need to set a threshold that indicates how far below and above we should start to sell and buy.
The code for this is fairly simple and is a direct translation of the theory. We create a new subclass of the
Strategy class and call it
MovingAveragesCrossover . We set the
buy_qty to sell or buy our entire position. The justification for this is that there really isn’t any justification to do otherwise. Here is what the implementation will look like:
from Strategies.strategy import Strategy
THRESH_SELL = 0.98 # Percentage of long_ma to sell below
THRESH_BUY = 1.02 # Percentage of long_ma to buy above
MA_SHORT = 60 # short moving average look-back period, "n"
MA_LONG = 1440 # long moving average look-back period, "n"
def should_sell(self, **tick_info):
ma_short = tick_info['ma_short']
ma_long = tick_info['ma_long']
return ma_short < ma_long*MovingAveragesCrossover.THRESH_SELL def sell_qty(self, **position_info):
# Sell all our crypto
return position_info['crypto_position'] def should_buy(self, **tick_info):
ma_short = tick_info['ma_short']
ma_long = tick_info['ma_long']
return ma_short > ma_long*MovingAveragesCrossover.THRESH_BUY def buy_qty(self, **position_info):
# Buy with all our cash
And it’s as simple as that!*
Now let’s get some in-sample data from Athena** and see how well we do!
*I omitted a few methods here:
should_cancel_buy. You can assume for the purpose of simplicity all three simply return
False. After trying out some implementations for each, I decided the marginal benefit (+1-2%) added was not worth writing about.
** In the previous post, I mention how Athena splits up the data into Training, Validation, and Testing. This split is mainly for when we get the machine learning models. For the purposes of this post, we will be referring to the Training and Validation sets as in-sample data, and the Test sets as out-of-sample data. These are standard statistical forecasting terms and they seem much more appropriate to use here.
In order to evaluate how good our performance is, we need to have a benchmark that we can use to compare our strategy against. As is standard with cryptocurrency, we use HODLing (simply not doing any trades) as our benchmark to compare against.
We start off long 1 ETH. That was worth ~$9 at the start. Our strategy yields us: $422.44, however simply hodling our 1 ETH would have yielded us $734.97. What can be causing this? Let’s plot our position over time and color each line segment with the trading signal associated with that time step. Green will represent us wanting to buy (going long) and grey will represent us wanting to sell (being neutral). Blue, as before, is the price of ETH (and also our benchmark). We will use the same April 2017 snapshot we had above:
We see that during this period, even though ETH dropped in value ~14%, our position only lost around 3% (dropping from 35.7 to 34.7) however, we are not able to take advantage of this massive drop in price, we are only able to minimize the damages from it. How do we fix this? Short Selling! Basically, short selling means that we borrow some ETH from someone who owns ETH and immediately sell it, hoping to buy back when the price dips. This helps us make money during the periods where our strategy says there is a downward trend. Adding shorting to the
backtester , we see we fare a lot better (shorting is represented as red):
Even though our position starts off less than the benchmark, we quickly catch up and even surpass it for a while. Still, we don’t end up making any more money. In fact, we are still lower than the benchmark!
Actually though, as with almost all forecasting techniques, we don’t need to be right all the time. i.e. we don’t care if some trades end up losing up money, we just need to be right more often than not! Indeed, when we look at the entire in-sample performance we get the following:
Our strategy ends up earning us $1,792.26 whereas hodling left us at $734.97. That is almost 244% return over the benchmark!
Can we do better though?
Recall that we initialized our strategy’s parameters as follows:
THRESH_SELL = 0.98
THRESH_BUY = 1.02
MA_SHORT = 60
MA_LONG = 1440
How do we know that these are the optimal values for all four? We don’t! Let’s figure them out, however. You will have to trust me that
MA_LONG are at their best values, otherwise, it would require me to try and visualize the 4D space of all four parameters, and I still haven’t figured that one out yet. Setting
MA_LONG to 60 minutes and 1440 minutes, respectively, we get values that are only at or above 100% returns (1.0x the last HODL position). Backtesting values of
THRESH_SELL ranging from [0.95, 0.999], and
THRESH_BUY values in the range of [1.001, 1.066] we get the following heatmap:
We see that the algorithm really favors tighter thresholds, with a peak of ~6.4x at (1.006, 0.998)! Using those parameters to plot the backtest we get the following graph:
Pretty cool. Our algorithm ends up with $4,711.16 versus the benchmark $734.97. That is an in-sample alpha of about 642%! But in order for our results to be somewhat statistically valid, we need to test our performance on out-of-sample data.
We use the period between January 1st, 2018 and July 30th, 2018. At the start of the period, 1 ETH was worth around $752. However, by the end of the period, hodling would have left us with $459.82. That is the number to beat. Let’s see how well our strategy does on data we have never seen before:
Our strategy nets us $3,155.47! We see the strength of the SMA-Crossover strategy in responding to sharp drops in the price and being able to short the price just in time! This leads to quadrupling our initial investment and having an alpha of about 686%! Not bad!
Since the goal of this series is to approach cryptocurrency trading quantitatively, let’s try and quantify our performance.
Let’s start by looking at our 7-day rolling returns for both the benchmark and our strategy:
The rolling returns graph helps visualize a lot of concepts for us. First of all, we see that our strategy is remarkably good at hedging against sharp drops. We see that in some cases (around minute 50,000) we are even able to successfully move against this drop in price and rise and increase our returns! We also see that we never have extremely large drawdowns, which is very different from the benchmark which experiences wildly varying returns from period to period.
Where can our strategy do better? Well, for one, we see that we can never really beat the market when the price rises too fast. That makes sense as the moving average is a lagging indicator, and so our strategy cannot match the speed at which some price rises happen. This means that we can really never beat our benchmark when it rises or falls too fast, we can only catch up and react as best as possible.
Can we do better than a simple visual analysis? Is there a way to quantify how well our returns are in terms of the risk associated with our strategy?
The Sharpe Ratio is exactly what we need. It is a measure of risk-adjusted return of a strategy (or asset) against the risk-adjusted returns of a given benchmark. For assets in the normal world, the standard benchmark that is used is the 3-month T-bill. However, since we are working in the world of cryptocurrencies, we use our hodling benchmark as the one to beat.
The annualized Sharpe Ratio for our strategy’s 7-day return is ~2.46! Strategies are considered tradeable only if their Sharpe ratios are greater than one and extremely successful at Sharpe ratios greater than 2. It’s safe to say that this strategy is successful!
But how bad can things get? We turn to the drawdown next to answer this…
Looking at a graph of our drawdown over time we see the following:
It’s obvious that our strategy has a very low drawdown vs. the benchmark. In fact, our maximum drawdown is ~59% whereas the benchmark has a maximum drawdown of ~159%!
Now, obviously, a drawdown of ~59% is not ideal, but it is important to put into consideration that we are dealing with cryptocurrency, which is inherently extremely volatile. We are also doing a lot better than our benchmark.
In conclusion, we see that the SMA-Crossover strategy has a lot of applicability to Ethereum. We also see that it is a good strategy that is effective at hedging against strong market movements by taking advantage of fluctuating market trends. It can be lucrative, but we must be able to take advantage of both the rises and the falls. Even then, we will always have some response lag because of the nature of the indicators used.
So it seems with all this information that this strategy is an easy moneymaker! Why not start trading this right away? As with most things in life, many assumptions of this backtest break when faced with the real world.
- As mentioned before, there is a trade delay when executing trades. This is due to exchanges sometimes not having enough volume. Odysseus has a built-in mechanism that assumes a random execution delay of up to 10 minutes, but this still may not be enough in some cases
- Short-selling cryptocurrencies requires Margin Trading on exchanges, which requires you to have some form of collateral in your margin account and will force close your short position if you lose too much. Also, most crypto exchanges are not available in the US.
- Trading Fees: I haven’t accounted for trading fees here. Most exchanges charge around a 0.1% trading fee which does need to be taken into account.
These points may seem simple to get around, but it’s details such as these that may eliminate any returns attainable from such a strategy.
In the next chapter, we will be venturing out of Ancient Greece to the Wild West to examine the BumpRider!