For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/
Share my implementation of the trend following strategy in the book "Following the trend" by Andreas Clenow
-
I implemented the trend following strategy in the book "Following the trend" by Andreas Clenow. The following is the core logic:
class ClenowTrendFollowingStrategy(bt.Strategy): """The trend following strategy from the book "Following the trend" by Andreas Clenow.""" alias = ('MeanReversion',) params = ( ('trend_filter_fast_period', 50), ('trend_filter_slow_period', 100), ('fast_donchian_channel_period', 25), ('slow_donchian_channel_period', 50), ('trailing_stop_atr_period', 100), ('trailing_stop_atr_count', 3), ('risk_factor', 0.002) ) def __init__(self): self.trend_filter_fast = bt.indicators.EMA(period=self.params.trend_filter_fast_period) self.trend_filter_slow = bt.indicators.EMA(period=self.params.trend_filter_slow_period) self.dc_fast = DonchianChannelsIndicator(period=self.params.fast_donchian_channel_period) self.dc_slow = DonchianChannelsIndicator(period=self.params.slow_donchian_channel_period) self.atr = bt.indicators.ATR(period=self.params.trailing_stop_atr_period) # For trailing stop self.max_price = self.data.close[0] # track the highest price after opening long positions self.min_price = self.data.close[0] # track the lowest price after opening short positions def next(self): is_long = self.trend_filter_fast > self.trend_filter_slow # trend filter # Position size rule max_loss = self.broker.getvalue() * self.p.risk_factor # cash you afford to loss position_size = max_loss / self.atr # self.dc_slow.low <= self.dc_fast.low <= self.dc_fast.high <= self.dc_slow.high assert self.dc_slow.low <= self.dc_fast.low assert self.dc_fast.low <= self.dc_fast.high assert self.dc_fast.high <= self.dc_slow.high if self.data.close > self.dc_slow.high: if is_long and self.position.size == 0: self.long_order = self.buy(size=position_size) # Entry rule 1 print(f'Long {position_size}') self.max_price = self.data.close[0] return elif self.data.close > self.dc_fast.high: if self.position.size < 0: print(f'Close {self.position.size} by exit rule 2') self.close() # Exit rule 2 return elif self.data.close > self.dc_fast.low: pass elif self.data.close > self.dc_slow.low: if self.position.size > 0: print(f'Close {self.position.size} by exit rule 1') self.close() # Exit rule 1 return else: if (not is_long) and self.position.size == 0: self.short_order = self.sell(size=position_size) # Entry rule 2 print(f'Short {position_size}') self.min_price = self.data.close[0] return # Trailing stop if self.position.size > 0: self.max_price = max(self.max_price, self.data.close[0]) if self.data.close[0] < (self.max_price-self.atr[0]*3): print(f'Close {self.position.size} by trailing stop rule') self.close() return if self.position.size < 0: self.min_price = min(self.max_price, self.data.close[0]) if self.data.close[0] > (self.min_price+self.atr[0]*3): print(f'Close {self.position.size} by trailing stop rule') self.close() return
Jupyter notebook: https://github.com/soulmachine/crypto-notebooks/blob/master/backtest/Clenow-trend-following.ipynb
Any improvement suggestions? Thanks.
-
Typo:
alias = ('ClenowTrendFollowing',)
-
Nice work! Look forward to testing this out. Did you see positive results in backtesting?