Skip to content
All library documents

Testing Trailing Stops and Fixed Profit-Loss Exits with Random Entries

Article Quant Q&A · Author: SuperCodeBrah

Summary

The document describes a test of exit rules in short-term foreign exchange data using randomly selected entry points. It compares a trailing stop, which closes a position after gains retrace by a preset amount, with a fixed threshold that exits when either a gain or loss crosses the same amount. The reported test uses 2019 one-minute EUR/USD prices and presents average returns, dispersion, extremes, and holding times for long and short positions.

In these results, trailing-stop trades have small negative average returns on both sides, while the fixed symmetric exit shows nearly offsetting results for long and short positions. The author wonders whether stops can be adjusted to turn the apparent drag into gains. The test is exploratory: entries are random, and the document notes that spread comparisons exclude commissions. It does not establish that either exit rule is profitable in a tradable strategy, and the shown results do not account for other execution effects or selection of entry signals.

Key ideas

  • Random entry points are used to compare trailing and fixed symmetric exit rules.
  • The trailing stop exits after a position retraces a preset amount from its best gain.
  • The fixed exit closes when the preset gain or loss threshold is reached.
  • The reported trailing-stop averages are slightly negative for both long and short positions.
  • The results are exploratory and omit commissions, so they do not establish live profitability.

Tags

Full text
# Forex trailing stops - better alternatives?


# Forex trailing stops - better alternatives?












I've been pursuing the holy grail of trading, short term FX trading, using machine learning. I've experimented with a ton of strategies but mainly those revolving around holding each trade for a fixed duration, however, I think a better strategy can be pursued by training on data where the y-value represents the actual return given some set of rules. For example, if a stop loss were used, the gain-loss value could be calculated for each training example given the stop loss rules and those values could be used to train the model. Then when the model is deployed, the same rules would be used.

I wanted to test out how stop loss methods would perform if the entry points were picked at random to determine if they create any kind of headwind or tailwind in normal market conditions. Here's the function that I'm using to do that:

```
def stop_loss_random_test(prices, side="long", stop_type="trailing", stop_loss=0.0005, num=1000, forward=1000):
    
    np.random.seed(0)
    prices_indices = np.arange(prices.shape[0])
    random_indices = np.random.choice(prices_indices[:-forward], num, replace=False)
    forward_range = np.arange(forward)

    multiplier = 1 if side == "long" else -1
    entry_price = None
    max_gain = None
    gains = []
    durations = []
    
    for i in random_indices:

        entry_price = prices[i]
        max_gain = 0
        
        for p in forward_range:
            gain = prices[i+p] / entry_price - 1
            gain *= multiplier
            max_gain = max(gain, max_gain)
            
            if stop_type == "trailing":
                close_position = gain < max_gain - stop_loss
            elif stop_type == "race":
                # position is closed if stop_loss amount is lost OR gained
                close_position = abs(gain) > stop_loss
            else: raise Exception("Invalid stop type")
            
            if close_position or p == forward - 1:
                gains.append(gain)
                durations.append(p)
                entry_price = None
                trailing_stop = None
                break
    
    
    gains = np.array(gains)
    
    print("Stop loss results:", side, stop_type)
    print("Mean", gains.mean())
    print("Std", gains.std())
    print("Min", gains.min()) # only using close - loss can exceed stop amount
    print("Max", gains.max())
    print("Count", gains.shape[0])
    print("Average holding time:", np.array(durations).mean())
```

Note that when the `stop_type` parameter is set to `race`, the stop loss value remains the same throughout the trade and the trade is exited when the amount of the stop loss, e.g., 0.05% (using percentages instead of pips for easier calculation) is exceeded as a gain OR loss, i.e., the stop-loss amount is also the take-profit amount. This yields the following results using 2019 1-minute EURUSD prices:

```
Stop loss results: long trailing
Mean -6.676326476038819e-05
Std 0.0005539449863005583
Min -0.0019789614410355982
Max 0.003713987268880059
Count 1000
Average holding time: 101.665

Stop loss results: short trailing
Mean -1.4801988162509883e-05
Std 0.0006089588441524955
Min -0.0018916471674601532
Max 0.006041128848146338
Count 1000
Average holding time: 106.612

Stop loss results: long race
Mean -2.622936566547718e-05
Std 0.0005885432783222677
Min -0.0019789614410355982
Max 0.0018916471674601532
Count 1000
Average holding time: 104.207

Stop loss results: short race
Mean 2.622936566547718e-05
Std 0.0005885432783222677
Min -0.0018916471674601532
Max 0.0019789614410355982
Count 1000
Average holding time: 104.207
```

What's interesting to me about this is that the trailing stop loses regardless of whether long or short positions are entered. Granted, these are very small numbers, but the average of around -0.004% is greater than the usual market spread for the EURUSD using a broker such as Dukascopy (not including commissions).

I would not have expected this and would probably more have expected the opposite (i.e., that on short time frames, momentum would allow the trailing stop method to result in small gains on average). Using the `race` option fixes the imbalance, but I'm not sure how practical it is for actual trading.

I understand the value of trailing stops and I also understand how small these numbers would appear to anyone trading on a longer time frame, but I'm wondering if there's a way to still use stops but to turn that small loss into a small gain with each trade.

Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.