For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See:

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:
    Excel Heatmap

    But right now I'm getting a maximum of 7.7% and the output looks too uniform which makes me think something is wrong:
    Python Heatmap

  • I forgot to copy the code:

    from __future__ import (absolute_import, division, print_function,
    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))
        modpath = os.path.dirname(os.path.abspath(sys.argv[0]))
        datapath = os.path.join(modpath, 'data/SPY.csv')
        data = bt.feeds.YahooFinanceCSVData(
        comminfo = DegiroCommission()
        optimized_runs =
        final_results_list = []
        for run in optimized_runs:
            for strategy in run:
                PnL = round( - 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)
                strategy.params.slow, PnL, round(average_annual_return*100, 2)])

    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 =
            if not position:
                size = comminfo.getsize(data.close[0], cash * self.p.risk)
                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]
            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(, 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:
            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
            # 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:
            if self.crossover > 0:
                self.log(f'BUY CREATE {self.dataclose[0]:.2f}')
                self.order =
            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))

  • @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 =
        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.

Log in to reply