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:
    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:
    main.py

    from __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)
    

    commissions.py

    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
    

    sizers.py

    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
    

    strategies.py

    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}')
    

    heatmap.py

    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.


Log in to reply
 

});