Need help on backtesting Scaling In and Out

Hello community, I'm having some troubles testing the following idea:
The system calculate IBS indicator (internal bar strength) and if its value is inside a particular range then it buys a position and hold for a certain amount of bars. If while the position is still open it occurs that another value of IBS is into the range, then it buys another position and hold it for the same amount of bars (so this part of the trade will be closed after the first part). Buy the next Monday open and sell at the nth week close. This have to be allowed for a certain amount of times.
Furthermore this code have to work for a portfolio of stocks each one with it's IBS and holding period parameters.

I have studied the commands and backtest documentation and read several forum posts like

and few others

but there is something that I can't grasp.

I'm happy to share with you my attempts and I apologize in advance for the long message.

To fix the ideas please consider to trade on the ticker A2A.MI (data from yahoo! finance) and WEEKLY time frame. Backtest period: the first quarter of the 2010. IBS range is [0.4 : 0.55]. Holding period 3 weeks.

Signals and chart

FIRST ATTEMPT

initEqty = 3000;
maxPositions = 3;
investedSize = 1000;
holdingPeriod = 3;

SetBacktestMode(backtestRegularRawMulti);
SetOption("InitialEquity", initEqty);
SetOption("MaxOpenPositions", maxPositions);
SetPositionSize(investedSize, spsValue);
SetOption("CommissionMode", 1);
SetOption("CommissionAmount", 0.0);
SetTradeDelays(1,0,0,0);

IBS = SafeDivide((C-L),(H-L), Null);

Buy = IBS >= 0.40 AND IBS <= 0.55;
Sell = Ref(Buy, -holdingPeriod);

This doesn't work for me due the fact the sell command closes all the position at the same time, but I want a scale out closing instead.

SECOND ATTEMPT
Seeing that the duration of the trade is fixed, I've tried to use the applyStop function:

initEqty = 3000;
maxPositions = 3;
investedSize = 1000;
holdingPeriod = 3;

SetBacktestMode(backtestRegularRawMulti);
SetOption("InitialEquity", initEqty);
SetOption("MaxOpenPositions", maxPositions);
SetPositionSize(investedSize, spsValue);
SetOption("CommissionMode", 1);
SetOption("CommissionAmount", 0.0);
SetTradeDelays(1,0,0,0);
BuyPrice = Open;
SellPrice = Close;

IBS = (C-L)/(H-L);

Buy = IBS >= 0.40 AND IBS <= 0.55;
Sell = 0;
ApplyStop(stopTypeNBar, stopModeBars, holdingPeriod-1);

The resulting detailed report is the following:
Second Attempt Report

which is CORRECT:
there are no entry signal until the 19th of February, so the first buy is the next week Monday open and the close at the 12/03/2010 (three week later), the week after the first signal another IBS value is inside the range, so the trading system opens another position that is closed at the 19/03/2010.

THIRD ATTEMPT
Now that I have a working single stock trading system, I've slightly modified it to handle different time series for the portfolio backtesting:

initEqty = 3000;
SetBacktestMode(backtestRegularRawMulti);
SetOption("InitialEquity", initEqty);
SetOption("CommissionMode", 1);
SetOption("CommissionAmount", 0.0);
SetTradeDelays(1,0,0,0);
BuyPrice = Open;
SellPrice = Close;

if (Name() == "A2A.MI")
{	
	IBS = SafeDivide((C-L),(H-L),0);
	holdingPeriod = 3;
	maxPositions = holdingPeriod;
	SetOption("MaxOpenPositions", maxPositions);
	SetPositionSize(initEqty / holdingPeriod, spsValue);
	Buy = IBS >= 0.40 AND IBS <= 0.55;
	Sell = 0;
	ApplyStop(stopTypeNBar, stopModeBars, holdingPeriod-1);
}

Unfortunately this doesn't work in the portfolio backtesting because the closes are all at the same time at the end of the backtesting time, as shown below.

on a single "current" time series it works:
Current backtest

the same on multiple time series doesn't work anymore:
Portfolio backtest

Why this behavior?

Please note that there are also some days different than the ones of the previous backtest (for example the 14/02/10). I've verified that this occurs due the fact I did't padded and aligned the time series to a common one.

