Not reading the docs, was: Buy sell on same bar causing unexpected backtest results

I am encountering a problem while trying to enter at open and sell at close of the same bar. To make things clear, I have created a simple code below. No special setting on settings window as well that could complicate the behaviour

SetOption("AllowSameBarExit", True );
SetOption("SettlementDelay", 1 );

BuyPrice = Open;
SellPrice = Close;
Buy = Ref( Close < MA( Close, 10 ), -1 ) ;
Sell =  (Close > Open)  ;

The intention of the code above is straightforward. Just to make it clear - buy at open when yesterday's close is less than last 10 days' moving average. sell if close is greater than open.

A scan obviously shows all signals as I expect, but the backtest simply skips many trades even if signals are there.

If you run the above afl on any instrument and any timeframe, you will see stunning results :slight_smile: and you will understand what I mean..

Can someone throw light on what needs to be done so that backtest shows the intended result? I believe it must be some setting that I need to make active, but I can not figure out which..

@amiuser, in general, the best tool to understand WHY some trades are skipped is the "Detailed Log".

As reported in the manual '... you can watch the selection process if you backtest with "Detailed log" report mode turned on'.

thanks. I did try that. That was the first thing I tried :slight_smile: But that did not help to figure out what was going on behind.
Did you try to run the above code?

Yes, but without the full formula (and/or your "default" settings) it makes not a lot of sense for me; for instance, using my current general configuration, a lot of trades are simply skipped due to insufficient funds.

Insufficient funds are okay and intended. This is done by settlementdelay. It is a way to prevent future leaks i.e it prevents taking position until funds are available again as sell happens at close while buy happens at open. ( you will see your 'returns' are much larger if you remove the line that sets settlementdelay.)

The code that I provided can be considered complete. the settings are done through Allowsamebarexit and settlementdelay.

trades1 gneral stops

Just for completeness am including the settings too. Note that buyprice /sellprice/'allow same bar exit' are explicitly set in AFL. I used qqq as symbol, but you can use any instrument you have

@amiuser some possible ideas to try. I think your Trade Delays are incorrect. If I understand your strategy, the signal is generated at the Close of current bar. You want to Buy on the next bar Open

 SetTradeDelays( 1, 0, 0, 0);

Also your code will generate repeated Buy signals even if a Sell has not occurred so consider,

Buy = ExRem(Buy, Sell);

And you wrote

So you are using Daily bars. But your settings you show 30 minute bars?

I haven't tested those changes but I think they are worth a look.

1 Like

Thanks for the response

Signal is generated based on previous bar ( 30 minute / daily actually does not matter to show the point I wanted to ). In the shown code I have used Ref -1 and setting buyprice to current open. Having said that I have tried setradedelays(1,0,0,0) as well (in conjunction with removing Ref). That did not work as well. i.e same impossible results.

Exrem i have read that should not be used in conjunction with same bar exits. ( I can also confirm that as I tried that too)

may be put in another way, how should the afl be written to get the backtest result the intended way :slight_smile:

i.e buy at open whenever previous bar's close is less than previous 10 day moving average of close and sell whenever (including current bar ) close is greater than open.

@amiuser sorry I am not at my desktop but also look at this,

Buy = Ref( Close < MA( Close, 10 ), -1 ) ;

Shouldn't that really be this,

Buy = Close < Ref(MA( Close, 10 ), -1 ) ;

Don't know if that helps with your issues but does seem to correctly identify your logic.

And I think you are wrong about,

 SetTradeDelays( 1, 0, 0, 0);

I think you need it.

1 Like
SetOption("AllowSameBarExit", True );
SetOption("SettlementDelay", 1 );

BuyPrice = Open;
SellPrice = Close;

Buy = Ref( Close < MA( Close, 10 ), -1 ) ;
sell = close > open;

is essentially the same as

SetOption("AllowSameBarExit", True );
SetOption("SettlementDelay", 1 );

BuyPrice = Open;
SellPrice = Close;
Buy = Close < MA( Close, 10 ) ;
sell = close > open;

your suggestion of

SetTradeDelays( 1, 0, 0, 0);
Buy = Close < Ref(MA( Close, 10 ), -1 ) ;

is slightly different buy logic as current bar close is compared to a calculated value that does not use current bar.

In any case even if i use your suggestion, the amazing results (slighly different statistic due to changed buy logic) from backtest still persists.. When you get a chance, please run the code on any instrument/timeframe and see. I guess some future leak is happening.

I suspect the sell condition of c > o together with allowsamebarexit is causing the strange behaviour and am pretty sure am missing something trivial here.. just cant figure what it is!

You are selling only if a current or a future bar Close > respective bar Open, until then, every Buy signal will be removed in regular backtest.

