For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/
Execution gives many 'Canceled/Margin/Rejected' orders
-
When executing an optimization I'm getting many 'Canceled/Margin/Rejected' orders. There must be something wrong with my strategy but I can't figure out what it is.
Also the output that I'm expecting is around 10% and it should look like this:
But right now I'm getting a maximum of 7.7% and the output looks too uniform which makes me think something is wrong:
-
I forgot to copy the code:
main.pyfrom __future__ import (absolute_import, division, print_function, unicode_literals) import datetime import os.path import sys import backtrader as bt from strategies.strategies import * from sizers import MaxRiskSizer from commissions import DegiroCommission from heatmap import my_heatmap STARTING_CASH = 1700 if __name__ == '__main__': cerebro = bt.Cerebro(optreturn=False) cerebro.addanalyzer(bt.analyzers.AnnualReturn, _name='annual_return') #cerebro.optstrategy(EMAcrossover, fast=range(5, 7), slow=range(10,12)) #cerebro.optstrategy(EMAcrossover, fast=range(5, 55, 5), slow=range(60, 300, 10)) cerebro.optstrategy(EMAcrossover, fast=range(8, 26), slow=range(38, 51)) cerebro.broker.set_cash(STARTING_CASH) cerebro.broker.set_coc(True) modpath = os.path.dirname(os.path.abspath(sys.argv[0])) datapath = os.path.join(modpath, 'data/SPY.csv') data = bt.feeds.YahooFinanceCSVData( dataname=datapath, reverse=False ) cerebro.adddata(data) cerebro.addsizer(MaxRiskSizer) comminfo = DegiroCommission() cerebro.broker.addcommissioninfo(comminfo) optimized_runs = cerebro.run() final_results_list = [] for run in optimized_runs: for strategy in run: PnL = round(strategy.broker.get_value() - STARTING_CASH, 2) my_dict = strategy.analyzers.annual_return.get_analysis() annual_returns = [v for _, v in my_dict.items() if v != 0] average_annual_return = sum(annual_returns) / len(annual_returns) final_results_list.append([strategy.params.fast, strategy.params.slow, PnL, round(average_annual_return*100, 2)]) my_heatmap(final_results_list)
import backtrader as bt class DegiroCommission(bt.CommInfoBase): params = (('per_share', 0.004), ('flat', 0.5),) def _getcommission(self, size, price, pseudoexec): return self.p.flat + size * self.p.per_share
import backtrader as bt class MaxRiskSizer(bt.Sizer): params = (('risk', 0.98),) def __init__(self): if self.p.risk > 1 or self.p.risk < 0: raise ValueError('The risk parameter is a percentage which must be' 'entered as a float. e.g. 0.5') def _getsizing(self, comminfo, cash, data, isbuy): position = self.broker.getposition(data) if not position: size = comminfo.getsize(data.close[0], cash * self.p.risk) else: size = position.size return size
import backtrader as bt class EMAcrossover(bt.Strategy): params = (('fast', 20), ('slow', 50),) def log(self, txt, dt=None): dt = dt or self.datas[0].datetime.date(0) print(f'{dt.isoformat()} {txt}') # Comment this line when running optimization def __init__(self): self.dataclose = self.datas[0].close fast_ema, slow_ema = bt.ind.EMA(period=self.p.fast), bt.ind.EMA(period=self.p.slow) self.crossover = bt.indicators.CrossOver(fast_ema, slow_ema) #self.signal_add(bt.SIGNAL_LONGSHORT, bt.ind.CrossOver(ema1, ema2)) self.order = None self.buyprice = None self.buycomm = None def notify_trade(self, trade): if not trade.isclosed: return self.log(f'OPERATION GROSS {trade.pnl:.2f}, NET {trade.pnlcomm:.2f}') def notify_order(self, order): if order.status in [order.Submitted, order.Accepted]: # An active Buy/Sell order has been submitted/accepted - 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(f'BUY EXECUTED, ' f'Price: {order.executed.price:.2f}, ' f'Cost: {order.executed.value:.2f}, ' f'Commission: {order.executed.comm:.2f}') self.buyprice = order.executed.price self.buycomm = order.executed.comm elif order.issell(): self.log(f'SELL EXECUTED, ' f'Price: {order.executed.price:.2f}, ' f'Cost: {order.executed.value:.2f}, ' f'Commission: {order.executed.comm:.2f}') self.bar_executed = len(self) elif order.status in [order.Canceled, order.Margin, order.Rejected]: self.log('Order Canceled/Margin/Rejected') self.order = None def next(self): if self.order: return if self.crossover > 0: self.log(f'BUY CREATE {self.dataclose[0]:.2f}') self.order = self.buy() elif self.crossover < 0: self.log(f'SELL CREATE {self.dataclose[0]:.2f}')
import datetime import os.path import sys import pandas as pd import matplotlib.pyplot as plt import seaborn as sns import numpy as np from matplotlib.colors import LinearSegmentedColormap from matplotlib.patches import Rectangle def my_heatmap(data): data = np.array(data) xs = np.unique(data[:, 1].astype(int)) ys = np.unique(data[:, 0].astype(int)) vals = data[:, 3].reshape(len(ys), len(xs)) min_val_ndx = np.unravel_index(np.argmin(vals, axis=None), vals.shape) max_val_ndx = np.unravel_index(np.argmax(vals, axis=None), vals.shape) cmap = LinearSegmentedColormap.from_list('', ['red', 'orange', 'yellow', 'chartreuse', 'limegreen']) ax = sns.heatmap(vals, xticklabels=xs, yticklabels=ys, cmap=cmap, annot=True, fmt='.2f') ax.add_patch(Rectangle(min_val_ndx[::-1], 1, 1, fill=False, edgecolor='blue', lw=3, clip_on=False)) ax.add_patch(Rectangle(max_val_ndx[::-1], 1, 1, fill=False, edgecolor='blue', lw=3, clip_on=False)) plt.tight_layout() plt.show()
-
@quake004 said in Execution gives many 'Canceled/Margin/Rejected' orders:
if self.crossover > 0: self.log(f'BUY CREATE {self.dataclose[0]:.2f}') self.order = self.buy() elif self.crossover < 0: self.log(f'SELL CREATE {self.dataclose[0]:.2f}')
Did you forget to copy the last line where the sell order is created or not? Other than that, I can't quickly spot something wrong with your code.