Trying to understand Multiple Scale Outs Example

Hi All,

I am attempting to understand the line by line logic of the ScaleOut example provided by the AmiBroker Users Guide (Portfolio-level back testing). I am hoping that someone can assist... I have copied the code directly from the users guide and attempted to go line by line to understand what is happening within the code. I commented to the right with " //** " followed by my question or guess at what is happening. If anyone is willing to help walk me through, it would be so hugely appreciated! :pray: :pray: :pray:

Buy = Cross( MA( C, 10 ), MA( C, 50 ) );
Sell = 0;

// the system will exit
// 50% of position if FIRST PROFIT TARGET stop is hit
// 50% of position is SECOND PROFIT TARGET stop is hit
// 100% of position if TRAILING STOP is hit

FirstProfitTarget = 10; // profit targets in percent
SecondProfitTarget = 20; // profit targets in percent
TrailingStop = 10; // Trail stop in percent

priceatbuy = 0; //** Is this establishing a baseline in the timeseries? As in the Buy price is timeseries 0?
highsincebuy = 0; //** Again, is this establishing a baseline in the timeseries and once the position moves in profitability, this will be increased? 

exit = 0;

for( i = 0; i < BarCount; i++ )
{
   if( priceatbuy == 0 AND Buy[ i ] ) //** Is this checking to see that there are no existing positions ("priceatbuy == 0") and that a new buy signal ("Buy[ i ]") has been generated?
    {
       priceatbuy = BuyPrice[ i ]; //** "Priceatbuy" now becomes the entry price from the buy signal?
    }

   if( priceatbuy > 0 ) //** Is this checking as to whether price has moved higher from the buy signal? 
    {
       highsincebuy = Max( High[ i ], highsincebuy ); //** Is this establishing the high point for the trailing stop further down in the code? Checking to see the high of today's bar vs the high since buy signal?

      if( exit == 0 AND
          High[ i ] >= ( 1 + FirstProfitTarget * 0.01 ) * priceatbuy ) //** Is this saying if there has not been an exit yet ("exit == 0"), and today's high has exceeded the first profit target in %, then scale out?
       {
         // first profit target hit - scale-out
         exit = 1; //** Now the code knows there has been 1 exit so far?
         Buy[ i ] = sigScaleOut; //** Can someone explain this line for me? Why is this different than the code for the scale out after "exit = 2" a few lines below?
       }

      if( exit == 1 AND 
          High[ i ] >= ( 1 + SecondProfitTarget * 0.01 ) * priceatbuy ) //** Is this saying, if there has already been one exit ("exit == 1") and today's high has exceeded the second profit target in %, then scale out?
       {
         // second profit target hit - exit
         exit = 2; //** Now the code knows there have been 2 exits?
         SellPrice[ i ] = Max( Open[ i ], ( 1 + SecondProfitTarget * 0.01 ) * priceatbuy ); //** Establishing execution price of second scale, being that a gap could have occurred beyond the profit target? Why does this code and the code of the first scale out differ?
       }

      if( Low[ i ] <= ( 1 - TrailingStop * 0.01 ) * highsincebuy ) //** If at any point the low of a bar is less than or equal to the trail stop in % off the highs since the entry?
       {
         // trailing stop hit - exit
         exit = 3; //** Now the code knows there have been 3 exits?   
         SellPrice[ i ] = Min( Open[ i ], ( 1 - TrailingStop * 0.01 ) * highsincebuy ); //** Establishing execution price of trail stop, being that a gap lower could have occurred beyond the set trail stop percentage?
       }

      if( exit >= 2 ) //** Confused on why this reads >= to 2... I am assuming this is resetting the code in some way, so shouldnt exit be = 3?
       {
         Buy[ i ] = 0;
         Sell[ i ] = exit + 1; // mark appropriate exit code
         exit = 0; //** Is this resetting the code to say that there are no exits as we are no longer in a position?
         priceatbuy = 0; //** Is this resetting the code to say that we are no longer in a position?
         highsincebuy = 0; //** Resetting the code that there are no highs yet established as there is no open position?
       }
    }
}

SetPositionSize( 100, spsPercentOfEquity );
SetPositionSize( 50, spsPercentOfPosition * ( Buy == sigScaleOut ) ); // scale out 50% of position
1 Like

Here are answers after your questions:
See //Answer: ... lines

