Scale in as percent of allocated equity

I am requesting help in write correct formula for scale in as percent of available equity for portfolio backtest. I want to allocate 20% of availability equity on a trade but want to allocate 50% of that equity to first entry and then 50% to second entry and exit 100% when exit conditions meet.

For example I have 250000 equity I want to allocate 50000 for first trade but want to use only 25000 for first entry and when criteria meet for second entry I want to spend 25000 again.
I have written code as shown below.

SetOption( "InitialEquity", 250000 );
MaxPos = 5;
SetOption( "MaxOpenPositions", MaxPos );
SetPositionSize( 20, spsPercentOfEquity);

Buy = RSI(4) <= 25 AND C > MA(Close,200) AND Close > 20 AND Volume > 100000;
Sell = RSI(2) > 55;

First entry criteria is 4 period rsi less than 25, sclae in 2nd entry when 4 period rsi is less then 20.

How amibroker treates scalein in porfolio test mode, is it treated as added trade or part of a trade for example if I am already in two trades with scale in how third sclae in will be treated cuz it will be 6 trades altogether, i want to restrict portfolio to 5 complete trades. Thanks

@aridzone It would be nice if there was a tutorial or book chapter somewhere that summarized the various methods.

I would like to help but unfortunately do not know much about the various (and very different methods) that you can code scale trading in Amibroker. You can see in the following examples that it can be achieved using various methods.

Here are a couple of resources that may help.
A nice video tutorial,

The user guide
https://www.amibroker.com/guide/h_pyramid.html

A trading system with multiple scale in and scale out orders.
http://www.amibroker.com/members/library/formula.php?id=1380

Using the Custom Backtest interface,
http://www.amibroker.com/kb/2006/04/24/using-redundant-signals-for-entries/comment-page-1/

Rebalancing with scaling in and out using the CBT
http://www.amibroker.com/kb/2006/03/06/re-balancing-open-positions/

2 Likes

Hi @aridzone

To get scaling working, you have to tell the Buy array what is an initial entry and what is a scaling entry with sigScaleIn. Then for the sizing, SetPositionSize takes an input type of Array, so you can define the value bar-by-bar.

In your case you have an added complication as it's quite possible you will get both your entries triggering on the same bar. So what do you do then? Presumably you'd like to enter with a full position size? You also might get the situation where you have two legs open and you get a third scaling entry trigger, so to avoid that you can use ExRem to remove extraneous Entry2 signals.

So for the code we divide the entries between an inital entry (when either Entry1 or both signals occur), and just Entry2 on its own. We then assign sigScalein to just Entry2 in the Buy statement. Position sizing is taken care of by multiplying the size for each entry by its occurrence and then adding them.

Also as a side note, you'll want to use impulse signals with Cross rather than state signals for this.

MaxPos = 5;
SetOption( "InitialEquity", 250000 );
SetOption( "MaxOpenPositions", MaxPos );

EntrySetup = C > MA(Close,200) AND Close > 20 AND Volume > 100000;

RSI4 = RSI(4);
Entry1 = Cross(25, RSI4); // First Entry
Entry2 = Cross(20, RSI4); // Second entry

ExitSignal = RSI(2) > 55;

BothOnSameBar = Entry1 AND Entry2; 
Entry2 = ExRem(Entry2, ExitSignal); // Remove subsequent Entry2 signals until ExitSignal
OnlyEntry2 = Entry2 AND NOT Entry1; 

InitialEntry = EntrySetup AND (BothOnSameBar OR Entry1);
ScaleInEntry = EntrySetup AND OnlyEntry2;

Buy = 	IIf(InitialEntry , 1, 
		IIf(ScaleInEntry, sigScaleIn, 0
		));

Sell = ExitSignal;

// Position sizing
EntryPosSize1 = 10; // Size for first entry
EntryPosSize2 = 10; // Size for second entry
PosSize = (Entry1 * EntryPosSize1) + (Entry2 * EntryPosSize2);
SetPositionSize(PosSize, spsPercentOfEquity);

Where you just get Entry1 occurring, the position size will be 10%:
image

The scale in following that then takes it up to 20%
image

And when you get both entries on the same bar, you enter with a 20% size
image

And to answer your final question, Amibroker treats all scaling entries and exits as occurring within the same trade. So scaling doesn't impact your MaxOpenPositions count, only the initial entry and final exit alter the number of positions open.

5 Likes

This is the cleared example of scalein that I have seen.

In my situation, I want to keep my existing entry and exit rules but add a rebalance once a year.

Would this be a suitable base to adapt from or is rebalancing much more complex?

I don’t know how it worked but I copied the code from the old example, except for the first 6 lines for rotational trading. I also put in my position percent.

http://www.amibroker.com/kb/2006/03/06/re-balancing-open-positions/

