For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/

Implementing Kokers momentum strategy with improvements; error with inputs to momentum.



  • Hi all,

    first of all, my complements for an excellent platform.

    I am implementing Kokers momentum strategy, with suggested improvements:
    https://www.backtrader.com/blog/2019-05-20-momentum-strategy/momentum-strategy/

    When running the strategy, I get an error;
    "TypeError: momentum_func() takes 1 positional argument but 2 were given"

    I have tried for some time to fix this error, and assume it has something to do with passing "self" around.

    my code:

    def momentum_func(the_array):
    # convert 'Close' to returns, create x-axis and do linear regression on returns
    returns = np.log(the_array)
    x = np.arange(len(returns))
    slope, _, rvalue, _, _ = linregress(x, returns)

    # annualize the slope, multiply with R2 for best momentum when best linear-fit
    annualized = (1 + slope) ** 252
    momentum = annualized * (rvalue ** 2)
    return momentum
    

    class Momentum(bt.ind.OperationN):
    lines = ('trend',)
    params = {'period': 90}
    func = momentum_func

    Some of the stragegy:
    class Strategy(bt.Strategy):
    params = dict(
    momentum=Momentum, # parametrize the momentum and its period
    momentum_period=90,

        movav=bt.indicators.SMA,  # parametrize the moving average and its periods
        idx_movav_period=200,
        stock_movav_period=100,
    
        volatr=bt.indicators.ATR,  # parametrize the volatility and its period
        vol_period=20,
    
        stock_size_in_portfolio = 0.001,
        portfoliol_share_of_market = 0.4
    )
    
    def __init__(self):
        self.indicators = collections.defaultdict(dict)  # avoid per data dct in for
    
        # Use "self.data0" (or self.data) in the script to make the naming not
        # fixed on this being a "spy" strategy. Keep things generic
        self.stocks = self.datas[1:]
        self.stocks_with_data = []
        self.required_stock_history = max(self.p.momentum_period, self.p.vol_period, self.p.stock_movav_period)
    
        self.idx_mav = self.p.movav(self.data0, period=self.p.idx_movav_period)
        for stock in self.stocks:
            stock_name = stock._name
            self.indicators[stock_name]['momentum'] = self.p.momentum(stock, period=self.p.momentum_period)
            self.indicators[stock_name]['movingaverage'] = self.p.movav(stock, period=self.p.stock_movav_period)
            self.indicators[stock_name]['volatility'] = self.p.volatr(stock, period=self.p.vol_period)
    

    Any Idea why I get the error?



  • @gjertro

    try this

    def momentum_func(the_array, period):
    
        # function body
    


  • Dear @ab_trader

    Thanks for your answer. I actually solved this by declaring it as @staticmethod, even if it is outside the class definition.
    Quite weird, I think, but now it works.

    I did get some errors when the dataseries for the different stocks started at different times. I solved this by aligning the datetime indexes in the input data (pandas) and returning a very low momentum if there are nans in the array. The idea is that with the low momentum, the stocks will never be sorted at the top, so there will not be any positions in the stock. I do consider this risky though. Any idea as to how to include all data?

    The momentum function:

    @staticmethod
    def momentum_func(the_array):

    if np.nan in the_array:
        momentum = -10**10
    
    else:
        # convert 'Close' to returns, create x-axis and do linear regression on returns
        returns = np.log(the_array)
        x = np.arange(len(returns))
        slope, _, rvalue, _, _ = linregress(x, returns)
    
        # annualize the slope, multiply with R2 for best momentum when best linear-fit
        annualized = (1 + slope) ** 252
        momentum = annualized * (rvalue ** 2)
    
    return momentum

Log in to reply
 

});