// https://www.amibroker.com/guide/h_pyramid.html
// Questions and answers:
// https://forum.amibroker.com/t/trying-to-understand-multiple-scale-outs-example/27445
Buy = Cross( MA( C, 10 ), MA( C, 50 ) );
Sell = 0;

// the system will exit
// 50% of position if FIRST PROFIT TARGET stop is hit
// 50% of position is SECOND PROFIT TARGET stop is hit
// 100% of position if TRAILING STOP is hit

FirstProfitTarget = 10; // profit targets in percent
SecondProfitTarget = 20; // profit targets in percent
TrailingStop = 10; // Trail stop in percent

//** Is this establishing a baseline in the timeseries? As in the Buy price is timeseries 0?
// Answer: it is initialization of price at Buy (out of trade)
priceatbuy = 0; 
//** Again, is this establishing a baseline in the timeseries and once the position moves in profitability, this will be increased? 
// Answer: same as before. Initialize highest price since buy
highsincebuy = 0;

exit = 0;

for( i = 0; i < BarCount; i++ )
{
    //** Is this checking to see that there are no existing positions 
    //("priceatbuy == 0") and that a new buy signal ("Buy[ i ]") has been generated?
    
    // Answer: it checks whether being out of trade and 
    // in addition checking whether there is entry signal at same time/bar of check
    if( priceatbuy == 0 AND Buy[ i ] )
    {
        //** "Priceatbuy" now becomes the entry price from the buy signal?
        
        // Answer: Yes, it stores price at Buy entry to variable priceatbuy.
        priceatbuy = BuyPrice[ i ]; 
    }

    //** Is this checking as to whether price has moved higher from the buy signal?
    
    //Anwser: it checks whether being in long trade. 
    if( priceatbuy > 0 ) 
    {
        //** Is this establishing the high point for the trailing stop further down in the code? 
        // Checking to see the high of today's bar vs the high since buy signal?
        
        // Answer: It stores highest high price since Buy entry
        highsincebuy = Max( High[ i ], highsincebuy ); 
        
        //** Is this saying if there has not been an exit yet ("exit == 0"), 
        //and today's high has exceeded the first profit target in %, then scale out?
        
        // Answer: Yes.  
        if( exit == 0 AND
            High[ i ] >= ( 1 + FirstProfitTarget * 0.01 ) * priceatbuy ) {
                
            // first profit target hit - scale-out
            //** Now the code knows there has been 1 exit so far?
            
            // Answer: if statement is true then make exit = 1 from zero.
            // and scale out position            
            exit = 1; 
            
            //** Can someone explain this line for me? 
            //Why is this different than the code for the scale out after "exit = 2" a few lines below?
            
            // Answer: it is just an instruction to not exit completely but to reduce position.
            // It is partial exit in profit.
            Buy[ i ] = sigScaleOut;            
        }
        
        //** Is this saying, if there has already been one exit ("exit == 1") 
        //and today's high has exceeded the second profit target in %, then scale out?
        
        // Answer: No, it says if there has been scaled out of position 
        // and if second profit level has been reached then exit everything completely
        if( exit == 1 AND
                High[ i ] >= ( 1 + SecondProfitTarget * 0.01 ) * priceatbuy ) 
        {
            // second profit target hit - exit            
            //** Now the code knows there have been 2 exits?
            
            // Answer: the program just checks statements.
            // if statement is true then execute further code inside the statement
            // so here 2nd level is hit so set "exit" flag to 2 
            // so that the program can set Sell signal and reset everything in below statement "if( exit >= 2 )"             
            exit = 2; 
            
            // /** Establishing execution price of second scale, 
            // being that a gap could have occurred beyond the profit target? 
            // Why does this code and the code of the first scale out differ?
            
            // Answer: No, it is not a second scale out but regular exit and exit all of the remaining position
            // It differs because first one is scale out and this is complete exit.

            SellPrice[ i ] = Max( Open[ i ], ( 1 + SecondProfitTarget * 0.01 ) * priceatbuy ); 
        }

		//** If at any point the low of a bar is less than or equal to the trail stop in % off the highs since the entry?
		// Answer: Yes.
		
        if( Low[ i ] <= ( 1 - TrailingStop * 0.01 ) * highsincebuy ) 
        {
            // trailing stop hit - exit
            
            //** Now the code knows there have been 3 exits?            
            //Answer: similar to exit = 2 but here it is complete exit of remaining position in loss
           // so here loss level is hit so set "exit" flag to 3
            // so that the program can set Sell signal and reset everything in below statement "if( exit >= 2 )" 
            exit = 3; 
            
            //** Establishing execution price of trail stop, being that a gap lower could have occurred beyond the set trail stop percentage?
            
            // Answer: Yes.
            SellPrice[ i ] = Min( Open[ i ], ( 1 - TrailingStop * 0.01 ) * highsincebuy ); 
        }

		//** Confused on why this reads >= to 2... I am assuming this is resetting the code in some way, so shouldnt exit be = 3?
		
        // Answer: if there has been set Sell "exit" flags (either in profit "exit = 2" or in loss "exit = 3")
        // Then set Sell signal and reset everything else (since we are out of trade)
        if( exit >= 2 ) 
        {
            Buy[ i ] = 0;
            Sell[ i ] = exit + 1; // mark appropriate exit code            
            
            //** Is this resetting the code to say that there are no exits as we are no longer in a position?
            // Answer: Yes.
            exit = 0; 
            //** Is this resetting the code to say that we are no longer in a position? 
            //Answer: Yes, resetting price at Buy since we are out of trade
            priceatbuy = 0; 
            //** Resetting the code that there are no highs yet established as there is no open position?            
            //Answer:  Yes, resetting to original since we are out of trade
            highsincebuy = 0; 
        }
    }
}

