Flipping a position from long to short
-
@run-out said I tried with your piece of code, however, I received an error:
Traceback (most recent call last): File "C:/Users/marketwizard/PycharmProjects/MW_Backtests/01_WSTF_EMA_Crossover_50_200_Flip_LongShort/main.py", line 406, in <module> strategies = cerebro.run(tradehistory=True) File "C:\Users\marketwizard\PycharmProjects\Algotrading_libraries1\lib\site-packages\backtrader\cerebro.py", line 1127, in run runstrat = self.runstrategies(iterstrat) File "C:\Users\marketwizard\PycharmProjects\Algotrading_libraries1\lib\site-packages\backtrader\cerebro.py", line 1301, in runstrategies strat._stop() File "C:\Users\marketwizard\PycharmProjects\Algotrading_libraries1\lib\site-packages\backtrader\strategy.py", line 486, in _stop analyzer._stop() File "C:\Users\marketwizard\PycharmProjects\Algotrading_libraries1\lib\site-packages\backtrader\analyzer.py", line 200, in _stop self.stop() File "C:\Users\marketwizard\PycharmProjects\Algotrading_libraries1\lib\site-packages\backtrader\analyzers\vwr.py", line 154, in stop dt = pn / (pi * math.exp(ravg * n)) - 1.0 ZeroDivisionError: float division by zero
(compared to the code I had posted in the previous messages, I added some analyzers. I know this part is working well because I use it often with other strategies without problems)
-
@run-out I analyzed my problem a bit more and it seems that with the code you recommended to use in next(), no trade is executed.
def next(self): if self.order: return # pending order execution if self.cross > 0.0 and self.getposition().size <= 0: self.order = self.order_target_percent(target=self.p.percent) elif self.cross < 0.0 and self.getposition().size >= 0: self.order = self.order_target_percent(target=-self.p.percent)
Maybe someone can help to solve the problem ?
-
I copied your code and ran it locally. It seems to be working. You have a long period for ema, just make sure your data covers this many periods. I shortened it for mine. Below is your code, I removed some of the logs and printouts.
import backtrader as bt import pandas as pd import os.path import sys from datetime import datetime, timedelta, date import itertools from csv import reader import csv import math period = 7305 icap = 100000 strategy_name = "EMA_CrossOver_50_200_Flip_LongShort" class EMAStack(bt.Strategy): # Define the parameters of the strategy params = ( ("portfolio_expo", 0.98), # Max 15% of the Portfolio per trade ("trade_risk", 0.02), # Max 2% risk per trade (stop loss) ("atrdist", 2.0), # ATR based Stop loss distance ("stake", 1.00), ("mtrade", False), ("percent", .5) ) # Initialize the elements which are needed for the strategy (indicators, etc...) def __init__(self): self.order = None # sentinel to avoid operations on pending order # Define the indicators self.ema_fast = bt.indicators.EMA( self.data.close, period=5, plot=True, subplot=False ) self.ema_slow = bt.indicators.EMA( self.data.close, period=20, plot=True, subplot=False ) self.atr = bt.indicators.ATR(period=14, plot=False, subplot=False) # Define the crossover signals self.cross = bt.indicators.CrossOver( self.ema_fast, self.ema_slow, plot=False, subplot=False ) if self.p.mtrade: self.tradeid = itertools.cycle([0, 1, 2]) else: self.tradeid = itertools.cycle([0]) def next(self): # Get the Amount of cash in the Portfolio cash = self.broker.get_cash() brokervalue = self.broker.get_value() if self.order: return # pending order execution if self.cross > 0.0 and self.getposition().size <= 0: self.order = self.order_target_percent(target=self.p.percent) elif self.cross < 0.0 and self.getposition().size >= 0: self.order = self.order_target_percent(target=-self.p.percent) print( "next(): Cash: {}, Broker Value: {}, Fast EMA: {}, Slow EMA: {}, CrossOver: {}".format( round(cash, 2), round(brokervalue, 2), round(self.ema_fast[0], 2), round(self.ema_slow[0], 2), round(self.cross[0], 2), ) ) def notify_order(self, order): if order.status == order.Completed: pass if not order.alive(): self.order = None # indicate no order is pendin date = self.data.datetime.datetime().date() if order.status == order.Accepted: print("-" * 32, " NOTIFY ORDER ", "-" * 32) print("{} Order Accepted".format(order.info["name"])) print( "{}, Status {}: Ref: {}, Size: {}, Price: {}".format( date, order.status, order.ref, order.size, "NA" if not order.price else round(order.price, 5), ) ) if order.status == order.Completed: print("-" * 32, " NOTIFY ORDER ", "-" * 32) print("{} Order Completed".format(order.info["name"])) print( "{}, Status {}: Ref: {}, Size: {}, Price: {}".format( date, order.status, order.ref, order.size, "NA" if not order.price else round(order.price, 5), ) ) print( "Created: {} Price: {} Size: {}".format( bt.num2date(order.created.dt), order.created.price, order.created.size, ) ) print("-" * 80) if order.status == order.Canceled: print("-" * 32, " NOTIFY ORDER ", "-" * 32) print("{} Order Canceled".format(order.info["name"])) print( "{}, Status {}: Ref: {}, Size: {}, Price: {}".format( date, order.status, order.ref, order.size, "NA" if not order.price else round(order.price, 5), ) ) if order.status == order.Rejected: print("-" * 32, " NOTIFY ORDER ", "-" * 32) print("WARNING! {} Order Rejected".format(order.info["name"])) print( "{}, Status {}: Ref: {}, Size: {}, Price: {}".format( date, order.status, order.ref, order.size, "NA" if not order.price else round(order.price, 5), ) ) print("-" * 80) def notify_trade(self, trade): date = self.data.datetime.datetime() if trade.isclosed: print("-" * 32, " NOTIFY TRADE ", "-" * 32) print( "{}, Close Price: {}, Profit, Gross {}, Net {}".format( date, trade.price, round(trade.pnl, 2), round(trade.pnlcomm, 2) ) ) print("-" * 80) if __name__ == "__main__": cerebro = bt.Cerebro() tickers = ["FB", "MSFT"] for ticker in tickers: data = bt.feeds.YahooFinanceData( dataname=ticker, timeframe=bt.TimeFrame.Days, fromdate=datetime(2020, 9, 1), todate=datetime(2020, 12, 1), reverse=False, ) cerebro.adddata(data, name=ticker) # Set the Cash for the Strategy cerebro.broker.setcash(icap) # Set the comissions cerebro.broker.setcommission(commission=0.005) cerebro.addstrategy(EMAStack) cerebro.addsizer(bt.sizers.AllInSizer) cerebro.broker.set_checksubmit(checksubmit=False) # Run over everything strategies = cerebro.run(tradehistory=True) # strategies = cerebro.run() # Sta = strategies[0]
-
@run-out Thank you for your reply. I tested your code (with the same parameters) and now it works !
With 50% of the portfolio no problem, however when I try to go all-in, the results are a bit strange.
Some short trades are taken in a row without going long inbetween. Here is a screenshot of the tradelist I generated: -
@marketwizard You're hitting
margin
or lack of cash. Do your short sales first if possible, leave a buffer of cash. I usually use 2.5% or so. -
@run-out If I understand that well, setting self.p.percent to 0.975 should provide enough cash buffer for the backtests ?
-
@marketwizard Probably yes.
-
@run-out The problem remain until I reduce the percentage to approx. 0.92.
Does other solution exists in order to avoid this Problem ? (beside reducing the position size) in order to go all in
-
@marketwizard Check out leverage. https://community.backtrader.com/topic/118/leverage-discussion
-
@marketwizard Try
cheat-on-open feature
. In this caseopen
price will be known and size can be calculated close to 100%. -
@ab_trader I tried with cheat on open and the same problems seems to occure (2 shorts in a row, etc... The order is accepted, completed but the trade is not executed).
class EMAStack(bt.Strategy): # Define the parameters of the strategy params = ( ("stake", 1.00), ("mtrade", False), ("percent", 0.99) ) # Initialize the elements which are needed for the strategy (indicators, etc...) def __init__(self): self.order = None # sentinel to avoid operations on pending order self.cheating = self.cerebro.p.cheat_on_open # Define the indicators self.ema_fast = bt.indicators.EMA( self.data.close, period=5, plot=True, subplot=False ) self.ema_slow = bt.indicators.EMA( self.data.close, period=20, plot=True, subplot=False ) # Define the crossover signals self.cross = bt.indicators.CrossOver( self.ema_fast, self.ema_slow, plot=False, subplot=False ) if self.p.mtrade: self.tradeid = itertools.cycle([0, 1, 2]) else: self.tradeid = itertools.cycle([0]) def operate(self, fromopen): if self.order is not None: return if self.cross > 0.0: self.order = self.order_target_percent(target=self.p.percent) elif self.cross < 0.0: self.order = self.order_target_percent(target=-self.p.percent) def prenext(self): cash = self.broker.get_cash() brokervalue = self.broker.get_value() print('Prenext(): Cash: {}, Broker Value: {}, Fast EMA: {}, Slow EMA: {}, CrossOver: {}'.format(round(cash, 2), round(brokervalue, 2), round(self.ema_fast[0], 2), round(self.ema_slow[0], 2), round(self.cross[0], 2))) self.next() def next(self): # Get the Amount of cash in the Portfolio cash = self.broker.get_cash() brokervalue = self.broker.get_value() data_value = self.broker.get_value([self.data]) if self.cheating: return self.operate(fromopen=False) #if self.order: # return # pending order execution print('next(): Cash: {}, Broker Value: {}, Fast EMA: {}, Slow EMA: {}, CrossOver: {}'.format(round(cash, 2), round(brokervalue, 2), round(self.ema_fast[0], 2), round(self.ema_slow[0], 2), round(self.cross[0], 2))) def next_open(self): if not self.cheating: return self.operate(fromopen=True) def notify_order(self, order): if order.status == order.Completed: pass if not order.alive(): self.order = None # indicate no order is pendin date = self.data.datetime.datetime().date() if order.status == order.Accepted: print('-'*32,' NOTIFY ORDER ','-'*32) print('{} Order Accepted'.format(order.info['name'])) print('{}, Status {}: Ref: {}, Size: {}, Price: {}'.format( date, order.status, order.ref, order.size, 'NA' if not order.price else round(order.price,5) )) if order.status == order.Completed: print('-'*32,' NOTIFY ORDER ','-'*32) print('{} Order Completed'.format(order.info['name'])) print('{}, Status {}: Ref: {}, Size: {}, Price: {}'.format( date, order.status, order.ref, order.size, 'NA' if not order.price else round(order.price,5) )) print('Created: {} Price: {} Size: {}'.format(bt.num2date(order.created.dt), order.created.price,order.created.size)) print('-'*80) if order.status == order.Canceled: print('-'*32,' NOTIFY ORDER ','-'*32) print('{} Order Canceled'.format(order.info['name'])) print('{}, Status {}: Ref: {}, Size: {}, Price: {}'.format( date, order.status, order.ref, order.size, 'NA' if not order.price else round(order.price,5) )) if order.status == order.Rejected: print('-'*32,' NOTIFY ORDER ','-'*32) print('WARNING! {} Order Rejected'.format(order.info['name'])) print('{}, Status {}: Ref: {}, Size: {}, Price: {}'.format( date, order.status, order.ref, order.size, 'NA' if not order.price else round(order.price,5) )) print('-'*80) def notify_trade(self, trade): date = self.data.datetime.datetime() if trade.isclosed: print('-'*32,' NOTIFY TRADE ','-'*32) print('{}, Close Price: {}, Profit, Gross {}, Net {}'.format( date, trade.price, round(trade.pnl,2), round(trade.pnlcomm,2))) print('-'*80)
-
@marketwizard Do an experiment and reduce your percet to something low, like 0.75. If your algo works this means you are too close to margin for the volatility in your stocks.