vectorbt icon indicating copy to clipboard operation
vectorbt copied to clipboard

Bug: Stop loss results is very largely different between from_signals(sl_stop=0.1) and generate_ohlc_stop_exits(sl_stop=0.1)

Open tan-yong-sheng opened this issue 2 years ago • 3 comments

I'm trying to compare the stop loss mechanisms implemented with different ways such as model 1: from_signals(sl_stop=0.1, sl_trail=False), model 2: generate_ohlc_stop_exits(sl_stop=0.1, sl_trail=False) and model 3: vbt.OHLCSTX.run(sl_stop=[0.1]).

I found that model 2 and model 3 gave the exact same results (i.e., total return=459.05%) , but for model 1, the results are very different (i.e., total return = 47.47%) as displayed below:

Model 1: from_signals(sl_stop=0.1, sl_trail=False) image

Model 2: generate_ohlc_stop_exits(sl_stop=0.1, sl_trail=False) image

Model 3: vbt.OHLCSTX.run(sl_stop=[0.1]) image

Is it a bug? Or is it my implementation error? And btw, here is where I got the reference from: https://github.com/polakowo/vectorbt/issues/181

Below are the codes for these 3 models:

Model 1: Using from_signals(sl_stop=0.1, sl_trail=False)

# Reference: stop exits with RANDENEX indicator: https://github.com/polakowo/vectorbt/issues/181
import vectorbt as vbt

ohlcv = vbt.YFData.download(
    "BTC-USD",
    start='2017-01-01 UTC',
    end='2020-01-01 UTC'
).concat()
# Random enter signal generator based on the number of signals.
rand = vbt.RAND.run(ohlcv["Close"].shape, n=10, seed=42)
# Random exit signal generator based on the number of signals.
randx = vbt.RANDX.run(rand.entries, seed=42)
pf1 = vbt.Portfolio.from_signals(ohlcv["Close"], 
                                rand.entries, 
                                randx.exits,
                                open=ohlcv["Open"],
                                high=ohlcv["High"],
                                low=ohlcv["Low"],
                                sl_stop=0.1,
                                sl_trail=False,
                                )
pf1.stats()

Model 2: Using generate_ohlc_stop_exits(sl_stop=0.1, sl_trail=False)

import vectorbt as vbt

ohlcv = vbt.YFData.download(
    "BTC-USD",
    start='2017-01-01 UTC',
    end='2020-01-01 UTC'
).concat()

# Random enter signal generator based on the number of signals.
rand = vbt.RAND.run(ohlcv["Close"].shape, n=10, seed=42)
# Random exit signal generator based on the number of signals.
randx = vbt.RANDX.run(rand.entries, seed=42)

stop_exits = rand.entries.vbt.signals.generate_ohlc_stop_exits(
    open=ohlcv["Open"], 
    high=ohlcv['High'],
    low=ohlcv['Low'], 
    close=ohlcv['Close'], 
    sl_stop=0.1,
    sl_trail=False,
)
exits = randx.exits.vbt | stop_exits # optional: combine exit signals such that the first exit of two conditions wins
entries, exits = rand.entries.vbt.signals.clean(exits) # optional: automatically remove ignored exit signals

pf2 = vbt.Portfolio.from_signals(ohlcv['Close'], entries, exits,
                                 open=ohlcv["Open"],high=ohlcv["High"],
                                 low=ohlcv["Low"])
pf2.stats()

Model 3: Using vbt.OHLCSTX.run(sl_stop=[0.1])

import numpy
import vectorbt as vbt

ohlcv = vbt.YFData.download(
    "BTC-USD",
    start='2017-01-01 UTC',
    end='2020-01-01 UTC'
).concat()

# Random enter signal generator based on the number of signals.
rand = vbt.RAND.run(ohlcv["Close"].shape, n=10, seed=42)
# Random exit signal generator based on the number of signals.
randx = vbt.RANDX.run(rand.entries, seed=42)

stops = [0.1,]
sl_exits = vbt.OHLCSTX.run(
    rand.entries, 
    ohlcv['Open'], 
    ohlcv['High'], 
    ohlcv['Low'], 
    ohlcv['Close'], 
    sl_stop=list(stops),
    stop_type=None, 
    stop_price=None
).exits
exits = randx.exits.vbt | sl_exits

pf3 = vbt.Portfolio.from_signals(ohlcv['Close'], rand.entries, exits)  # with SL
pf3.stats()

tan-yong-sheng avatar Aug 27 '23 05:08 tan-yong-sheng

The model 1 and 2 & 3 (they are the same thing) differ in only one thing: 1 executes orders using the hit price, and 2 & 3 execute orders using the close price. You can make model 1 execute stops using close as well by providing a proper stop_exit_price, or you can extract the hit price when working with models 2 & 3 and use it as order price to match the first model.

polakowo avatar Sep 15 '23 21:09 polakowo

so, is it possible to change it to trade on next open price instead of current close price?

tan-yong-sheng avatar Sep 25 '23 01:09 tan-yong-sheng

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

github-actions[bot] avatar Feb 04 '24 01:02 github-actions[bot]

This issue was closed because it has been inactive for 14 days since being marked as stale.

github-actions[bot] avatar Feb 19 '24 01:02 github-actions[bot]