Strategy closing instead of reverting to short
-
Hello. I programmed a very simple MA crossover strategy, however the short signals are only closing the long positions, not opening new short positions. I used self.sell() for that, and changing it for self.close() will yield the same result. This is the code:
from datetime import datetime import backtrader as bt class MACross(bt.Strategy): params = dict( pfast=10, pslow=30 ) def log(self, text, date=None): date = date or self.datas[0].datetime.datetime(0) print(f'{date.isoformat()}: {text}') def __init__(self): self.bar_executed = len(self) self.order = None self.comm = None ema_short = bt.ind.EMA(period=self.p.pfast) ema_long = bt.ind.EMA(period=self.p.pslow) self.crossover = bt.ind.CrossOver(ema_short, ema_long) def notify_order(self, order): order.executed.comm = round(order.executed.comm, 2) order.executed.price = round(order.executed.price, 2) if order.status in [order.Submitted, order.Accepted]: return elif order.status in [order.Completed]: if order.isbuy(): self.log(f'Entry long, price: {order.executed.price}; commission: {order.executed.comm}') elif order.issell(): self.log(f'Entry short, price: {order.executed.price}; commission {order.executed.comm}') elif order.status in [order.Margin]: self.log('Order Margin') self.order = None def notify_trade(self, trade): trade.pnlcomm = round(trade.pnlcomm, 2) if not trade.isclosed: return else: self.log(f'Net profit: {trade.pnlcomm}') def next(self): if self.crossover > 0: self.buy() if self.crossover < 0: self.sell() if __name__ == "__main__": def print_sqn(analyzer): sqn = round(analyzer.sqn, 2) print(f'SQN: {sqn}') start_cash = 10000 commission_percentage = 0.075 cerebro = bt.Cerebro() data = bt.feeds.GenericCSVData( dataname='BTCUSDT_1h.csv', fromdate=datetime(2020, 1, 1), # todate=datetime(2020, 5, 8), timeframe=bt.TimeFrame.Minutes, compression=60, dtformat='%Y-%m-%d %H:%M:%S', openinterest=None ) cerebro.adddata(data) cerebro.broker.setcash(start_cash) cerebro.addsizer(bt.sizers.PercentSizer, percents=99) cerebro.broker.setcommission(commission=(commission_percentage / 100), margin=False) cerebro.addstrategy(MACross) cerebro.addanalyzer(bt.analyzers.SQN) strategies = cerebro.run() current_strategy = strategies[0] port_value = round(cerebro.broker.getvalue(), 2) pnl = round(port_value - start_cash, 2) print(f'Final value: ${port_value}') print(f'P/L: ${pnl}') print_sqn(current_strategy.analyzers.sqn.get_analysis()) # cerebro.plot(style='candle')
What should I do to get short positions?
-
you may try closing your trades:
def next(self): if self.position: # close position on sma cross if self.crossover[0] != 0: self.close(self.data_primary) else: if self.crossover[0] > 0: self.buy() elif self.crossover[0] < 0: self.sell()
-
@dasch said in Strategy closing instead of reverting to short:
self.close(self.data_primary)
change the code above to:
self.close()
-
@dasch Hello, thanks for your answer, but it still is not reverting to short.
The result from using your code is:
Final value: $18389.98
P/L: $8389.98
If i use my original code, the result is:
Final value: $18389.98
P/L: $8389.98
If I change the code toif self.crossover[0] > 0: self.buy() if self.crossover[0] < 0: self.close()
The result still will be:
Final value: $18389.98
P/L: $8389.98This is what is bugging me, self.sell() should revert long positions to short, but it's behaving the same way as self.close().
-
Sell will not revert to a short position if you have a long position open. It will subtract the position size from the open position. You would either need to close the trade or increase the position size by the open position size.
-
When you call close it is doing just a order in the counter direction of the current open position. So if you call close on a long position it will create a sell order with the same size as your open position.
-
Target size orders will be good choice to use with the strategy which reverses positions and stay always in the market. Just use size 1 for long, size -1 for short.
-
@dasch ok, but then if I use
if self.position: if self.crossover[0] != 0: self.close() else: if self.crossover[0] > 0: self.buy() if self.crossover[0] < 0: self.sell()
Shouldn't it close the current position and then open a short?
@ab_trader target orders? What are these?
-
@Eduardo-Menges-Mattje said in Strategy closing instead of reverting to short:
target orders? What are these?
I believe it is documented here:
https://www.backtrader.com/docu/order_target/order_target/ -
@ab_trader using
if self.crossover > 0: self.order_target_size(target=1) if self.crossover < 0: self.order_target_size(target=-1)
Worked, it reverts the positions. But how can I use the sizer instead of 1?
-
@Eduardo-Menges-Mattje said in Strategy closing instead of reverting to short:
But how can I use the sizer instead of 1?
Docs
You are welcome! :) -
Yes, thank you, I know how to read. The issue is that if I use
if self.crossover > 0: self.order_target_size(target=self.getsizing()) elif self.crossover < 0: self.order_target_size(target=-self.getsizing())
Trades are only exited when they generate a profit, as you can see on the graph, which still has a short position opened.
The same thing happens if I use isbuy=False.
If the sizer is a FixedReverser, I don't even need to use the target orders, I can just use a self.buy or a self.close, but what I really want to use is a percent sizer that reverts the position, something that seems to be missing from Backtrader. -
@Eduardo-Menges-Mattje said in Strategy closing instead of reverting to short:
Yes, thank you, I know how to read. The issue is that if I use
maybe check out https://backtrader.com/docu/sizers/sizers/#sizer-development
where there is this:
Another example: A position rerverser
Cheers
-
Made a PercentReverter sizer which does what I wanted. In case anyone else needs it:
class PercentReverter(bt.Sizer): params = ( ('percents', 20), ) def _getsizing(self, comminfo, cash, data, isbuy): position = self.broker.getposition(data) if not position: size = cash / data.close[0] * (self.params.percents / 100) else: size = abs(position.size) + (position.size + cash / data.close[0]) * (self.params.percents / 100) return size