18 Comments

I may have missed it but does the model require you to hold a losing trade until this trigger is met “Close the trade whenever the SPY close is higher than yesterday's high.” I.e. there is no separate stop loss mechanism?

Expand full comment

In the "Improvement 2" section, we hold the trades until either of the following is triggered:

- Close the trade whenever the price is higher than yesterday's high (same as before);

- Close the trade whenever the price is lower than the 300-SMA (new condition).

This is more than enough. The first event (price closing higher than yesterday's high) is pretty frequent: it occurs on average once every 3 days :)

Expand full comment

Hi there, have you assumed any commissions?

Expand full comment

Not in this exercise... I assumed slippage only (1 basis point against all transactions)

Expand full comment

Hi, when you refer to the rolling high over 10 days in bullet 3, do you mean the mean of high over a 10 day window?

Expand full comment

Hi Mike! No, it’s rolling high. In python:

df[(asset, 'Band High')] = t[(asset, 'High')].rolling(window=10).max()

Expand full comment

well done!

Expand full comment

Do you enter the trade at the close, or the next day's open?

Expand full comment

Hi! Always next day's open

Expand full comment

Consider to apply this model on 20 other equity indices to see if this is a robust one.

Expand full comment

Good idea! Will do that, thx!

Expand full comment

Hello, thanks for sharing the interesting post! I try to re-implement it, and found that the performance is worse in Improvement 2. My question is the market source is from yahoo finance or not?

Expand full comment

I'm currently using Norgate data and Sharadar Core US Equities Bundle.

But I've used free datasets such as Stooq and Yahoo Finance in the past.

The problem with free datasets is that the quality is extremely low.

They frequently forget to adjust for corporate actions, there are missing values, discontinuities, etc...

Expand full comment

Thanks for the reply! I think I've found the bug :). And another question about the "Summary of the backtest statistics", what's the difference between "Buy&Hold" and "S&P 500", I know "Buy&Hold" strategy, but how do you calculate "S&P 500" column? Really appreciate your time!

Expand full comment

Thanks! If the target asset is QQQ, for instance, Buy&Hold means B&H QQQ.

Cheers

Expand full comment

Thanks for the explanation!

Expand full comment

I've tried to reproduce your results. But I got much less trades in the similar timeframe.

Here are my snippets:

def high_low_mean(high: pd.Series, low: pd.Series, interval: int) -> pd.Series:

return (high - low).rolling(interval).mean()

def lower_band(high: pd.Series, hl: pd.Series, intervall:int, factor: float) -> pd.Series:

return high.rolling(intervall).high() - factor * hl

stock["ibs"] = (stock.Close - stock.Low) / (stock.High - stock.Low)

The Roule is to buy on next days open on following condition (-1 means current day):

if (

(_close < self.lower_band[-1])

and (self.ibs[-1] < (self.ibs_low))

and (_low > self.sma[-1])

):

self.buy(sl=self.sma[-1])

Trade management:

if trade.is_long:

# update trailing stop

trade.sl = max(trade.sl, self.sma[-1])

# close on next day open

if _close > self.data.High[-2]:

trade.close()

Expand full comment

Hi @Markus,

I'm not familiar with this backtesting framework you are using. So, when you call [-1], I don't know what you are referring to. I assume it's the current bar's (today) close.

Also, I don't know if you are placing orders at the day's open or the day's close. That makes quite a difference. I'm assuming orders will be placed next day's open.

Finally, there are some things funny with the logic:

- You should only buy if you are not positioned (if trade.is_long).

- I'm not using trailing stops, but a dynamic stop: if the price drops below the SMA, get out immediately.

Hope it helps!

Expand full comment