Can't import a custom indicator from default folder.
-
Hi. I'm trying to write a strategy which uses a Stochastic RSI indicator which is not pre-installed in backtrader. I've created my own StochRSI indicator, placed it inside the backtrader/indicators/ folder, and edited the init.py in backtrader/indicators/ to also include the name of my custom indicator. I cannot, however, manage to import the indicator into my strategy successfully ("AttributeError: module 'backtrader.indicators' has no attribute 'StochasticRSI'"). Any help would be greatly appreciated.
The strategy:
#boilerplate from __future__ import (absolute_import, division, print_function, unicode_literals) import datetime # For datetime objects import os.path # To manage paths import sys # To find out the script name (in argv[0]) import backtrader as bt import backtrader.indicators as btind from backtrader.indicators.mabase import MovAv class ExtremeLongs(bt.Strategy): """ Buy Logic: -No position is open on the data (one trade at a time) -Both Stochastic indicator lines are under 10 for last 3 candles -Candle under bottom the Bollinger bands -Last 4 candles have been red -Candle is green -After buying a target is set and stored as a variable based on Moving Average (9) Sell Logic -Target has been met """ params = ( #Stochastic RSI ('stoch_k_period', 3), ('stoch_d_period', 3), ('stoch_rsi_period', 14), ('stoch_period', 14), ('stoch_upperband', 90.0), ('stoch_lowerband', 10.0), ('take_profit', 0.04), ('stop_loss', 0.01), #Bollinger Bands ('bbands_period', 20), ('bbands_devfactor', 2.0), ('bbands_movav', MovAv.Simple) ) def __init__(self): #intialising indicators #intitialise stochastic rsi self.stochrsi = btind.StochasticRSI( k_period=self.stoch_k_period, d_period=self.stoch_d_period, rsi_period=self.stoch_rsi_period, stoch_period=self.stoch_period, upperband=self.p.stoch_upperband, lowerband=self.p.stoch_lowerband) #initialise bollinger bands self.bbands = btind.BollingerBands( period=self.bbands_period, devfactor=self.bbands_devfactor, movav=self.bbands_movav) if __name__ == '__main__': #start Cerebro and add strategy cerebro = bt.Cerebro() cerebro.addstrategy(ExtremeLongs) #find data modpath = os.path.dirname(os.path.abspath(sys.argv[0])) datapath = os.path.join(modpath, '../../datas/orcl-1995-2014.txt') #intialise data data = bt.feeds.YahooFinanceCSVData( dataname=datapath, fromdate=datetime.datetime(2000, 1, 1), todate=datetime.datetime(2000, 12, 31), reverse=False) cerebro.adddata(data, "Oracle - Year 2000") #broker settings cerebro.broker.setcash(100000.0) #run cerebro print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue()) cerebro.run() print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue()) cerebro.plot(style='candle')
The custom indicator:
from __future__ import (absolute_import, division, print_function, unicode_literals) from . import Indicator, MovAv, RelativeStrengthIndex, Highest, Lowest import backtrader as bt class StochasticRSI(Indicator): """ K - The time period to be used in calculating the %K. 3 is the default. D - The time period to be used in calculating the %D. 3 is the default. RSI Length - The time period to be used in calculating the RSI Stochastic Length - The time period to be used in calculating the Stochastic Formula: %K = SMA(100 * (RSI(n) - RSI Lowest Low(n)) / (RSI HighestHigh(n) - RSI LowestLow(n)), smoothK) %D = SMA(%K, periodD) """ alias = ('StochasticRSI') lines = ('fastk', 'fastd',) params = ( ('k_period', 3), ('d_period', 3), ('rsi_period', 14), ('stoch_period', 14), ('movav', MovAv.Simple), ('rsi', RelativeStrengthIndex), ('upperband', 80.0), ('lowerband', 20.0), ) plotlines = dict(percD=dict(_name='%D', ls='--'), percK=dict(_name='%K')) def _plotlabel(self): plabels = [self.p.k_period, self.p.d_period, self.p.rsi_period, self.p.stoch_period] plabels += [self.p.movav] * self.p.notdefault('movav') return plabels def _plotinit(self): self.plotinfo.plotyhlines = [self.p.upperband, self.p.lowerband] def __init__(self): rsi = bt.indicators.RSI(period=self.p.rsi_period) rsi_ll = bt.indicators.Lowest(rsi, period=self.p.rsi_period) rsi_hh = bt.indicators.Highest(rsi, period=self.p.rsi_period) stochrsi = (rsi - rsi_ll) / (rsi_hh - rsi_ll) self.l.fastk = k = self.p.movav(100.0 * stochrsi, period=self.p.k_period) self.l.fastd = self.p.movav(k, period=self.p.d_period)
The init.py file in backtrader/indicators/ (with the custom stochasticrsi added underneath stochastic):
#!/usr/bin/env python # -*- coding: utf-8; py-indent-offset:4 -*- ############################################################################### # # Copyright (C) 2015, 2016, 2017 Daniel Rodriguez # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # ############################################################################### from __future__ import (absolute_import, division, print_function, unicode_literals) from backtrader import Indicator from backtrader.functions import * # The modules below should/must define __all__ with the Indicator objects # of prepend an "_" (underscore) to private classes/variables from .basicops import * # base for moving averages from .mabase import * # moving averages (so envelope and oscillators can be auto-generated) from .sma import * from .ema import * from .smma import * from .wma import * from .dema import * from .kama import * from .zlema import * from .hma import * from .zlind import * from .dma import * # depends on moving averages from .deviation import * # depend on basicops, moving averages and deviations from .atr import * from .aroon import * from .bollinger import * from .cci import * from .crossover import * from .dpo import * from .directionalmove import * from .envelope import * from .heikinashi import * from .lrsi import * from .macd import * from .momentum import * from .oscillator import * from .percentchange import * from .percentrank import * from .pivotpoint import * from .prettygoodoscillator import * from .priceoscillator import * from .psar import * from .rsi import * from .stochastic import * from .stochasticrsi import * from .trix import * from .tsi import * from .ultimateoscillator import * from .williams import * from .rmi import * from .awesomeoscillator import * from .accdecoscillator import * from .dv2 import * # depends on percentrank # Depends on Momentum from .kst import * from .ichimoku import * from .hurst import * from .ols import * from .hadelta import *
-
Did you check your path is importing your customized bt library and not another version on your system?
-
@Patrick-Mermelstein said in Can't import a custom indicator from default folder.:
class StochasticRSI(Indicator):
As a work around you could just put your custom indicator class in your strategy file
-
You an also put the indicator class in a local directory and import it.
-
I've run your code and it is working ok (up to some syntax errors in accessing strategy parameters directly and not through
self.params
). So your problem seems to be an additional backtrader package installed somewhere, such that you are importing a wrong backtrader package.It is easy to check - just print/debug the
bt.__file__
var -
@vladisld sorry, I'm fairly new to backtrader. Could you expand on the syntax mistakes you mentioned about the parameters?
-
Basically you may access the strategy parameter either through
self.p
orself.params
.Here the code that worked for me (just compare it with yours):
# boilerplate from __future__ import (absolute_import, division, print_function, unicode_literals) import datetime # For datetime objects import os.path # To manage paths import sys # To find out the script name (in argv[0]) import backtrader as bt import backtrader.indicators as btind from backtrader.indicators.mabase import MovAv class ExtremeLongs(bt.Strategy): """ Buy Logic: -No position is open on the data (one trade at a time) -Both Stochastic indicator lines are under 10 for last 3 candles -Candle under bottom the Bollinger bands -Last 4 candles have been red -Candle is green -After buying a target is set and stored as a variable based on Moving Average (9) Sell Logic -Target has been met """ params = ( # Stochastic RSI ('stoch_k_period', 3), ('stoch_d_period', 3), ('stoch_rsi_period', 14), ('stoch_period', 14), ('stoch_upperband', 90.0), ('stoch_lowerband', 10.0), ('take_profit', 0.04), ('stop_loss', 0.01), # Bollinger Bands ('bbands_period', 20), ('bbands_devfactor', 2.0), ('bbands_movav', MovAv.Simple) ) def __init__(self): # intialising indicators # intitialise stochastic rsi self.stochrsi = btind.StochasticRSI( k_period=self.p.stoch_k_period, d_period=self.p.stoch_d_period, rsi_period=self.p.stoch_rsi_period, stoch_period=self.p.stoch_period, upperband=self.p.stoch_upperband, lowerband=self.p.stoch_lowerband) # initialise bollinger bands self.bbands = btind.BollingerBands( period=self.p.bbands_period, devfactor=self.p.bbands_devfactor, movav=self.p.bbands_movav) if __name__ == '__main__': # start Cerebro and add strategy cerebro = bt.Cerebro() cerebro.addstrategy(ExtremeLongs) # find data modpath = os.path.dirname(os.path.abspath(sys.argv[0])) datapath = os.path.join(modpath, 'w:/backtrader/datas/orcl-1995-2014.txt') # intialise data data = bt.feeds.YahooFinanceCSVData( dataname=datapath, fromdate=datetime.datetime(2000, 1, 1), todate=datetime.datetime(2000, 12, 31), reverse=False) cerebro.adddata(data, "Oracle - Year 2000") # broker settings cerebro.broker.setcash(100000.0) # run cerebro print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue()) cerebro.run() print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue()) cerebro.plot(style='candle')
-
Is this what you're looking for?
self.stochRSI = bt.talib.STOCHRSI()
Cause you have these: https://mrjbq7.github.io/ta-lib/funcs.html