The Odyssey — Chapter Two: Taking The Bullshit by the Horns

Youssef Victor
Aug 7 · 11 min read

Table of Contents:

The Theory

One thing that many people discover when entering the world of cryptocurrencies, is that it is truly like the wild west. By its very own nature, there is very little regulation on how you can trade a cryptocurrency as they are meant to be a decentralized form of currency (most of them, anyway). This decentralization means that their worth is by definition not backed by any tangible economic entity such as a company, a commodity, or a country (most ‘classic’ ones, anyway). This has led to its incredibly high volatility as market sentiment can rapidly influence a currency’s price, much faster than with traditional assets such as stocks, commodities, and currencies.

ETHUSD between July 1st, 2019 and July 17th, 2019 — Example of manipulation can be seen on the 7th of July
It’s going to be a bumpy ride

The Plan

Since we are not the manipulators here, we can only work retroactively. If we see a sudden sharp spike in the price, we know that this it is whale season, and it is time to short as we know the price will fall back down soon. When do we buy? Well, if the price drops to what it was before the spike, then we buy back again and vice-versa for sharp drops in the price as well.

The Code

The code for this strategy is also simple to follow. It adheres to the principles mentioned above:

from Strategies.strategy import Strategy

class BumpRider(Strategy):

# Lookback used to calculate velocity
# Threshold at which we determine we're experiencing a bump
# Threshold to cut our losses and go long (from short position)
# Threshold to cut our losses and go short (from long position)
# Threshold at which to cancel a pending buy order
# Threshold at which to cancel a pending shortorder

def should_short(self, **tick_info):
velocity = tick_info['velocity'] # current_velocity
return (velocity > BumpRider.THRESH_VELOCITY)

def should_buy(self,**tick_info):
velocity = tick_info['velocity'] # current_velocity
return (velocity < -BumpRider.THRESH_VELOCITY)

def should_stopshort(self, **tick_info):
# current_price
price = tick_info['price']
# price where we started being long
long_price = tick_info['long_price']
# velocity where we started being long
long_vel = tick_info['long_vel']-1

# Our position is dropping fast and we're not riding -> sell
return price < cutoff

def should_stopbuy(self, **tick_info):
# current_price
price = tick_info['price']
# price where we became short
short_price = tick_info['short_price']
# velocity when we became short
short_vel = 1+tick_info['short_vel']

# Our position is rising fast and we're not riding -> buy
cutoff =
return price > cutoff

def should_cancel_short(self, **tick_info):
# current_price
price = tick_info['price']
# price where we became short
short_price = tick_info['short_price']

return price < (short_price*BumpRider.THRESH_CANCEL_SHORT)

def should_cancel_buy(self, **tick_info):
# current_price
price = tick_info['price']
# price where we started being long
long_price = tick_info['long_price']

return price > (long_price*BumpRider.THRESH_CANCEL_BUY)

In-Sample Results

Remember that our benchmark starts off at ~$9 and reaches an end of year total of $734.97. Let’s see how well our bump-rider strategy performs:

BumpRider Performance for ETHUSD for all of 2017 vs. HODLing (blue)


We have six parameters that need to be tuned here. Trying to display that on a heatmap would be really hard, and so we’re going to have to skip that part for this strategy.

Testing the following values for each parameter:
velocity_lookback = [10, 20, 30, 60]
thresh_velocity = [0.02, 0.035, 0.01, 0.05, 0.1, 0.2]
thresh_stopbuy = [1.01, 1.025, 1.05, 1.1, 1.15]
thresh_stopshort = [0.2, 0.5, 0.7]
thresh_cancel_buy = [1.01, 1.05, 1.1]
thresh_cancel_short = [0.95, 0.97, 0.99]
Optimized BumpRider in-sample performance vs Benchmark (Blue)

Out-of-Sample Performance

Optimized BumpRider in-sample performance vs Benchmark (Blue)

Further Analysis

Rolling Returns

7-Day Rolling Returns of our benchmark (blue) vs our BumpRider strategy (Green when >0, Red when below)
BumpRider 7-day Returns histogram
BumpRider total drawdown (red) vs. Benchmark (blue)


The BumpRider seemed like a very easy concept when we first started. If the price shoots up really fast, we sell. Once it falls back down, we buy. If it doesn’t we cut our losses. We were able to make amazing (imaginary) returns of 1,500,000% on our in-sample data, but we quickly realized that we were just writing an algorithm that perfectly fits the data we were working off of.


The caveats for this chapter are basically the same as in the previous one. However, it is also important to note that the in-sample data is misrepresentative of real-world performance because of more than just us tuning our strategy to it. As our position size grows, orders take increasingly longer to fill. For example, at some point in the in-sample data backtest, our position reached nearly $160,000 at a time when ETH was trading close to $800. In real life, it would be really difficult to trade on a minute-level scale with that amount of money. Our backtest does take into account an up to 10-minute random trade delay, but even then, it is impractical to hinge a strategy on moving 200 ETH within the span of 10 minutes.

    Youssef Victor

    Written by

    Computer Science and Data Science Master‘s Graduate interested in applying Statistics and Machine Learning to real-world trading.

    Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
    Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
    Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade