Partial exit with sigScaleOut doesn't work

I have got a problem with partial exit. Highly appreciated for any clue.
My strategy code is as follows. There are two entries with two different exits. There is no problem with entries, but partial exit with sigScaleOut seems not working.


SetBacktestMode(backtestRegularRawMulti);
SetOption( "MaxOpenPositions", 2 );
SetOption("MaxOpenLong", 2 );

t1 = 093000;	// enter time 1
t2 = 103000;	// enter time 2

t3 = 150000;	// exit time 1
t4 = 160000;	// exit time 2

buy1 = Cross(TimeNum(), t1);
buy2 = Cross(TimeNum(), t2);

sell1 = Cross(TimeNum(), t3);
sell2 = Cross(TimeNum(), t4);

in1 = in2 = 0;
Buy = Sell = Short = Cover = 0;
for( i = 0; i < BarCount; i++ ) 
{
	if (!in1 && buy1[i]) {
		in1 = True;
		Buy[i] = True;
	}
	
	if (!in2 && buy2[i]) {
		in2 = True;
		Buy[i] = True;
	} 
	
	// partial exit here
	if (in1 && sell1[i]) {
		Buy[i] = sigScaleOut;
		in1 = False;
	}
	
	if (in2 && sell1[i]) {
		Sell[i] = True;
		in2 = False;
	}
}

SetPositionSize( 1, spsShares ); 
SetPositionSize( 50, spsPercentOfPosition * ( Buy == sigScaleOut ) );

And here is the result. They always exit at the first hit.
partial%20exit1
And Scale in/Out is always 0.
partial%20exit2

Thanks a lot for any help and clue.

@zenchanhk try using the "Detailed Log" for your Reports. This will give you a deeper look at which trades are being executed. It may help you fix your code.

image

2 Likes

@zenchanhk, is sell1[i] the correct variable to use in this line?

This may not fix the main issue but it is something to check.

2 Likes

@portfoliobuilder I've looked into detailed report, but nothing special found. It just exits 2 positions at the first hit.
partial%20exit3

@beppe there is no problem with variable sell1.

I would suggest using a simple Explore so that you can observe the values of the Buy, Sell, and PositionSize arrays.

2 Likes

@zenchanhk,

Do your math.

SetPositionSize( 1, spsShares ); 
SetPositionSize( 50, spsPercentOfPosition * ( Buy == sigScaleOut ) );

50% of position and 1 (one) share bought is what?

If entry price was e.g. $100 -> so then it is $100 x 1 share = $100 position value.

Now 50% of $100 position value is....?
Right, it is $50.

How are you supposed to partially exit half of one share -> (e.g. $50/$100 = 0.5 share)?
How is that supposed to work?

You just need to think what you have written there... no logs required.


Next you should change

if (in2 && sell1[i]) {

to

if (in2 && sell2[i]) {

, shouldn't you?


Test code (I did not test it... it is your job):

SetBacktestMode(backtestRegularRawMulti);
SetOption( "MaxOpenPositions", 2 );
SetOption("MaxOpenLong", 2 );

t1 = 093000;	// enter time 1
t2 = 103000;	// enter time 2

t3 = 130000;	// exit time 1
t4 = 140000;	// exit time 2

buy1 = Cross(TimeNum(), t1);
buy2 = Cross(TimeNum(), t2);

sell1 = Cross(TimeNum(), t3);
sell2 = Cross(TimeNum(), t4);

in1 = in2 = 0;
Buy = Sell = Short = Cover = 0;
for( i = 0; i < BarCount; i++ ) 
{
	if (!in1 && buy1[i]) {
		in1 = True;
		Buy[i] = True;
	}
	
	if (!in2 && buy2[i]) {
		in2 = True;
		Buy[i] = True;
	} 
	
	// partial exit here
	if (in1 && sell1[i]) {
		Buy[i] = sigScaleOut;
		in1 = False;
	}
	
	if (in2 && sell2[i]) {
		Sell[i] = True;
		in2 = False;
	}
}

SetPositionSize( 2, spsShares ); 
SetPositionSize( 50, spsPercentOfPosition * ( Buy == sigScaleOut ) );
3 Likes

@fxshrat Thanks a lot for your time to looking into my code. It's my misunderstanding about SetPositionSize. I thought it should be defined as 1 spsShares since I buy only 1 contract at one time, and 50% of open positions (that should be 2 when it hits the first exit time). I tested it after I corrected errors in my code (including sell1 as sell2 ). But I still cannot get the expected behavior. Below are the detailed report and exploration.
partial%20exit4
partial%20exit5
I searched through knowledge bases about the error scaling ignored because signal predates already processed event, but nothing found. And PositionSize is quite weired in exploration.
Below is the trade list.
partial%20exit6
They all exit at the second hit.
Thanks again for any help and clue.

Yes, you are using regular raw mode. But you do not need regular raw mode.

Also read here.

AFL Function Reference - SETBACKTESTMODE

  • backtestRegularRawMulti - signal-based backtest, redundant (raw) entry signals are NOT removed, MULTIPLE positions per symbol will be open if BUY/SHORT signal is true for more than one bar and there are free funds, Sell/Cover exit all open positions on given symbol, Scale-In/Out work on all open positions of given symbol at once.

As for your picture...
If you look at your picture you can see that before first exit you have 2 positions a 2 shares so 2x2 = 4 shares since you have set SetPostionSize(2, spsShares).

After Scaleout you have 2 positions a 1 share since you have set 50% scale out, 1+1 = 2 shares and 2 positions. So it is correct (and considering your backtest mode).


Now, I do not think loop is even required to do what you are looking for. Here is loop less code and with backtestRegular mode. Also take a look how I modified position sizing. (I have changed exit times to avoid much scrolling on my end, so change it back to your needs).

SetBacktestMode(backtestRegular);
SetOption("MaxOpenPositions", 1 );// Note: positions are not shares
SetOption("MaxOpenLong", 1 );

t1 = 093000;	// enter time 1
t2 = 103000;	// enter time 2

t3 = 110000;	// exit time 1
t4 = 120000;	// exit time 2

tn = TimeNum();

buy1 = Cross(tn, t1);
buy2 = Cross(tn, t2);

sell1 = Cross(tn, t3);
sell2 = Cross(tn, t4);

Buy = buy1;
Buy = Buy + sigScaleIn * buy2; 
Buy = Buy + sigScaleOut * sell1; 

Sell = sell2;

Short = Cover = 0;

BuyPrice = SellPrice = Close;

SetPositionSize( 1, spsShares ); // enter 1 share
SetPositionSize( 1, spsShares * ( Buy == sigScaleIn ) ); // scale in 1 share
SetPositionSize( 1, spsShares * ( Buy == sigScaleOut ) ); // scale out 1 share
//SetPositionSize( 50, spsPercentOfPosition * ( Buy == sigScaleOut ) ); // scale out 50% of position

Now here are pictures:
5

6

7

8


BTW, -1050 in your exploration picture means 50% of position value (abs(-1050 - (-1000)) = 50).
Read here about what each value range means:

AFL Function Reference - SETPOSITIONSIZE

New SetPositionSize function automatically encodes new methods of expressing position size into old "positionsize" variable as follows:

  • values below -2000 encode share count,
  • values between -2000 and -1000 encode % of current position
  • values between -1000 and 0 encode % of portfolio equity
  • values above 0 encode dollar value

So it is not weird at all.

7 Likes

@fxshrat thanks so much for your great help. Seems ab backtest mode is more complicated than MultiCharts, I need more time to study the reference. Thanks again for your precious time to correct on my mistakes and misunderstanding.

2 Likes

No, it is just more comprehensive and powerful, more precise and very near to zero error-prone, much faster and not a copycat work. :wink:

5 Likes