1 Like

Above is a great example of indicator based scalein. I have another script which is based on price of the entry and i can not wrap my head around it.

Logic of the system is following.

Close > 20
Close > ma(close,200)
volume > 250000

RSI(2) falling three days in a row, first day it is below 60 and today is less then 10 you buy half of allocated equity which is 20 % of available equity at close. Once you are in a trade if price closes below the entry price you scale in 50% of allocated equity, so suppose i have 100000 account i will open 5 max positions and will allocate 20% of 100000 which is 20000 so I will buy 10000 worth of stock. I have written first part of the code minus scale in part.

SetOption( "InitialEquity", 100000 );
MaxPos = 5;
SetOption ( "MaxOpenPositions", MaxPos );
PositionScore = 100-RSI(2);

RSI2         = 	 RSIa(C,2);
MA200	=	MA(C,200);

Buy = C	>	MA200
			AND      Sum	(	RSI2	<Ref(RSI2,-1),3	) ==	3
			AND      Ref	(	RSI2,-2)  < 60
			AND      RSI2	 <	10
			AND     Close > 20
			AND     Volume > 250000;
			
Sell = RSI2 > 70;

I have another question regarding scale in arrows on the chart. I can view trade arrows by right clicking on the trade list and choose show actual trade arrows, but it does not display scale in arrows. How do I add those arrows. Thanks for help

@aridzone For scale arrows perhaps something like this can help you.

PlotShapes(IIf(ScaleInEntry, shapeSmallUpTriangle, shapeNone),colorBlue, 0, L, Offset=-45);
PlotShapes(IIf(InitialEntry, shapeupTriangle, shapeNone),colorGreen, 0, L, Offset=-45);

image

1 Like

I assume that you are testing against a watch list that is large enough that you may not be able to take all entries on a given day. If that is the case, then one of the primary decisions you need to make is whether to handle your scaling in the Phase 1 AFL (i.e. with your Buy and Sell rules) or in a Custom Back Test (CBT). It’s much easier to handle it in Phase 1, as you can generate your scale-in signals with a simple loop, or perhaps even without looping if you’re proficient with AFL functions. The down side of the Phase 1 approach is that you have to assume that the first entry signal is the one that will actually be taken in the back test. For example, let’s say that stock XYZ signals a Buy on Monday, and you determine that your one scale-in should occur on Tuesday. In the back test, however, you have no capital to take the XYZ trade on Monday, so you probably just miss the XYZ trade completely, even if it signals another Buy sometime before the Sell signal occurs. If you can live with that, then this is probably the best option for you.

The CBT approach lets you get around this shortcoming, but if you’re not familiar with AFL and programming in general, then writing your own CBT can be a daunting task. As Tomasz has pointed out many times, 90+ % of traders should avoid writing a CBT because the potential for messing something up is too high. When I worked at Connors Research (where this strategy likely originated), we used the CBT a lot because of the level of control it gave us. But we also had colleagues with whom we could verify our results, and reduce the likelihood of an inadvertent mistake.

Matt

2 Likes

Hi @aridzone

You can do this by keeping track of the price the fist entry took place at, and basing your second signal on a close below that level. It’s perfectly feasible to do it using array processing (ie without the need for a loop). There is a famous strategy by Larry Connors called TPS which employs this same approach (maybe @mradtke had a hand in its development? :wink: ) If you study the code for that, which is available in the AFL Library, you should be able to figure out how to implement it in your own code. @quantboy already linked to the code in question above, and quoted below.

I have completed my script based on the great feedback I have received but position sizing is not working the way I want it, sometime it enters with full 20% allocated amount of available equity on the first entry and sometime with 50% of 20%.

SetOption( "InitialEquity", 100000 );
MaxPos = 5;
SetOption( "MaxOpenPositions", MaxPos );
PositionScore = 100-RSI(2);

MALongPeriod = 200;
RSI2 =	RSIa(C,2);

// Long logic

CloseAboveLongTermMA = C > EMA(C, MALongPeriod);

EntryRSI = Sum (RSI2 < Ref(RSI2,-1),3) ==3
         AND Ref(RSI2,-2) < 60
         AND RSI2 < 10;
ExitRSI = 70;

SetTradeDelays(0, 0, 0, 0);

Sell = RSI(2) > 70;
BarsSinceSell = BarsSince(Sell);

FirstEntry = CloseAboveLongTermMA AND EntryRSI;
InFirstPos = Flip(FirstEntry, Sell);
FirstTrigger = ExRem(InFirstPos, Sell);
BarsSinceFirstTrigger = BarsSince(FirstTrigger);
FirstTriggerPrice = IIf(BarsSinceFirstTrigger < BarsSinceSell, Ref(C,-BarsSinceFirstTrigger), 0 );

