How to Exit Current Position and Enter Opposite Position at Same Time
-
Hi Backtrader Community,
I am trying to modify "Quickstart" strategy as a common Renko strategy.
Strategy:
- Buy: If two consecutive candles are bullish
- Sell: If two consecutive candles are bearish
- Exit: If two consecutive candles move to the opposite direction
As a start, I changed the logic as follows:
class TestStrategy(bt.Strategy): def log(self, txt, dt=None): ''' Logging function fot this strategy''' dt = dt or self.datas[0].datetime.date(0) print('%s, %s' % (dt.isoformat(), txt)) def __init__(self): # Keep a reference to the "close" line in the data[0] dataseries self.dataclose = self.datas[0].close # Keep a reference to the "open" line in the data[0] dataseries self.dataopen = self.datas[0].open # To keep track of pending orders self.order = None 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, %.2f' % order.executed.price) elif order.issell(): self.log('SELL EXECUTED, %.2f' % order.executed.price) self.bar_executed = len(self) elif order.status in [order.Canceled, order.Margin, order.Rejected]: self.log('Order Canceled/Margin/Rejected') # Write down: no pending order self.order = None def next(self): # Simply log the closing price of the series from the reference self.log('Open, %.2f' % self.dataopen[0]) self.log('Close, %.2f' % self.dataclose[0]) # Check if an order is pending ... if yes, we cannot send a 2nd one if self.order: return # Check if we are in the market if not self.position: if self.dataclose[0] > self.dataopen[0]: # current bar is bullish if self.dataclose[-1] > self.dataopen[-1]: # previous bar is bullish # BUY with all possible default parameters self.log('BUY CREATE, %.2f' % self.dataclose[0]) # Keep track of the created order to avoid a 2nd order self.order = self.buy() if self.dataclose[0] < self.dataopen[0]: # current bar is bearish if self.dataclose[-1] < self.dataopen[-1]: # previous bar is bearish # SELL with all possible default parameters self.log('SELL CREATE, %.2f' % self.dataclose[0]) # Keep track of the created order to avoid a 2nd order self.order = self.sell() else: # Already in the market ... we might exit if self.dataclose[0] > self.dataopen[0]: # current bar is bullish if self.dataclose[-1] > self.dataopen[-1]: # previous bar is bullish # Exit self.log('Exit: BUY CREATE, %.2f' % self.dataclose[0]) # Keep track of the created order to avoid a 2nd order self.order = self.buy() # Already in the market ... we might exit if self.dataclose[0] < self.dataopen[0]: # current bar is bearish if self.dataclose[-1] < self.dataopen[-1]: # previous bar is bearish # Exit self.log('Exit: SELL CREATE, %.2f' % self.dataclose[0]) # Keep track of the created order to avoid a 2nd order self.order = self.sell()
This, of course, is unsuccessful because it fails to avoid a 2nd order and it fails to exit the current position and entering the opposite position at same time.
How could I code this strategy? Thanks for your insights!
-
I think you can use
order_target_xxx
for backtests. Lets operate with 100 stocks. If you havebuy
signal, you doself.order_target_size(size=100)
bt
buys 100 stocks. If then you havesell
signal, you doself.order_target_size(size=-100)
And
bt
will reverse position selling 200 stocks.Docs - Target Orders
-
The approach by @ab_trader is probably the cleanest.
See also how you can reverse position with a sizer and
buy
,sell
commands (where thesize
is not specified and left to theSizer
to calculate) -
Somehow I am having hard time understanding how
order_target_xxx
works and I ended up usingbuy
andsell
commands withsize
.# Check if we are in the market if not self.position: #Enter Signal # Two Bullish Candles if self.dataclose[0] > self.dataopen[0] and self.dataclose[-1] > self.dataopen[-1]: # BUY self.order = self.buy(size=100) # Two Bearish Candles elif self.dataclose[0] < self.dataopen[0] and self.dataclose[-1] < self.dataopen[-1]: # SELL self.order = self.sell(size=100) else: pass else: #Exit Signal # Already in the market & Two Bullish Candles if self.position.size < 0 and self.dataclose[0] > self.dataopen[0] and self.dataclose[-1] > self.dataopen[-1]: self.order = self.buy(size=100) # Already in the market & Two Bearish Candles if self.position.size > 0 and self.dataclose[0] < self.dataopen[0] and self.dataclose[-1] < self.dataopen[-1]: self.order = self.sell(size=100)
This does avoid 2nd order but I would like to know how
order_target_xxx
is different (or better) and how it will change the above code.Also, this made me question how
size
works. Feel stupid to ask but I do not get why buy size=100 and then sell size=100 end up selling 200. If I continue to reverse the position, size will continue to increase as well? I guess I'm not understanding the concept intuitively. I am grateful if you could help me understand. -
Instead of doing a
buy
/sell
and latersell
/buy
, you replace each pair of calls with a singleorder_target_size
. -
Hi, I am running into the same problem with you, do you figure out to solve this problem finally?