'backtrader: custom columns as indicator

import backtrader.feeds as btfeeds
class PandasDataSource(bt.feeds.PandasData):
    lines = ('Strategy_GoldenCross_Signal',) 
    params=(
             ('Strategy_GoldenCross_Signal', -1),
             ('Strategy_MACDDiff', -1),
                ('dtformat', ('%Y-%m-%d')),
                ('datetime', None),
                ('time', -1),
                ('high', 2),
                ('low', 3),
                ('open', 1),
                ('close', 4),
                ('volume', 5),
                ('openinterest', -1),)
            

Hi,

I want to use my custom column in the dataframe which is Strategy_GoldenCross_Signal.

After adding the data:

st_date = datetime.datetime(2021,1,1)
ed_date = datetime.datetime(2021,12,20)
cerebro = bt.Cerebro()
datafeed1 = PandasDataSource(dataname=data1, fromdate=st_date, todate=ed_date)

I then define the strategy class:

class TestStrategy(bt.Strategy):
        def next(self):
            if not self.position:
                if self.data.Strategy_GoldenCross ==1:
                    self.buy()  
                elif self.data.Strategy_GoldenCross ==-1:
                    self.close()

So basically, when the customized signal is 1 -> buy, -1 -> sell Adding into the strategy:

cerebro.addstrategy(TestStrategy)
cerebro.broker.setcash(1000)
cerebro.addsizer(bt.sizers.PercentSizer, percents=20)
print(cerebro.broker.getvalue())
result = cerebro.run()
end_cash = cerebro.broker.getvalue()
print(end_cash)

I want to ask if this is the right way to use the columns in the dataframe? And what do you recommend for using the indicators? Would you engineer the dataframe first and use the engnieered columns rather than using the existing indicators inside the backtrader package? Thanks.



Solution 1:[1]

Easy, It seems that you only want to touch the extra data by date (in your use case) just do: ( For make use of self.datetime.date(), please follow the config with point 3. )

self.data._dataname.loc[self.datetime.date()]['extra_column']

But if you really want to change the extra column to lines object or Indicator see here: (first create a hack wapper class to change the column data to line object by using 'next' method, believe me, only this way, you cannot do that by using 'init' method ):

class CMFLineWapper(bt.Indicator):
lines = ('cmf',)
params = dict(period=14)
def __init__(self, period, strategy  ):
    self.strategy = strategy
    self.p.period = period
def next(self):
    self.lines.cmf[0] = self.data._dataname.loc[self.strategy.datetime.date()]['cmf']
  1. Then :

    def __init__(self):
     for i , data in enumerate ( self.datas ):
         self.data._dataname['cmf'] = ta.volume.ChaikinMoneyFlowIndicator(
             self.data._dataname['high'],
             self.data._dataname['low'],
             self.data._dataname['close'],
             self.data._dataname['volume'],
             self.p.cmf_period
         ).chaikin_money_flow()
    
         CMFVector = CMFLineWapper(data,self.p.cmf_period,self,plotname='CMF' , subplot=True , plothlines = [self.p.cmf_low_line, self.p.cmf_top_line ] )
         data.lines.cmf = CMFVector.cmf
    

where cmf is the extra column. Or you can use ._dataname to touch your extra column.

  1. The settings:

    cerebro.run(maxcpus=None, live=False, runonce=True, exactbars=False, optdatas=True, optreturn=True, stdstats=False, quicknotify=True)

Ok, now cmf becomes the lines object, you can change lins object to Indicator by using sma(1) or lineplotterindicator.

Good luck, I finally don't use backtrader anymore. :)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1