SecondEntry = CloseAboveLongTermMA AND C < FirstTriggerPrice AND InFirstPos AND Ref(InFirstPos,-1);
InSecondPos = Flip(SecondEntry, Sell);
SecondTrigger = ExRem(InSecondPos, Sell);
BarsSinceSecondTrigger = BarsSince(SecondTrigger);
SecondTriggerPrice = IIf(BarsSinceSecondTrigger < BarsSinceSell, Ref(C,-BarsSinceSecondTrigger), 0);

Buy = IIf( FirstTrigger, 1, 
             IIf( SecondTrigger,sigScaleIn, 0  ) );

 

Sell = ExRem(Sell,Buy);


//Position sizing

EntryPosSize1 = 10; // Size for first entry
EntryPosSize2 = 10; // Size for second entry
PosSize = (IsTrue(FirstEntry) * EntryPosSize1) + (IsTrue(secondentry) * EntryPosSize2);
             
SetPositionSize(PosSize, spsPercentOfEquity);


PlotShapes(IIf(FirstTrigger, shapeUpTriangle, shapeNone),colorGreen, 0, L);
PlotShapes(IIf(Secondtrigger, shapeupTriangle, shapeNone),colorBlue, 0, L);
PlotShapes(IIf(Sell,shapeDownTriangle, shapeNone),colorRed, 0, H);

3 Likes

Hi @aridzone

Try getting familiar with the steps in How do I debug my formula?

A simple exploration quickly identifies your issue:

Filter = 1;
AddColumn(FirstEntry, "FirstEntry", 1);
AddColumn(secondentry, "secondentry", 1);
AddColumn(FirstTrigger, "FirstTrigger", 1);
AddColumn(SecondTrigger, "SecondTrigger", 1);
AddColumn(Sell, "Sell", 1);
AddColumn(PosSize, "PosSize", 1);

image

Notice how FirstEntry and SecondEntry can be True on multiple bars, whereas FirstTrigger and SecondTrigger are ExRem'd until the Sell signal. Therefore you'll want to use FirstTrigger and SecondTrigger in your PosSize definition instead.

PosSize = (IsTrue(FirstTrigger) * EntryPosSize1) + (IsTrue(SecondTrigger) * EntryPosSize2);
3 Likes

Hi,

I have tried backtesting TPS code from AFL Library http://www.amibroker.com/members/library/formula.php?id=1380. But when I insert trade delays and buyprice and sell price as open, the position size is incorrect. What am I missing here ? Please see below code...

/*Description:
Based on "High Probability ETF Trading"
Larry Connors and Cesar Alvarez, page 87

This is a mean reversion system
Scale in as RSI falls below entry point during an uptrend, up to three times after initial entry

Connors tested 01/01/1993 through 31/12/2008 on a group of 20 liquid ETFs
Formula:
//	Connors TPS - ETFs.afl
//
//	Based on "High Probability ETF Trading"
//	Larry Connors and Cesar Alvarez, page 87
//
//	This is a mean reversion system
//	Scale in as RSI falls below entry point during an uptrend, up to three times

// after initial entry
//
//	Connors tested 01/01/1993 through 31/12/2008 on a group of 20 liquid ETFs
*/

SetOption( "InitialEquity", 10000000 );
MaxPos = 1;
SetOption( "MaxOpenPositions", MaxPos );
//TradeSize = 10000;
MaxPositionSizePercent = 100;
MALongPeriod = 200;


//---
// Long logic
//---

CloseAboveLongTermMA = C > MA(C, MALongPeriod);
LowerClose = C < Ref(Close, -1);
EntryRSI = 25;
ExitRSI = 70;
RSIPeriod = 2;
LowRSIBars = 2;
CurrentRSI = RSI(RSIPeriod);
MultipleDayLowRSI = Sum(RSI(RSIPeriod) < EntryRSI, LowRSIBars) == LowRSIBars;

FirstScaleIn = 0.1;
SecondScaleIn = 0.2;
ThirdScaleIn = 0.3;
FourthScaleIn = 0.4;

SetTradeDelays(1, 1, 1, 1);
BuyPrice = SellPrice = ShortPrice = CoverPrice = Open;

Sell = Cross( RSI(RSIPeriod), ExitRSI );
BarsSinceSell = BarsSince(Sell);

FirstEntry = CloseAboveLongTermMA AND MultipleDayLowRSI;
InFirstPos = Flip(FirstEntry, Sell);
FirstTrigger = ExRem(InFirstPos, Sell);
BarsSinceFirstTrigger = BarsSince(FirstTrigger);
FirstTriggerPrice = IIf(BarsSinceFirstTrigger < BarsSinceSell,Ref(C,-BarsSinceFirstTrigger), 0 );