FOURTH ATTEMPT
My last attempt is using the sigScaleIn and sigScaleOut parameter for the buy command:

SetBacktestMode(backtestRegularRawMulti);
SetOption("AllowSameBarExit", True );
SetOption("InitialEquity", initEqty);
SetOption("MaxOpenPositions", maxPositions);
SetPositionSize(investedSize, spsValue);
SetOption("CommissionMode", 1);
SetOption("CommissionAmount", 0.0);
SetTradeDelays(1,0,0,0);

BuyPrice = O;
SellPrice = C;

IBS = SafeDivide((C-L), (H-L), Null);

Buy = False;
Sell = False;
IBScond = False;
scaleOutCond = False;
duration = 0;

//	Trading System
if (Name() == "A2A.MI")
{
	duration = 3;
	IBScond = IBS >= 0.40 AND IBS <= 0.55;
	SetOption("HoldMinBars", duration);
	scaleOutCond = Ref(IBScond, -duration);
	Buy = IIf(IBScond AND scaleOutCond == False, sigScaleIn, IIf(scaleOutCond AND IBScond == False, sigScaleOut,0));
	Sell = 0;
	
}

//	plottings
PlotOHLC(O,H,L,C, "", colorBlack, styleBar);
SetChartOptions(0, chartShowDates);
PlotShapes(IIf(IBScond,shapeUpArrow,shapeNone), colorBlack, 0, Ref(O, 1), 0, 1);
PlotShapes(IIf(scaleOutcond,shapeDownArrow,shapeNone), colorRed, 0, C, 0, 1);

I've set the option "AllowSameBarExit" to True, because I intended to hard control the signals in the Buy statement:

IBScond AND scaleOutCond == False
scaleOutCond AND IBScond == False

this is the result after padding and aligning to A2A.MI
Pad and Align

only for current timeserie
current scale in-out

portfolio backtest:
Portfolio scale in-out

As you can see there are 2 kind of errors:

  1. a wrong entry signal at the 08/01/2010 which shouldn't be
  2. the system doesn't close the trades after 3 weeks.

I can't figure out how to fix this, what I'm doing wrong?

Many thanks for any insight

Marco

Your post is super long and I did not have time digest it, but one staring problem just jumps out of your formula:

This is totally wrong use of SafeDivide. The purpose of SafeDivide is NOT to produce Nulls/Nans. The purpose of save divide is to produce regular NUMBER. Placing Nulls in the middle of array (where it is unexpected) can create problems. Don't do that.

You should just write:

Because this will give you correct result - 0 (not null). Null in AFL is NOT ZERO. Null means "I have no idea what the value is"

Hello Tomasz, thank you for your help. Unfortunately, changing the SafeDivide parameter to a number did't resolve my issue. Could you please take a look to the following?

initEqty = 3000;

SetBacktestMode(backtestRegularRawMulti);
SetOption("InitialEquity", initEqty);
SetOption("CommissionMode", 1);
SetOption("CommissionAmount", 0.0);
SetTradeDelays(1,0,0,0);
BuyPrice = Open;
SellPrice = Close;

if (Name() == "A2A.MI")
{	
	IBS = SafeDivide((C-L),(H-L),-1);
	holdingPeriod = 3;
	maxPositions = holdingPeriod;
	SetOption("MaxOpenPositions", maxPositions);
	SetPositionSize(initEqty / holdingPeriod, spsValue);
	Buy = IBS >= 0.40 AND IBS <= 0.55;
	Sell = 0;
	ApplyStop(stopTypeNBar, stopModeBars, holdingPeriod-1);
}

