polakowo / vectorbt

Find your trading edge, using the fastest engine for backtesting, algorithmic trading, and research.
https://vectorbt.dev
Other
4.39k stars 620 forks source link

Trailing Stop -- totally confused #669

Open bob-mcrae opened 11 months ago

bob-mcrae commented 11 months ago

Thank you for sharing this excellent library! I hope I can learn to use it to its potential.

Sorry if I have missed something fundamental, but after reviewing issues, documentation, and all other info sources I remain confused how to properly set a trailing stop. Essentially, I want to set a trailing stop at various levels (eg: 0-100%). My entries are based on a simple custom indicator, which is shifted 1-day to prevent look-ahead bias. Below that, I am just merging some of the data to see the entries next to the exits, along with some columns to help understand how this configuration is and/or should be working (run_max_close, run_close_pct, run_low_pct). For example, run_max_close is the trailing price which I think should be used for triggering a trailing stop loss. run_close_pct is the percent below that trigger price. So, in this example, trailing stop losses should occur when run_close_pct > 0.01. However, you can see that an exit should have been triggered on 2022-12-07, but was not. In fact, the exit did not occur until 2022-12-09, when the run_close_pct was 0.005 (below the trailing stop).

Lastly, I want to be able to generate exits in a manner similar to this so I can see them easily to validate the logic.

Any insight as to what I am doing wrong and/or pointer to the best, current documentation or example would be much appreciated.

ohlcv       = vbt.YFData.download('AAPL', period='1y', auto_adjust=False).get(['Open', 'Low', 'High', 'Close', 'Volume'])
entries     = pd.Series(smoothed_returns_np(ohlcv.to_numpy(), 5, 5)[:,0], index=ohlcv.index, name='entries').shift().bfill() > 0
ts_exits    = vbt.OHLCSTX.run(
    entries,
    ohlcv['Open'],
    ohlcv['High'],
    ohlcv['Low'],
    ohlcv['Close'],
    sl_stop=[0.01],
    sl_trail=True,
    stop_type=None,
    stop_price=None
).exits

# merge some columns together to see results together
merge_ = pd.merge(entries, ts_exits.rename('exits'), left_index=True, right_index=True)
merge_ = pd.merge(merge_, ohlcv, left_index=True,right_index=True)

# calculate a rolling max, based on the 'close' of the first entry
first_entry_idx = np.argmax(entries==True)
merge_['run_max_close'] = np.concatenate( (np.full(first_entry_idx, np.nan), running_max(merge_.Close.values[first_entry_idx:]) ))
merge_['run_close_pct'] = merge_['Close'] / merge_['run_max_close'] - 1
merge_['run_low_pct']   = merge_['Low']   / merge_['run_max_close'] - 1
merge_.head(first_entry_idx + 10)

image

bob-mcrae commented 11 months ago

Related to this, I would also like to know whether it's possible to configure trailing stops with the following behavior:

github-actions[bot] commented 8 months ago

This issue is stale because it has been open for 90 days with no activity.