Hello,
I was puzzling over the same message in the detailed log. I’m hoping my train of thought is correct, but I’m still left with some questions that probably require mid-level CBT.
E.g. simple counter-trend system trading S&P100 constituents. Buy signal generated on close and position entered next day on open. Sell signal generated and “executed” on close. Allow same bar exit. Maximum open positions = 10, PositionScore to select top 10 positions.
// Counter-Trend System w/ PositionScore
SetOption("UsePrevBarEquityForPosSizing",True);
SetOption("MinShares",1);
SetOption("AllowSameBarExit",True);
SetOption("HoldMinBars",0);
SetTradeDelays(1,0,0,0);
InitialEquity = 100000;
SetOption("InitialEquity",InitialEquity);
MaxPos = 10;
SetOption("MaxOpenPositions",MaxPos);
SetPositionSize(100/MaxPos,spsPercentOfEquity);
//
Oversold = C < MA(C,10);
Trend = C > MA(C,100);
//
BuySignal = Oversold AND Trend;
BuyPrice = O;
Buy = BuySignal;
SellSignal = C > Ref(C,-1);
SellPrice = C;
Sell = SellSignal;
PositionScore = 1000 + ROC(C,100);
1/3/2017 CHTR generates buy signal - rank #20 - position not entered b/c not in top 10
1/4/2017 CHTR generates buy signal - rank #10 - position entered on open at 286.98
1/4/2017 CHTR generates sell signal, but “Exit ignored because signal predates already processed event”
1/5/2017 CHTR generates sell signal, position exited on close at 296.17

According to UserGuide the default backtestRegular mode:
“If trade is NOT entered on first entry signal due to weak rank, not enough cash or reaching the maximum open position count, subsequent entry signals are ignored until matching exit signal.”
In other words, the first sell signal (1/4/2017) is ignored because it refers to first buy signal (1/3/2017) and “physically can’t” execute because the position was never taken. Hence, we need to wait for next sell signal (1/5/2017) to exit position entered on second buy signal (1/4/2017).
But this raises another puzzle: why was the position entered on 1/4/2017 in the first place when the first “trade” had not yet completed? After all “If trade is NOT entered on first entry signal … subsequent entry signals are ignored until matching exit signal.”
It seems this is because the combination of AllowSameBarExit and the default HoldMinBars (zero) means that in this instance the sell signal is processed before the buy signal. There is a buy signal and a sell signal on the same bar. According to scenario 2 (cf. http://www.amibroker.com/guide/h_portfolio.html):
“If we are flat on given symbol, then entry is taken … but we do not move to next bar immediately.”
“If we are long on given symbol, then sell signal is taken. … Only after processing all signals we move to the next bar.”
On 1/4/2017 we are “long” ghost trade from 1/3/2017 buy signal which means the sell signal takes precedence (but signal ignored because it refers to a ghost trade). But, before moving to the next bar, the backtester checks whether there is also a buy signal on the bar - which there is - and enters the position on 1/4/2017 without the possibility of exiting this position on the same bar.
One could remove “ghost trades” by ranking the PositionScores and restricting buy signals to the top 10 issues (cf. https://www.amibroker.com/kb/2014/11/26/handling-limit-orders-in-the-backtester/).
// Counter-Trend System w/ Ranking
SetOption("UsePrevBarEquityForPosSizing",True);
SetOption("MinShares",1);
SetOption("AllowSameBarExit",True);
SetOption("HoldMinBars",0);
SetTradeDelays(1,0,0,0);
InitialEquity = 100000;
SetOption("InitialEquity",InitialEquity);
MaxPos = 10;
SetOption("MaxOpenPositions",MaxPos);
SetPositionSize(100/MaxPos,spsPercentOfEquity);
//
List = CategoryGetSymbols(categoryWatchlist,12); // #12 = S&P 100
if(Status("stocknum")==0)
{
StaticVarRemove("values*");
for( n = 0; (Symbol = StrExtract(List,n)) !=""; n++)
{
SetForeign(symbol);
values = 1000 + ROC(C,100);
RestorePriceArrays();
StaticVarSet("values"+symbol,values);
_TRACE(symbol);
}
StaticVarGenerateRanks("rank","values",0,1224);
}
symbol = Name();
values = StaticVarGet("values" + symbol);
rank = StaticVarGet("rankvalues" + symbol);
PositionScore = values;
//
Oversold = C < MA(C,10);
Trend = C > MA(C,100);
Top = rank <= 10;
//
BuySignal = Oversold AND Trend AND Top;
BuyPrice = O;
Buy = BuySignal;
SellSignal = C > Ref(C,-1);
SellPrice = C;
Sell = SellSignal;
This seems to solve the “Exit ignored because signal predates already processed event” issue. But it does not completely address the problem.
If we are actually long on a given symbol, then the sell signal will still take precedence. If the bar has both buy and sell signal, then according to the backtester we would exit the long on close but also enter a new long on open!? At least this is my interpretation of the detailed log:
1/3/2017 NVDA generates buy signal - rank #1 - position entered on open at 104.4
1/4/2017 NVDA generates sell signal, position exited on close at 104.39
1/4/2017 NVDA generates buy signal - rank #1 - position entered on open at 103.4

(As an aside, I’m not clear why CHTR is not listed as a buy as above?)
In any case, if I understand “Scenario 2” properly, then we have a same bar, same symbol signal conflict. On 1/4/2017 we are long NVDA therefore sell signal takes precedence, subsequent to which buy signal is processed.
Changing HoldMinBars to one (“Scenario 3”) yields same result in this case because entering NVDA on 1/4/2017 does not violate HoldMinBars = 1 as sell signal preceded it, thereby guaranteeing that we hold NVDA minimum 1 bar.

If I understand the issue correctly, then it seems best to either change BuyPrice=SellPrice and set AllowSameBarExit to False avoiding this problem altogether, or delving into mid-level CBT to process signals manually (e.g., entry signal takes precedence over exit signal, if flat then execute entry before exit as per scenario 2, however if long then ignore entry signal and only execute exit signal)?
Is this correct? Any pointers would be much appreciated.