Optimal Stop-Loss for Crypto Trading

QFX Research
QFX Research
Published in
8 min readFeb 26, 2019

Stop-loss strategies are used by investors and traders to limit potential losses to acceptable levels, depending on their risk appetite. Most of the literature that examines stop-loss strategies focuses on their effect on long term buy and hold investments.

In general, there is a consensus among researchers that stop losses can help reduce the volatility of returns. There are, however, conflicting views as to whether stop losses can enhance returns, with negative, neutral and positive results having all been reported.

This study tries to give a shorter term perspective of stop losses in the crypto currency markets. We try to find the optimal stop loss strategy and stop level, using XBTUSD prices from Bitmex.

We have at our disposal 1 minute OHLC bid and ask quote bars. We assume ur trades always last for 30 minutes, unless stops or take profits are triggered. For this reason, we will use 30 minute OHLC bid and ask bars to record the market state at the time of trade entry, namely to calculate volatility and autocorrelation. We will use the 1 minute data to monitor the development of the trades and to calculate out P&L.

Data analysis

We load the 1 minute bar data for XBTUSD on Bitmex (https://public.bitmex.com/?prefix=data/quote/) and downsample it to 30 minute bars.

data_1min = pd.read_csv(‘./data/resampled_minute.csv.gz’,
index_col=0,
parse_dates=[0],
compression=’gzip’)
bid_30min = data_1min[‘bid_close’].resample(‘30Min’).ohlc()
ask_30min = data_1min[‘ask_close’].resample(‘30Min’).ohlc()
data_30min = pd.concat([bid_30min, ask_30min], axis=1)
data_30min.columns = data_1min.columns
XBTUSD prices are characterised by a relatively stable rise in 2016, steep rise and 2017, followed by a violent and volatile decline in 2018.
returns_30min = calculate_price_returns(data_30min.bid_close)
returns_30min.describe()

Let’s look at 30 minute returns. XBTUSD can be extremely volatile, with returns ranging from -18% to 10%. Huge spikes are commonplace. The volatility varies with time, peaking towards the end of 2017.

returns_30min.plot()
plt.title('XBTUSD 30 minute returns')
returns_30min.hist(bins=50)
plt.title('Histogram of 30 minute returns')

Below we calculate and plot rolling standard deviation using a 1-week window of 30 minute returns. As noted earlier, volatility exhibits regular spikes and peaks at the end of 2017 and beginning of 2018.

rolling_vollatility = returns_30min.rolling(window=WINDOW_SIZE).std()
rolling_vollatility.dropna(inplace=True)
rolling_vollatility.plot()
plt.title('Rolling Volatility, 1 week trailing window')

We also calculate rolling autocorrelation using the same trailing window size. Positive autocorrelation indicates a trending market while negative autocorrelation means increased probability that a large return will be followed by another large return in the opposite direction.

The dashed lines in the autocorrelation plot are the 95% confidence interval for the null hypothesis of no autocorrelation. We see that there are periods of significant positive and negative autocorrelation but they tend to end quickly, except for a period at the end of 2018, when we had a prolonged period of significant negative autocorrelation.

rolling_autocorrelation = returns_30min.rolling(window=WINDOW_SIZE).apply(lambda x: x.autocorr(), raw=False)
rolling_autocorrelation.dropna(inplace=True)
conf_interval = 1.96/np.sqrt(WINDOW_SIZE)plt.figure()
rolling_autocorrelation.plot()
plt.axhline(y=0, color='r', linestyle='-')
plt.axhline(y=conf_interval, color='r', linestyle='--')
plt.axhline(y=-conf_interval, color='r', linestyle='--')
plt.title('Rolling autocorrelation, 1 week trailing window')
plt.show()

Approach of study

Our approach to examining the effect of stop losses on trading prfitability in XTBUSD will be to simulate a number of trading histories for a single trading strategy that trades on average 5 times per day. Both long and short trades are allowed. As mentioned earlier, our trades will last for exactly 30 minutes, unless the stop is hit. We will use bootstrapping techniques to simulate the trading histories.

Following the literature, we will assess the performance of different stop-loss strategies relative to the no stop case(hold for 30 minutes), which will be used as a benchmark. WE assume that each trade is made via a market order, i.e. buys are executed at the prevailing ASK quote and sells are executed at the prevailing BID quote. No slippage is factored in since we are interested in relative rather than absolute performance.

We will distinguish between stop-loss strategies according to two criteria:

  1. Whether they are defined in terms of price level (% of current price) or as units of rolling standard
    deviation as calculated above.
  2. Whether they are fixed at entry time or trailing as price moves.

This gives in total 4 stop-loss strategies:

a) ‘fixed_pcnt_price’ — fixed stop loss as a percentage of price