SetPositionSize( 100, spsPercentOfEquity );
SetPositionSize( 50, spsPercentOfPosition * ( Buy == sigScaleOut ) ); // scale out 50% of position
7 Likes

@fxshrat massive help!! Thank you my friend, I really appreciate you taking the time. I will work with this and tweak on my end to see if I can fully understand how it flows through to the back test.

@fxshrat sorry to bother, but I have been messing with the code, adjusting the profit targets and the trailing stop % to understand the triggers but something seems to be off.

I back tested for NVDA 5/19/2021 through present as it would be a good example of the different scales after a successful buy signal. If I adjust the profit targets to extreme levels 50%+ in order to see the trail stop functionality, I see that it indeed works. As for the profit targets, it seems to scale all but 1 share of the position, and it does so on a random date (6/3/2021) where the high has not exceeded the initial % profit target as specified in the code... Is there an error in the code or am I missing something? This was straight from the User Guide, only adjustments I made were to the trail stop and profit target numbers. I've attached the analysis as well. Thank you again for your help.

Buy = Cross( MA( C, 10 ), MA( C, 50 ) );
Sell = 0;

// the system will exit
// 50% of position if FIRST PROFIT TARGET stop is hit
// 50% of position is SECOND PROFIT TARGET stop is hit
// 100% of position if TRAILING STOP is hit

FirstProfitTarget = 10; // profit targets in percent
SecondProfitTarget = 20; // profit targets in percent
TrailingStop = 10; // Trail stop in percent

priceatbuy = 0; 
highsincebuy = 0;  

exit = 0;

for( i = 0; i < BarCount; i++ )
{
   if( priceatbuy == 0 AND Buy[ i ] )
    {
       priceatbuy = BuyPrice[ i ]; 
    }

   if( priceatbuy > 0 )
    {
       highsincebuy = Max( High[ i ], highsincebuy ); 

      if( exit == 0 AND
          High[ i ] >= ( 1 + FirstProfitTarget * 0.01 ) * priceatbuy ) 
       {
         // first profit target hit - scale-out
         exit = 1; 
         Buy[ i ] = sigScaleOut;
       }

      if( exit == 1 AND 
          High[ i ] >= ( 1 + SecondProfitTarget * 0.01 ) * priceatbuy ) 
       {
         // second profit target hit - exit
         exit = 2; 
         SellPrice[ i ] = Max( Open[ i ], ( 1 + SecondProfitTarget * 0.01 ) * priceatbuy ); 
       }

      if( Low[ i ] <= ( 1 - TrailingStop * 0.01 ) * highsincebuy ) 
       {
         // trailing stop hit - exit
         exit = 3; 
         SellPrice[ i ] = Min( Open[ i ], ( 1 - TrailingStop * 0.01 ) * highsincebuy ); 
       }

      if( exit >= 2 )
       {
         Buy[ i ] = 0;
         Sell[ i ] = exit + 1; // mark appropriate exit code
         exit = 0; 
         priceatbuy = 0; 
         highsincebuy = 0; 
       }
    }
}

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

NVDA Analysis - 5.19.2021 - 8.31.2021

Apply:

RoundLotSize = 1;

If using non-zero Trade Delays with scaling in/out use:

SetPositionSize( 100, spsPercentOfEquity );
SetPositionSize( 50, spsPercentOfPosition * ( Buy == sigScaleOut ) ); // Scale out 50% of position
PositionSize = Ref( PositionSize, -1 ); // If using non-zero Trade Delays with scaling in/out

See Set Trade Delays.

1 Like

It's best to set trade delays to zero and using Ref() all in code at the top. Then you do not confuse yourself and rest can be kept as originally.

SetTradeDelays(0,0,0,0);// disable UI delays

Buy = Cross( MA( C, 10 ), MA( C, 50 ) );

delay = 1;
Buy = Ref(Buy,-delay);
BuyPrice = Iif(delay > 0, Open, Close);

Sell = 0;

// the system will exit
// 50% of position if FIRST PROFIT TARGET stop is hit
// 50% of position is SECOND PROFIT TARGET stop is hit
// 100% of position if TRAILING STOP is hit

FirstProfitTarget = 10; // profit targets in percent
SecondProfitTarget = 20; // profit targets in percent
TrailingStop = 10; // Trail stop in percent

priceatbuy = 0; 
highsincebuy = 0;  

exit = 0;

for( i = 0; i < BarCount; i++ )
{
   if( priceatbuy == 0 AND Buy[ i ] )
    {
       priceatbuy = BuyPrice[ i ]; 
    }

   if( priceatbuy > 0 )
    {
       highsincebuy = Max( High[ i ], highsincebuy ); 

      if( exit == 0 AND
          High[ i ] >= ( 1 + FirstProfitTarget * 0.01 ) * priceatbuy ) 
       {
         // first profit target hit - scale-out
         exit = 1; 
         Buy[ i ] = sigScaleOut;
       }

      if( exit == 1 AND 
          High[ i ] >= ( 1 + SecondProfitTarget * 0.01 ) * priceatbuy ) 
       {
         // second profit target hit - exit
         exit = 2; 
         SellPrice[ i ] = Max( Open[ i ], ( 1 + SecondProfitTarget * 0.01 ) * priceatbuy ); 
       }

      if( Low[ i ] <= ( 1 - TrailingStop * 0.01 ) * highsincebuy ) 
       {
         // trailing stop hit - exit
         exit = 3; 
         SellPrice[ i ] = Min( Open[ i ], ( 1 - TrailingStop * 0.01 ) * highsincebuy ); 
       }

      if( exit >= 2 )
       {
         Buy[ i ] = 0;
         Sell[ i ] = exit + 1; // mark appropriate exit code
         exit = 0; 
         priceatbuy = 0; 
         highsincebuy = 0; 
       }
    }
}

SetPositionSize( 100, spsPercentOfEquity );
SetPositionSize( 50, spsPercentOfPosition * ( Buy == sigScaleOut ) ); // scale out 50% of position
1 Like

Thank you @fxshrat for working through this with me. My apologies but I am still seeing an error in the first target execution price when I go line by line, using the updated code in the post above. If I use the same back test example, NVDA for 5/24/2021 through present, there is an entry on 5/25/2021. That entry price is $157.66. If I look at the code, first profit target is set to +10%, which should be a price of $173.42. That would have been reached on 6/4/2021. The back test does execute a scale out on 6/4/2021, but does so on the open price. This leads me to believe it is looking into the future at that day's high having exceeded the profit target, but executing at the day's open price. If I look at profit target 2 of +20%, it executes correctly on 6/18 at the intraday price of $189.19.

Is there something that needs to be added to the "Buy [ i ] = sigScaleOut" section of the code for the first profit target to execute at the correct price intraday on 6/4/2021?

Thank you again for your help, really trying to make sense of the example :pray:

NVDA Analysis - 5.24.2021 - 8.31.2021 - Scale 1

Replace

      if( exit == 0 AND
          High[ i ] >= ( 1 + FirstProfitTarget * 0.01 ) * priceatbuy ) 
       {
         // first profit target hit - scale-out
         exit = 1; 
         Buy[ i ] = sigScaleOut;
       }

by

      scale_level = ( 1 + FirstProfitTarget * 0.01 ) * priceatbuy;
      if( exit == 0 AND High[ i ] >= scale_level)
       {
         // first profit target hit - scale-out
         exit = 1; 
         Buy[ i ] = sigScaleOut;
         BuyPrice[ i ] = scale_level;// scale-out price
       }
1 Like

@fxshrat , beautiful! That works perfectly. Thank you for taking the time to help walk me through this!

1 Like

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