i know :slight_smile: but the problem is that it 'seems' random and mostly takes only the trades that are profitable skipping several losing trades. I get a very good performance no matter what instrument/timeframe i take and always a CAR/MDD that is too attractive and too good to be true which tells me that there is some future leak. Does anyone else not get amazing returns with the posted AFL running on any symbol/timeframe?

How are you verifying this

using scan vs backtest (even in detailed mode)

Btw did you run the code and see the performance figures? It is a holy grail :stuck_out_tongue: Jokes apart, it is a case of future leak., but I just can not figure out how it happens in this code

It’s your trade delays and buyprice variable. Your buying at the open of a bar when it close above a MA. You don’t know the level of that MA until the bar closes, so your looking forward. Set tradelay as recommended before. You’ll buy the next bars open price which is not forward looking.

Be very careful with buy price and sell price logic.

@Metamega Can you please post the corrected code ? Because I do not see the error :frowning: i have tried all combinations with and without trade delays

I tried the recommendation too. Did not work. Please be kind to post the code which you think is correct that i can copy paste and run to verify

Made some corrections to the code:

SetOption( "AllowSameBarExit", True );
SetOption( "MaxOpenPositions", maxpos = 1 );

Buy =  Close < MA( Close, 10 );
Sell = Close > Open  ;
Short = Cover = 0 ;
SetTradeDelays ( 1, 0, 0 , 0 ) ; 
BuyPrice = Open;
SellPrice = Close;

RoundLotSize = 0 ;

SetBacktestMode( backtestRegularRaw ) ;
SetCustomBacktestProc( "" );

if( Status( "action" ) == actionPortfolio )
    bo = GetBacktesterObject();	//  Get backtester object
    bo.PreProcess();	//  Do pre-processing
    PosQty = 0;

    for( i = 0; i < BarCount; i++ )	//  Loop through all bars
        // Open Positions the bar Open : 
        available = maxpos - PosQty ; // calculate the number of new position you can open
        size = ( bo.Cash - 10 )  / available ;   // calculate positions size leaving ~$10 in cash

        for( sig = bo.GetFirstSignal( i ); sig; sig = bo.GetNextSignal( i ) )
            if( sig.IsEntry() )
                if( available )
                    bo.EnterTrade( i, sig.Symbol, sig.IsLong(), sig.Price, size );
                    available --; 


        // Porcess exits and count the number of remaining open positions at the bar close:
        PosQty = 0 ;

        for( pos = bo.GetFirstOpenPos; pos ; pos = bo.GetNextOpenPos() )

            ExitSignal = bo.FindSignal( i, pos.Symbol , 2 ) ;

            if( ExitSignal )
                // exit at the close
                bo.ExitTrade( i, pos.Symbol, ExitSignal.Price );
                PosQty ++;


        bo.UpdateStats( i, 1 );	//  Update MAE/MFE stats for bar
        bo.UpdateStats( i, 2 );	//  Update stats at bar's end
    }	//  End of for loop over bars

    bo.PostProcess();	//  Do post-processing

1 Like

thanks @aron

well, am sure there should be a way to do it by CBT. I just took your code and ran and yes, all good and results are horrible as anyone can expect when there is no future leak!

I have designed dozens of strategies with amibroker that are live on markets over the years, but keeping it simple has been a priority. I have never ever had to use CBT.

Other amibroker experts, can someone explain why the original code (or the underlying logic) does not work on normal backtest?

Kind of played around with it a bit tonight. Don't usually fool around too much with same bar entry/exits.

Scratched my head for a bit but after looking up "SettlementDelay" , the manual recommends using backtestregularraw.

SettlementDelay - this option describes the number of days (not bars) it takes for sale proceeds to settle and be available for opening new positions.

SetOption("SettlementDelay", 3 ); // this will cause that proceeds from sale are only available for trading on 3rd day after sale

For detailed tracking " Detailed log" report option now shows available and unsettled funds for T+1, T+2 and so on

Note: when using this option it is recommended to use backtestRegularRaw instead of backtestRegular, otherwise some trades may not be entered because funds are not settled immediately and you need to be able to enter not on first but subsequent buy signals and that is exactly what backtestRegularRaw offers.

Note2: old backtester (Equity() function) ignores settlement delay

The behavior I was noticing was trades would close on an up candle as expected but then re-entry was allowed on same bar at the open. So everytime a trade closed, a new trade started on a guaranteed up candle as funds we're available. So it was guaranteeing an up candle, plus using funds that shouldn't be available so its leveraged.

With backtestregularraw set. "Settlement Delay" would not allow re-entry until funds we're settled. And the results went to crap as expected.

Note to self. Mixing same bar exits, and defining buyprice/sellprices cant get complicated.