b) ‘fixed_vol_unit’ — fixed stop loss as a unit of the rolling standard deviation

c) ‘trailing_pcnt_price’ — trailing stop loss as a percentage of price

d) ‘trailing_vol_unit’ — trailing stop loss as a unit of the rolling standard deviation

We associate 10 stop loss levels for each strategy, expressed in either percentage of price at time of entry, or as a unit of standard deviation, calculated over a rolling window at time of entry.

To assess performance, for each simulated trading history, we calculate the total returns, risk adjusted returns and volatility of the four stop-loss trading strategies and the no stop case. We then subtract the returns and volatility of the no stop case from those of the stop loss strategies. The results are averaged over all simulated trading histories.

We also test whether the average returns of the stop loss strategies and the no stop case are equal using a two-sided t-test.

It is generally accepted by researchers that stop losses reduce the volatility of returns. There is disagreement as to the influence of stop losses on the magnitude of returns. Kaminski and Lo (2008) find theoretically that stop loss strategies can improve returns under momentum and regime-switching models, but hurt returns when the return generating process follows a random walk. To check this result empirically, we will relate stop-loss strategy performance to autocorrelation at time of trade entry.

Stop loss strategies

def get_result(n_trades=N_TRADES,
m_histories=M_HISTORIES,
holding_period=HOLDING_PERIOD):
result = []for m in tqdm(range(m_histories)):
# Simulate entry points for one trading history
entry_points = data_1min.index[simulate_trades(data_1min.shape[0], n_trades)]

# Make sure we don't enter a trade before we can calculate our metrics
entry_points = entry_points[entry_points >= rolling_vollatility.index[0]]

# Simulate direction for each trade in the trading history (long or short)
trade_direction = np.random.choice(np.array([1, -1]), size=len(entry_points))

# Calculate volatility and autocorrelation at the time or just before each entry point
entry_point_vol, entry_point_autocorr = calculate_entry_point_metrics(entry_points=entry_points,
rolling_vol=rolling_vollatility,
rolling_autocorr=rolling_autocorrelation)

res = calculate_trade_returns(entry_points=entry_points,
trade_direction=trade_direction,
volatility=entry_point_vol,
autocorr=entry_point_autocorr,
quotes=data_1min,
holding_period=holding_period)
result.append(res)

return result

We calculate and summarise results

result = get_result()
result_returns, result_volatility, result_ra_returns, result_tests = extract_return_difference(result)

Plotting the results for total returns, we see that tight stop losses worked best and outperformed the no-stop case. We also see that stops fixed at entry time perform better than trailing stops. Stops based on price and volatility perform very similarly. Stops with large thresholds perform similarly to the no-stop case since they are rarely hit.

plt.figure()
result_returns['fixed_pcnt_price'].mean(axis=0).plot(label='fixed_pcnt_price')
result_returns['trailing_pcnt_price'].mean(axis=0).plot(label='trailing_pcnt_price')
plt.axhline(y=0, color='r', linestyle='--', label='Hold for 30 minutes')
plt.xlabel('Size of stop in terms of price percentage')
plt.ylabel('Return difference from hold strategy')
plt.legend()
plt.title('Total returns, stop loss strategies based on percentage of price')
plt.show()
plt.figure()
result_returns['fixed_vol_unit'].mean(axis=0).plot(label='fixed_vol_unit')
result_returns['trailing_vol_unit'].mean(axis=0).plot(label='trailing_vol_unit')
plt.axhline(y=0, color='r', linestyle='--', label='Hold for 30 minutes')
plt.xlabel('Size of stop in terms of units of standard deviation')
plt.ylabel('Return difference from hold strategy')
plt.legend()
plt.title('Total returns, stop loss strategies based on units of standard deviation')
plt.show()

