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?


Log in to reply
 

});