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

Aug 7 · 11 min read

# 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.

# 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
VELOCITY_LOOKBACK = 10
# Threshold at which we determine we're experiencing a bump
THRESH_VELOCITY = 0.02
# Threshold to cut our losses and go long (from short position)
# Threshold to cut our losses and go short (from long position)
THRESH_STOPSHORT = 0.03
# Threshold at which to cancel a pending buy order
# Threshold at which to cancel a pending shortorder
THRESH_CANCEL_SHORT = 0.99

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

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
(long_price*(-long_vel)*BumpRider.THRESH_STOPSHORT
return price < cutoff

# 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)

# current_price
price = tick_info['price']
# price where we started being long
long_price = tick_info['long_price']

# 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:

# Tuning

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_short = [0.95, 0.97, 0.99]
"""
VELOCITY_LOOKBACK    = 10
THRESH_VELOCITY = 0.01
THRESH_STOPSHORT = 0.2
THRESH_CANCEL_SHORT = 0.99

Rolling Returns

# Summary

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.

# Caveats

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.

Written by

## Youssef Victor

#### Computer Science and Data Science Master‘s Graduate interested in applying Statistics and Machine Learning to real-world trading. https://tinyurl.com/YVLinkedIn

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