If I run this on the "current" stock it works correctly, but if I run this on a filter (watchlist) the trades remain open (it seems that the function applystop doesn't work correctly).

CURRENT LOG:
CURRENT

FILTER LOG:
FILTER

What I'm doing wrong or missing?

Marco

ApplyStop is a global backtester switch, you can not place it inside if() statement. It must be called unconditionally. Move ApplyStop outside any conditional calls.

1 Like

Many thanks Tomasz, if I don't understand badly, with "a global backtester switch" you mean that in a portfolio backtesting I can't model different time exits for different tickers if using the ApplyStop function, right?

No, that is incorrect. Generally you can do anything in AmiBroker.

Specifically, it depends on BACKTEST MODE.

Different ApplyStops per symbol are only possible in backtestREGULAR mode, because only in that mode stops are executed in first phase of backtest.

In your formula you have been using different mode:
SetBacktestMode(backtestRegularRawMulti);

In modes other than backtestRegular you can use custom backtester to do whatever you want, including implementing different stops for every symbol on your own.

1 Like

Now I'm trying another way. I've read the forum and the articles "Portfolio level backtesting", "Pyramiding /scaling", the Positionsize reference and see some videos that talk about the sigScaleIn buy parameter. I feel I've grasp the background logic, but I'm stuck again when I try to apply the scale out (sigScaleOut).

Basically the idea is to scale in when a buy signal occurs and to scale out some bars later. To control this I'll use the Ref function to simply delay the buy signals. For initial simplicity I'm gonna scale in and out a fixed amount of dollars:

holdingPeriod = 3;

IBS = SafeDivide((C-L),(H-L),-1);

buyCond = IBS >= 0.40 AND IBS <= 0.55;   // buy signal when this condition is true 
sellCond = Ref(buyCond, -holdingPeriod);   // sell after some bar since the signals

scaleInCond = buyCond AND NOT sellCond;     // scale-in condition
scaleOutCond = sellCond AND NOT buyCond;  //scale-out condition

Buy = IIf(scaleInCond, sigScaleIn, IIf(scaleOutCond, sigScaleOut, 0));
Sell = 0;

SetOption("InitialEquity", 10000);
PositionSize = 1000;

What I'm doing wrong here? Why there's not scaling out and I have buy signals when they shouldn't be?

Ps.: same ticker and backtest period of the previous examples

BuyCond is state signal. And SellCond being dependent on BuyCond.

/// @link http://www.amibroker.com/guide/h_backtest.html
/// @link http://forum.amibroker.com/t/backtesting-exit-method/1804/2
/// @link https://forum.amibroker.com/t/need-help-on-backtesting-scaling-in-and-out/29374/8
SetOption("InitialEquity", 10000);
SetOption("PortfolioReportMode", 1); //detailed log

Buy = Sell = 0;
Short = Cover = 0;

nbars = 3;

IBS = SafeDivide((C-L),(H-L),-1);

// buy signal
BuySignal = IBS >= 0.40 AND IBS <= 0.55;

buyentrybar = 0; 
for( i = 0; i < BarCount; i++ ) {	
	if( BuySignal[ i ] && buyentrybar == 0 ) {
		Buy[ i ] = sigScaleIn;		
		buyentrybar = i;
	} 
	
	// scale out if bar is nth one after Buy
	SellSignal = i == buyentrybar + nbars;	
	if( SellSignal && buyentrybar > 0 ) {
		Buy[ i ] = sigScaleOut;    
		buyentrybar = 0;
	}	 
}

SetPositionSize( 1000, spsValue );
SetPositionSize( 50, spsPercentOfPosition * (Buy == sigScaleOut)); // scale out 50% of position

Hello fxshrat and thanks for the answer. It gives me some very interesting points to be deeply studied.

One thing that is not clear for me is why sellCond behave differently from buyCond due the fact it is dependant from the second. In my mind if a new variable is created by shifting a state signal it should be a state signal too, why this doesn't happen?

Hey thanks for the code! It works almost correclty for the scale out part, but it doesn't for the scale in, look at this:

Grafico A2A

With the black spots and red vertical lines I've highlighted 2 consecutive values of IBS that should trigger the scale-in and with the two dotted line I've represented the two theoretical trades.
Your code simulate only the first trade (the blue one) not the green one. I need to catch all the two (or generally more) signals. Each buy condition should scale-in the position and then scale them out, each one after the imposed number of week since the signal.

I've tested your code with the "backtestRegularRawMulti" option but it doesn't handle the scenario correclty in this case too.

Now I'm gonna searching a way to store the number of trades that exist at the same time and when each one has been created, maybe in this way I can handle the contemporaneity issue.
btw... Have you any shortcut about where I can look for it?

This topic was automatically closed 100 days after the last reply. New replies are no longer allowed.