Rookie mistake with Exrem: In case it's useful to anyone

Hello everyone,

I'd been beating myself up against the AFL for a while and couldn't figure out why the explorer said one thing and the backtest did another.

This is the AFL:

_SECTION_BEGIN( "Sistema RSC Mansfield Regular" );

// TIPO DE SISTEMA [SIEMPRE]
SetBacktestMode( backtestRegular ); 

// TEMPORALIDAD [SIEMPRE]
// Semanal

// PARÁMETROS DE ENTRADA [OPCIONAL] 
Periodo_RSC = Param( "Periodo RSC",52,1,500,1 ); 
Simbolo_RSC = ParamStr( "Simbolo RSC", "$SPX" );
Periodo_WMA = Param("Periodo WMA",52,1,500,1);

//  BACKTESTER SETTINGS [SIEMPRE]
SetOption("CommissionAmount", 0.00);
SetOption("InitialEquity",10000);

SetTradeDelays(0,0,0,0);
SetOption("AllowSameBarExit", True);

PosQty = Optimize("PosQty", 4,1,20,1);
SetOption("MaxOpenPositions", PosQty);
SetOption("AllowPositionShrinking",True);

// TAMAÑO DE LA POSICIÓN [SIEMPRE]
SetPositionSize(100/PosQty,spsPercentOfEquity);

// CÁLCULO INDICADORES [SIEMPRE]
Cociente = C/Foreign(Simbolo_RSC,"C");
Countr = Sum(Cociente,Periodo_RSC);
BasePrice = Countr/Periodo_RSC;
RSC = ((Cociente/BasePrice)-1)*10;

// REGLAS DEL SISTEMA [SIEMPRE]
Condicion_Compra = RSC > 1.75; 
Condicion_Venta	 =  RSC < 1.75; 

// Condiciones de Compra 
Buy = Condicion_Compra;
BuyPrice = Close;

// Condiciones de Venta 
Sell = Condicion_Venta;
SellPrice = Close;

/* Desempate
Si se dan más señales que huecos tenemos en cartera, los ordenaremos y en este caso, elegiremos los que tengan un RSC mayor
Esto se consigue con la variable PositionScore.
Le decimos que si se da la condicion de compra, ponga el valor de RSC en la variable Score, sino que ponga 0
El PositionScore ordena los valores en valor absoluto, por lo que un RSC de -3 lo va a poner por delante de 2
Por ello, añadimos un valor de 1000 al Score, así un RSC de -3+1000=997 lo pondrá por debajo de un 2+1000=1002
Si el Score da 0, no abrirá ninguna operación y si hubiera una abierta, la cerraría
*/
Score = IIf( Buy == 1, RSC, 0 );
PositionScore = 1000 + Score;


// STOPLOSS [OPCIONAL]
SetOption( "ActivateStopsImmediately", False );
ApplyStop(stopTypeLoss,stopModePercent,8,ExitAtStop=1,Volatile = False,ReEntryDelay=0 );

//ELIMINAR SEÑALES EXTRA
Buy = ExRem(Buy, Sell);
Sell = ExRem(Sell, Buy);

// EXPLORADOR [OPCIONAL]
ExploreFilter = ParamToggle("ExploreFilter", "LastBarInTest|All",1);
if (ExploreFilter)
	Filter = 1;
else	
	Filter = Status("LastBarInTest");
	
// Ordenar en Explorador
if (Status("actionex") ==actionExplore)
{
	SetSortColumns(2,-3);
	//columnas explorador
	AddColumn(RSC, "RSC MANSFIELD", 3.2);
	AddColumn(PositionScore, "PositionScore", 3.2);
	AddTextColumn(Name(),"Ticker",1.2, IIf(RSC>1.75, colorGreen,colorRed));
	AddTextColumn(FullName(), "Nombre",1.2,IIf(RSC>1.75,colorGreen,colorRed));
}

_SECTION_END();

This is the explorer's result:

Clearly, I was supposed to pick the top four stocks with the highest score, but it picked two others that were lower:

I didn't know what to do anymore, so I thought of commenting on the Exrem. That was the solution.

