simple MACD strategy with Long, Short and close
-
Hi all,
I just switched from Matlab to python and even newer to Backtrader so my questions might seem obvious.
My problem seems similar to this :
https://community.backtrader.com/topic/2857/wanted-exit-long-and-open-short-on-the-same-bar-and-vice-versa
and this :
https://community.backtrader.com/topic/2797/self-close-does-not-clear-positionThe code below is a simple MACD strategy.
The main part of the algo is underdef next(self):
# -*- coding: utf-8 -*- """ """ import backtrader as bt import argparse import backtrader.feeds as btFeeds import numpy as np import yfinance as yf import pandas as pd import talib class SimpleMACDStrat(bt.Strategy): def __init__(self): #Keep a reference to the "close" line in the data[0] dataseries self.dataclose = self.datas[0].close self.order = None def log(self, txt, dt=None): dt = dt or self.datas[0].datetime.date(0) print(f'{dt.isoformat()} {txt}') #Print date and close 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('LONG 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): self.log("Close: '{0}'" .format(self.data.adj_close[0])) print('%f %f %f %f %f %f %f %f %f %f %f %f %f' % (self.data.Indexx[0],self.data.open[0], self.data.high[0],self.data.low[0], self.data.close[0],self.data.adj_close[0], self.data.volume[0],self.data.EMA_100[0], self.data.RSI[0], self.data.CCI[0], self.data.MACD_macd[0],self.data.MACD_sign[0],self.data.MACD_hist[0])) if self.order: return if self.data.MACD_hist[0]>0: if self.position.size<0 and self.data.MACD_hist[-1]<0 : self.close() self.log('CLOSE SHORT POSITION, %.2f' % self.dataclose[0]) elif self.position.size==0: self.order=self.buy() self.log('OPEN LONG POSITION, %.2f' % self.dataclose[0]) elif self.data.MACD_hist[0]<0: if self.position.size>0 and self.data.MACD_hist[-1]>0: self.order=self.close() self.log('CLOSE LONG POSITION, %.2f' % self.dataclose[0]) elif self.position.size==0: self.order=self.sell() self.log('OPEN SHORT POSITION, %.2f' % self.dataclose[0]) print('') class BasicIndicatorsFeeded(btFeeds.PandasData): lines = ('Indexx', 'adj_close', 'EMA_100', 'RSI', 'CCI', 'MACD_macd', 'MACD_sign', 'MACD_hist',) params = ( ('Indexx', 0), ('adj_close', 5), ('volume', 6), ('EMA_100', 7), ('RSI', 8), ('CCI', 9), ('MACD_macd', 10), ('MACD_sign', 11), ('MACD_hist', 12),) if __name__ == '__main__': cerebro = bt.Cerebro() #Add data feed to Cerebro data1 = yf.download("AAPL",start="2021-08-09", end="2021-12-21",group_by="ticker") data1.insert(0,'Indexx',' ') data1['Indexx']=range(len(data1)) data1['EMA_100']=talib.EMA(data1['Adj Close'],100) data1['RSI']=talib.RSI(data1['Adj Close'],14) data1['CCI']=talib.CCI(data1['High'], data1['Low'], data1['Adj Close'], timeperiod=14) data1['MACD_macd']=talib.MACD(data1['Adj Close'], fastperiod=12, slowperiod=26, signalperiod=9)[0] data1['MACD_sign']=talib.MACD(data1['Adj Close'], fastperiod=12, slowperiod=26, signalperiod=9)[1] data1['MACD_hist']=talib.MACD(data1['Adj Close'], fastperiod=12, slowperiod=26, signalperiod=9)[2] # data1['Long_position'] # Run Cerebro Engine cerebro.broker.setcash(8000000000) start_portfolio_value = cerebro.broker.getvalue() cerebro.addstrategy(SimpleMACDStrat) data = BasicIndicatorsFeeded(dataname=data1) cerebro.adddata(data) cerebro.run() cerebro.plot() # print(data1) print('-------------------') #print('%f' %data) # print(data) end_portfolio_value = cerebro.broker.getvalue() pnl = end_portfolio_value - start_portfolio_value print(f'Starting Portfolio Value: {start_portfolio_value:2f}') print(f'Final Portfolio Value: {end_portfolio_value:2f}') print(f'PnL: {pnl:.2f}')
Here are the results :
On 2021-11-10, macd_hist goes from postive to negative. We are expecting that the next day( 2021-11-11):
a)the long position is closed and right after
b)a short position is opened1)We see that a) is actually closed the same day. Isn't it supposed to happen the next ?
2)Also a sell is executed the next day, which is not supposed to happen.Any suggestion for 1) and 2) would be more then welcome. Thanks.
Abbe
-
Btw, I'm aware the idea can be coded that way (only def next) :
def next(self): #print('%f' % (self.datas[0].Indexxx[0]) self.log("Close: '{0}'" .format(self.data.adj_close[0])) print('%f %f %f %f %f %f %f %f %f %f %f %f %f' % (self.data.Indexx[0],self.data.open[0], self.data.high[0],self.data.low[0], self.data.close[0],self.data.adj_close[0], self.data.volume[0],self.data.EMA_100[0], self.data.RSI[0], self.data.CCI[0], self.data.MACD_macd[0],self.data.MACD_sign[0],self.data.MACD_hist[0])) if self.order: return print(self.position) if self.data.MACD_hist[0]>0 and self.data.MACD_hist[-1]<0: self.order=self.buy() self.log('CLOSE SHORT POSITION and open long, %.2f' % self.dataclose[0]) if self.data.MACD_hist[0]<0 and self.data.MACD_hist[-1]>0: self.order=self.sell() self.log('CLOSE LONG POSITION and open short, %.2f' % self.dataclose[0]) print('')
But I really want to separate the
self.close()
and for instance the
self.buy()
That would allow me later to use different conditions for closing a position and opening one.
Thanks a lot for any inputs, ideas, remarks.
Abbe