next() beginning before minimum period
-
Hi I have the following code
class FirstStrat (bt.Strategy): params = ( ('system1EntryPeriod', 20), ('system1ExitPeriod', 10), ('system2EntryPeriod', 55), ('system2ExitPeriod', 20), ('printlog', True), ) def __init__(self): # self.dataclose = self.datas[0].close # To keep track of pending orders self.order = None self.buyprice = None self.buyComm = None # Tried with just self.data # It uses CLOSE instead of high self.S1HEntry = bt.indicators.Highest(self.data.high, period=self.p.system1EntryPeriod) self.S2HEntry = bt.indicators.Highest(self.data.high, period=self.p.system2EntryPeriod) self.S1LExit = bt.indicators.Lowest(self.data.low, period=self.p.system1ExitPeriod) self.S2LExit = bt.indicators.Lowest(self.data.low, period=self.p.system2ExitPeriod) self.S1LEntry = bt.indicators.Lowest(self.data.low, period=self.p.system1EntryPeriod) self.S2LEntry = bt.indicators.Lowest(self.data.low, period=self.p.system2EntryPeriod) self.S1HExit = bt.indicators.Highest(self.data.high, period=self.p.system1ExitPeriod) self.S2HExit = bt.indicators.Highest(self.data.high, period=self.p.system2ExitPeriod)
I'm using one data feed -- YahooFinanceData. The problem I'm facing is that next() should be called when we have 55 data points, but for me it's called right from the first data point. After some debugging I see that
self._minperiod = 55
whileself._minperiods = [1]
.What am I not getting?
-
You have a lot more code, which probably is the root of your problem and some more information which would be needed, like timeframes you use, how you load the data ...
Posting a complete working sample is what helps.
-
Understood. Here's the full code.
Again, the problem is that next() runs on the first candle with all the indicators as
nan
instead of running after the expected 55 candles.import backtrader as bt import pdb from enum import Enum, auto class TurtleSystem(Enum): SYSTEM_1 = auto() SYSTEM_2 = auto() NO_POSITION = auto() class FirstStrat (bt.Strategy): params = ( ('system1EntryPeriod', 20), ('system1ExitPeriod', 10), ('system2EntryPeriod', 55), ('system2ExitPeriod', 20), ('printlog', True), ) def __init__(self): # self.dataclose = self.datas[0].close # To keep track of pending orders self.order = None self.buyprice = None self.buyComm = None # Tried with just self.data # It uses CLOSE instead of high self.S1HEntry = bt.indicators.Highest(self.data.high, period=self.p.system1EntryPeriod) self.S2HEntry = bt.indicators.Highest(self.data.high, period=self.p.system2EntryPeriod) self.S1LExit = bt.indicators.Lowest(self.data.low, period=self.p.system1ExitPeriod) self.S2LExit = bt.indicators.Lowest(self.data.low, period=self.p.system2ExitPeriod) self.S1LEntry = bt.indicators.Lowest(self.data.low, period=self.p.system1EntryPeriod) self.S2LEntry = bt.indicators.Lowest(self.data.low, period=self.p.system2EntryPeriod) self.S1HExit = bt.indicators.Highest(self.data.high, period=self.p.system1ExitPeriod) self.S2HExit = bt.indicators.Highest(self.data.high, period=self.p.system2ExitPeriod) self.currentPositionSystem = TurtleSystem.NO_POSITION def log(self, txt, dt=None, doprint=False): ''' Logging function fot this strategy''' if self.params.printlog or doprint: dt = dt or self.datas[0].datetime.date(0) print('%s, %s' % (dt.isoformat(), txt)) def next(self): self.log('highest %.2f' % self.S1HEntry[0]) # Check if an order is pending ... if yes, we cannot send a 2nd one if self.order: return # We are already in the market if self.position: # pdb.set_trace() # If this is a short position if self.position.size < 0: # Check if exit criteria is reached if self.currentPositionSystem == TurtleSystem.SYSTEM_1 and \ self.data.close[0] > self.S1HExit[-1]: self.order = self.buy() elif self.currentPositionSystem == TurtleSystem.SYSTEM_2 and \ self.data.close[0] > self.S2HExit[-1]: self.order = self.buy() # This is a long position elif self.position.size > 0: if self.currentPositionSystem == TurtleSystem.SYSTEM_1 and \ self.data.close[0] < self.S1LExit[-1]: self.order = self.sell() elif self.currentPositionSystem == TurtleSystem.SYSTEM_2 and \ self.data.close[0] < self.S2LExit[-1]: self.order = self.sell() else: assert False, "Position size zero. Should not happen" # We're not in the market yet else: # pdb.set_trace() if self.data.close[0] > self.S1HEntry[-1]: # Need to do other checks before entering position self.order = self.buy() self.currentPositionSystem = TurtleSystem.SYSTEM_1 # Breaking 55H elif self.data.close[0] > self.S2HEntry[-1]: # Always take this entry self.order = self.buy() self.currentPositionSystem = TurtleSystem.SYSTEM_2 elif self.data.close[0] < self.S1LEntry[-1]: # Need to do other checks before entering this position self.order = self.sell() self.currentPositionSystem = TurtleSystem.SYSTEM_1 # Breaking 55L elif self.data.close[0] < self.S2LEntry[-1]: # Always take this entry self.order = self.sell() self.currentPositionSystem = TurtleSystem.SYSTEM_2 else: self.log("Do nothing") def notify_trade(self, trade): if not trade.isclosed: return pdb.set_trace() self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' % (trade.pnl, trade.pnlcomm)) def notify_order(self, order): if order.status in [order.Submitted, order.Accepted]: # Buy/Sell order submitted/accepted to/by broker - Nothing to do return # Check if an order has been completed # Attention: broker could reject order if not enough cash if order.status in [order.Completed]: if order.isbuy(): self.log('BUY EXECUTED, Price: %.2f Cost: %.2f, Comm %.2f' % (order.executed.price, order.executed.value, order.executed.comm)) elif order.issell(): self.log('SELL EXECUTED, Price: %.2f Cost: %.2f, Comm %.2f' % (order.executed.price, order.executed.value, order.executed.comm)) self.bar_executed = len(self) elif order.status in [order.Canceled, order.Margin, order.Rejected]: self.log('Order Canceled/Margin/Rejected') self.currentPositionSystem = TurtleSystem.NO_POSITION # Write down: no pending order self.order = None INSTRUMENT = 'BTC-USD' if __name__ == '__main__': cerebro = bt.Cerebro() data = bt.feeds.YahooFinanceData(dataname=INSTRUMENT, period='d', fromdate=datetime.datetime(2013, 1, 1), todate=datetime.datetime(2018, 9, 20), ) cerebro.adddata(data) cerebro.addstrategy(FirstStrat) cerebro.broker.setcommission(commission=0.001) cerebro.addsizer(bt.sizers.PercentSizer, percents=2) startingValue = cerebro.broker.getvalue() print('Starting Portfolio Value: %.2f' % startingValue) cerebro.broker.set_cash(100000) cerebro.run() finalValue = cerebro.broker.getvalue() print('Final Portfolio Value: %.2f' % finalValue)
-
A working sample doesn't mean your full code ... just a working sample, for example with a single indicator and no logic.
@xenide said in next() beginning before minimum period:
# Tried with just self.data # It uses CLOSE instead of high self.S1HEntry = bt.indicators.Highest(self.data.high,
Of course. If you apply
self.data
it gives you the default which everybody uses, i.e.: the close price.Highest
calculates the highest price of what you give to it and not the highest high, unless you pass thehigh
, like in your case.@xenide said in next() beginning before minimum period:
What am I not getting?
Nothing, in a good sense, because you have uncovered an unexpected behavior. You are probably the first to not use
self.data
(or any of the data feeds in multiple data feeds scenarios), because you have chosen the individual lines. Which generates this behavior. To be checked. -
See Community - Release 1.9.67.122
And for a test sample
import argparse import backtrader as bt class St(bt.Strategy): params = dict(usedata=False) def __init__(self): d = self.data if self.p.usedata else self.data.high bt.indicators.Highest(d, period=20) def nextstart(self): print('NEXTSTART len(self):', len(self), 'ok:', len(self) == 20) def runstrat(args=None): args = parse_args(args) cerebro = bt.Cerebro() cerebro.adddata(bt.feeds.BacktraderCSVData( dataname='../../datas/2006-day-001.txt')) cerebro.addstrategy(St, usedata=args.data) cerebro.run(stdstats=False) def parse_args(pargs=None): parser = argparse.ArgumentParser( formatter_class=argparse.ArgumentDefaultsHelpFormatter, description=('Sample Skeleton') ) parser.add_argument('--data', action='store_true', help='Do not use high') return parser.parse_args(pargs) if __name__ == '__main__': runstrat()
-
Here data0 is on 1Min timeframe and data1 is on 1Day timeframe.
When I am using this :
'''
self.movag0 = backTester.indicators.MovingAverageSimple(self.data0.close, period=5)
self.movag1 = backTester.indicators.MovingAverageSimple(self.data1.close, period=5)
'''
Everything seems fine and it prints after min period (that is after 5 days)
But when I am trying to use this:
'''
self.movag0 = backTester.indicators.MovingAverageSimple(self.data0.close, period=5)
self.noise = backTester.Min(self.data1.high - self.data1.open, self.data1.open - self.data1.low)
self.movag1 = backTester.indicators.MovingAverageSimple(self.noise, period=5)
'''
It starts printing before the minimum period of 5 (for 1Day timeframe) ,it takes the minimum period as 5 min using 1 Minute timeframe. -
@ry93 said in next() beginning before minimum period:
Here data0 is on 1Min timeframe and data1 is on 1Day timeframe.
When I am using this :self.movag0 = backTester.indicators.MovingAverageSimple(self.data0.close, period=5) self.movag1 = backTester.indicators.MovingAverageSimple(self.data1.close, period=5)
Everything seems fine and it prints after min period (that is after 5 days)
But when I am trying to use this:self.movag0 = backTester.indicators.MovingAverageSimple(self.data0.close, period=5) self.noise = backTester.Min(self.data1.high - self.data1.open, self.data1.open - self.data1.low) self.movag1 = backTester.indicators.MovingAverageSimple(self.noise, period=5)
It starts printing before the minimum period of 5 (which has to be based on 1Day timeframe) but it takes the minimum period as 5 min using 1 Minute timeframe.
Why is that so ?