The plot below shows the p_values for a two-sided t-test performed for each simulated trading history. We see that average returns for one of the better performing stop loss strategies are significantly higher than the no stop case.

result_tests['fixed_vol_unit'][0.2].hist()
plt.title('Histogram of t-test p values')
plt.show()

Plotting results for volatility we see again that tight stops perform best. In case of volatility trailing stops outperform fixed stops.

plt.figure()
result_volatility['fixed_pcnt_price'].mean(axis=0).plot(label='fixed_pcnt_price')
result_volatility['trailing_pcnt_price'].mean(axis=0).plot(label='trailing_pcnt_price')
plt.axhline(y=0, color='r', linestyle='--', label='Hold for 30 minutes')
plt.xlabel('Size of stop in terms of price percentage')
plt.ylabel('Volatility difference from hold strategy')
plt.legend()
plt.title('Volatility, stop loss strategies based on percentage of price')
plt.show()
plt.figure()
result_volatility['fixed_vol_unit'].mean(axis=0).plot(label='fixed_vol_unit')
result_volatility['trailing_vol_unit'].mean(axis=0).plot(label='trailing_vol_unit')
plt.axhline(y=0, color='r', linestyle='--', label='Hold for 30 minutes')
plt.xlabel('Size of stop in terms of units of standard deviation')
plt.ylabel('Volatility difference from hold strategy')
plt.legend()
plt.title('Volatility, stop loss strategies based on units of standard deviation')
plt.show()

Results for risk-adjusted returns are similar to those for total returns, but the performance gap between fixed and trailing stops is smaller as volatility performace of trailing stops is better.

plt.figure()
result_ra_returns['fixed_pcnt_price'].mean(axis=0).plot(label='fixed_pcnt_price')
result_ra_returns['trailing_pcnt_price'].mean(axis=0).plot(label='trailing_pcnt_price')
plt.axhline(y=0, color='r', linestyle='--', label='Hold for 30 minutes')
plt.xlabel('Size of stop in terms of price percentage')
plt.ylabel('Risk adjusted return difference from hold strategy')
plt.legend()
plt.title('Risk-adjusted returns, stop loss strategies based on percentage of price')
plt.show()
plt.figure()
result_ra_returns['fixed_vol_unit'].mean(axis=0).plot(label='fixed_vol_unit')
result_ra_returns['trailing_vol_unit'].mean(axis=0).plot(label='trailing_vol_unit')
plt.axhline(y=0, color='r', linestyle='--', label='Hold for 30 minutes')
plt.xlabel('Size of stop in terms of units of standard deviation')
plt.ylabel('Risk adjusted return difference from hold strategy')
plt.legend()
plt.title('Risk-adjusted returns, stop loss strategies based on units of standard deviation')
plt.show()

Finally, we will see how autocorrelation and volatility affect performance of stop loss strategies. In summary, there is no strong relationship between stop-loss strategy performance and autocorrelation and volatility, at least as measured by us at or close to the entry time of each trade.

vol_autocorr_result = extract_volatility_autocorelation(result=result,
strategy='fixed_pcnt_price',
thresh=0.002)
vol_autocorr_result.corr()

Sources and further reading

  • Efficiency of Stop-Loss Rules, Robin Erdestam & Olof Stangenberg
  • The Value of Stop Loss Strategies, Adam Y.C. Lei & Adam Y.C. Lei
  • Assessing Stop-loss and Re-entry Strategies, Joachim Klement
  • Optimal Trading Stops and Algorithmic Trading, Giuseppe Di Graziano
  • When Do Stop-Loss Rules Stop Losses?, Kathryn M. Kaminski, Andrew W. Lo
  • The value of stop-loss, stop-gain strategies in dynamic asset allocation, Austin Shelton

--

--