After reading several forum posts, I saw that Tomasz already made it very clear in this thread:
https://forum.amibroker.com/t/what-are-some-possible-ways-for-exrem-to-affect-backtest-results/17464/3?u=yonsi72
But forgetful people like me...hit the same stone repeatedly!

1 Like

Your code has some minor problems. But it works as expected, only that you need to understand how it works.

You had entries in the symbols with lower score because those are the only ones with Buy = 1 in that bar. If your backtest is not rotational then positionscore only matters when Buy is 1.

By the way, you don't need to set up values for position score if you don't buy, so you can simplify this way:

//Score = IIf( Buy == 1, RSC, 0 );
//PositionScore = 1000 + Score;
PositionScore = 1000 + RSC; // you only need this

Apart from that, after the ApplyStop you should call Equity() instead of Exrem():

// STOPLOSS [OPCIONAL]
SetOption( "ActivateStopsImmediately", False );
ApplyStop(stopTypeLoss,stopModePercent,8,1,False,0 );
Equity(1,0);

The code does what is expected. To see the trade signals you can use PlotShapes, to check when Buy = 1

PlotShapes(IIf(Buy,shapeUpArrow,shapeNone),colorGreen,0,L,-15);
PlotShapes(IIf(Buy,shapeHollowCircle,shapeNone),colorGreen,0,BuyPrice,0);
PlotShapes(IIf(Sell,shapeDownArrow,shapeNone),colorRed,0,H,-15);
PlotShapes(IIf(Sell,shapeHollowCircle,shapeNone),colorRed,0,SellPrice,0);

In this example the symbols with low positionscore are bought because the portfolio is flat and those are the only candidates:


1 Like

Both ExRem as well as Equity(1) calls should be avoided/removed.

The backtester handles redundant signals by itself, as explained in the manual Portfolio-level back testing. You should NOT REMOVE them, as typically removing them by either ExRem or Equity(1) will yield different results because these would work on ALL bars, regardless of what your ApplyTo/Range setting is.

Using ExRem/Equity(1) makes no sense. If somebody is using ExRems, it means that they don't know how to properly code their system. If they wanted to have impulse form, they should be using Cross(X, Y) instead of state form X > Y and later using ExRem to get impulses. Learn to code properly from the beginning instead of applying patch band-aids like ExRem.

So REMOVE ExRems and REMOVE Equity(1) calls.

They are NOT NEEDED at all.

1 Like

I understand. My use of Equity() comes from the need to visualize the stop signals on the chart.

I’d like to suggest a possible new chart setting for a future version of AmiBroker — perhaps for v7 — that I believe could help reduce one of the most common sources of confusion among users: the difference between what we see on the chart versus what the backtester actually executes.

Specifically, I’m thinking of something like:

SetChartOptions(ChartLikeBacktest);

Or alternatively:

SetChartOptions(ChartRemoveExtraSignals);

This setting would instruct the chart to automatically display only the trades that would be executed by the backtester — with redundant signals filtered out, just like the backtest engine does by default (e.g., in regular mode without the need for ExRem()).

The idea is not to change backtest logic, but to allow the chart to visually reflect what the backtester actually uses, especially regarding Buy/Sell signals and ignored redundancies.

This would be helpful because:

  • Most users rely heavily on visual inspection of the chart to verify their logic.
  • Without this, many are misled into thinking ExRem() is required for correct backtesting.
  • It would also make debugging and educational use of AmiBroker more intuitive and aligned with actual execution.

I think a chart-level switch like this could clear up a lot of confusion and help bridge the gap between what users "see" and what AmiBroker "does."

Thanks

EQUITY(1) does that already, but since it essentially performs single security backtest it doesn't account for PORTFOLIO effects such as running out of funds on other trades or hitting per portfolio limits. It is impossible to implement your suggestion on portfolio level since it would involve running full fledged portfolio backtest which is not feasible technically to be done on the fly each time chart needs refresh.

Instead you should just show right click on Analysis backtest results and choose show arrows. That is the way you should show arrows.

2 Likes