SecondEntry = CloseAboveLongTermMA AND C < FirstTriggerPrice AND InFirstPos AND Ref(InFirstPos,-1);
InSecondPos = Flip(SecondEntry, Sell);
SecondTrigger = ExRem(InSecondPos, Sell);
BarsSinceSecondTrigger = BarsSince(SecondTrigger);
SecondTriggerPrice = IIf(BarsSinceSecondTrigger < BarsSinceSell,Ref(C,-BarsSinceSecondTrigger), 0);

ThirdEntry = CloseAboveLongTermMA AND C < SecondTriggerPrice AND InSecondPos AND Ref(InSecondPos,-1);
InThirdPos = Flip(ThirdEntry, Sell);
ThirdTrigger = ExRem(InThirdPos, Sell);
BarsSinceThirdTrigger = BarsSince(ThirdTrigger );
ThirdTriggerPrice = IIf(BarsSinceThirdTrigger < BarsSinceSell,Ref(C,-BarsSinceThirdTrigger), 0);

FourthEntry = CloseAboveLongTermMA AND C < ThirdTriggerPrice AND InThirdPos AND Ref(InThirdPos,-1);
InFourthPos = Flip(FourthEntry, Sell); 
FourthTrigger = ExRem(InFourthPos, Sell);
BarsSinceFourthTrigger = BarsSince(FourthTrigger);
FourthTriggerPrice = IIf(BarsSinceFourthTrigger < BarsSinceSell,Ref(C,-BarsSinceFourthTrigger ), 0);


//---
// Trade signals
//---

if( FirstScaleIn + SecondScaleIn + ThirdScaleIn + FourthScaleIn == 1.0 )
{
  //PositionScore = PositionScore = 100 - CurrentRSI ;  // favour low RSI

  Buy = IIf( FirstTrigger, 1, 
             IIf( SecondTrigger OR ThirdTrigger OR FourthTrigger, sigScaleIn, 0 ) );

  SetPositionSize(IIf( FirstTrigger, MaxPositionSizePercent*FirstScaleIn, 
                      IIf( SecondTrigger, MaxPositionSizePercent*SecondScaleIn,
							IIf( ThirdTrigger, MaxPositionSizePercent*ThirdScaleIn,MaxPositionSizePercent*FourthScaleIn ) ) ), 
                  IIf( Buy > 0, spsPercentOfEquity, spsNoChange ) );

  Sell = ExRem(Sell,Buy);
}


Plot(FirstTrigger, "T1", colorYellow, styleThick );
Plot(SecondTrigger, "T2", colorBlue, styleThick );
Plot(ThirdTrigger, "T3", colorBrown, styleThick );
Plot(FourthTrigger, "T4", colorWhite, styleThick );
//Plot(FirstTriggerPrice, "TP1", colorYellow, styleOwnScale );
//Plot(SecondTriggerPrice, "TP2", colorBlue, styleOwnScale );
//Plot(ThirdTriggerPrice, "TP3", colorBrown, styleOwnScale );
//Plot(FourthTriggerPrice, "TP4", colorBrown, styleOwnScale );
Plot(Sell, "Sell", colorRed, styleThick );

Below is the detailed trade report.
image

Thanks.

1 Like

Even your initial entry is incorrect, as it is using ~20% of your equity, not 10% as you expected. If you're going to use trade delays, then I believe you will need to adjust your position size array as well, with something like:

PositionSize = Ref(PositionSize,-1);

Please see: https://www.amibroker.com/guide/afl/settradedelays.html

2 Likes

Got it Matt. Thanks. I made following changes to the code as per your guidance & it works fine..

if( FirstScaleIn + SecondScaleIn + ThirdScaleIn + FourthScaleIn + FifthScaleIn  == 1 )
{
  //PositionScore = Ref(100 - CurrentRSI,-1) ;  // favour low RSI

  Buy = IIf( FirstTrigger, 1, 
             IIf( SecondTrigger OR ThirdTrigger OR FourthTrigger OR FifthTrigger, sigScaleIn, 0  ) );

  PositionSize = SetPositionSize(IIf( FirstTrigger, MaxPositionSizePercent*FirstScaleIn,     
                      IIf( SecondTrigger, MaxPositionSizePercent*SecondScaleIn, 
                         IIf( ThirdTrigger, MaxPositionSizePercent*ThirdScaleIn,
							IIf(FourthTrigger,MaxPositionSizePercent*FourthScaleIn,MaxPositionSizePercent*FifthScaleIn)))),
                                    IIf( Buy > 0, spsPercentOfEquity, spsNoChange ) );
							 

  Sell = ExRem(Sell,Buy);
}  

PositionSize = Ref(PositionSize,-1);
3 Likes