Position Sizing on Available Cash + Value of Open Positions

Hi,

I am hoping you can assist with the following issue.

Problem:
Issue with code defining position sizing as Cash Available + Existing Value (Price * Quantity) of All Open Positions.

Context:
Two different systems tested on a single instrument (such as a futures contract or a stock, rather than a portfolio consisting of numerous instruments) with the same entry and exit signal, and one system defining position sizing as a Percent of Equity, and the other defining position sizing as Cash Available + Existing Value (Price * Quantity) of All Open Positions should return the same results in the backtest report.

However, the backtest report indicates otherwise, demonstrating that my code somewhere is incorrect. The comparative codes and backtest reports have been attached below:

Codes and Reports

//System With Position Sizing Based on Cash Available + Existing Value (Price * Quantity) of All Open Positions.//

// ENTRY SIGNALS // 
Buy = Close > Ref(HHV(High, 126), -1 ); //Long entry signal// 
Short = Close < Ref(LLV(Low, 126), -1 ); //Short entry signal// 
Sell = Cover = 0; 
 
// EXIT SIGNALS // 
InitialStopLength = (5 * ATR(63)); 
SubsequentStopLength = (8 * ATR(63)); 
 
ApplyStop(stopTypeLoss, stopModePoint, InitialStopLength, 2, False, 1); 
ApplyStop(stopTypeTrailing, stopModePoint, SubsequentStopLength, 2, True, 1); 
 
// POSITION SIZING // 
if(Status("action") == actionPortfolio) { 
    bo = GetBacktesterObject(); 
    bo.PreProcess(); 
 
    // Loop through bars 
    for (i = 1; i < BarCount; i++) { 
    
            // Calculate position size dynamically based on the new formula 
            initialBuyOrShortPrice = Open[i]; 
            currentCash = bo.Cash; 
            volatility = Ref(ATR(63),-1); 
            positionrisk = 0.5; 
 
            positionSize = positionrisk*(initialBuyOrShortPrice + currentCash)/volatility; 

        // Set position size for each bar 
        SetPositionSize(positionSize, spsShares); 
    } 
 
    bo.PostProcess(); 
} 

//System With Position Sizing Based on Percent of Equity//

// ENTRY SIGNALS //
Buy = Close > Ref(HHV(High, 126), -1 ); //Long entry signal//
Short = Close < Ref(LLV(Low, 126), -1 ); //Short entry signal//
Sell = Cover = 0;


// EXIT SIGNALS //
InitialStopLength = (5 * ATR(63));
SubsequentStopLength = (8 * ATR(63));
	
ApplyStop (stopTypeLoss, stopModePoint, InitialStopLength, 2, False, 1);
ApplyStop (stopTypeTrailing, stopModePoint, SubsequentStopLength, 2, True, 1);


// POSITION SIZING //
Volatility = Ref(ATR(63),-1);
PercentRisk = 0.5;
PercentSizeLong = PercentRisk*BuyPrice/Volatility;
PercentSizeShort = PercentRisk*ShortPrice/Volatility;
SetPositionSize(PercentSizeLong,spsPercentOfEquity);
SetPositionSize(PercentSizeShort,spsPercentOfEquity);

The backtest reports below identifies the resulting differences between the two codes. I have highlighted red circles around when the issue begins to emerge. Further, the divergence between the two only began to emerge ~30 years after (in 2011) the beginning of my dataset (in 1980).

//System With Position Sizing Based on Cash Available + Existing Value (Price * Quantity) of All Open Positions.//

//System With Position Sizing Based on Percent of Equity//

For additional reference, I have relied upon the following forum posts to develop my position sizing code based on available cash:

Thank you.

Your code is wrong, because SetPositionSize() function can only be sensibly called in FIRST phase of backtest. You are calling it inside CBT which is obviously wrong. Calling it in CBT has no effect at all.

To set position size in second backtest phase (in CBT) you have to modify the sig.PosSize property of each signal prior to processing.

Thanks @Tomasz for the clarification.

I have updated my code below, however the original problem continues to persist as the resulting trades between the code using position sizing as a Percent of Equity and the new code using position sizing as Cash + Initial Value of Open Positions are different.

The updated code is:

// ENTRY SIGNALS // 
Buy = Close > Ref(HHV(High, 126), -1 ); //Long entry signal// 
Short = Close < Ref(LLV(Low, 126), -1 ); //Short entry signal// 
Sell = Cover = 0; 
 
// EXIT SIGNALS // 
InitialStopLength = (5 * ATR(63)); 
SubsequentStopLength = (8 * ATR(63)); 
 
ApplyStop(stopTypeLoss, stopModePoint, InitialStopLength, 2, False, 1); 
ApplyStop(stopTypeTrailing, stopModePoint, SubsequentStopLength, 2, True, 1); 
 
Volatility = Ref(ATR(63),-1);
positionrisk = 0.5; 

// POSITION SIZING // 
SetCustomBacktestProc("");

if(Status("action") == actionPortfolio) { 
    bo = GetBacktesterObject(); 
    bo.PreProcess(); 
 
    // Loop through bars 
    for (i = 1; i < BarCount; i++) { 
        for (sig = bo.GetFirstSignal(i); sig; sig = bo.GetNextSignal(i) )
        {	
            initialBuyOrShortPrice = Open[i]; 
            currentCash = bo.Cash; 
            
		    // Set position size for each signal
            sig.PosSize = positionrisk * (initialBuyOrShortPrice + currentCash) / LastVisibleValue(Volatility); 

        }
        bo.ProcessTradeSignals(i);	//  Process trades at bar (always required)
    } 
 
    bo.PostProcess(); 
} 

Below is the updated backtest report providing the following results and continues to show a difference in the number of shares purchased:

Compared to:

I have also relied upon the following forum post for my updated code:

Thank you.

@HyO your CBT your CBT still seems wrong.

To access a specific data series (Open) and the calculated data (Volatility) associated to a stock you should store them as StaticVars before entering the CBT procedure (creating, for example, a variable name that includes the NAME() like StaticVarSet("open_" + Name(), Open); and StaticVarSet("volatility_" + Name(), Volatility);.

You should then retrieve these saved arrays in the CBT creating the static variable names using the sig.Symbol for the ticker name part.

Moreover, using LastVisibleValue(Volatility) here is wrong. You should probably use the correct "volatility" array retrieved from the StaticVar associated with the sig.Symbol (volatility = StaticVarGet("volatility_" + sig.Symbol);) and use the current signal bar (volatility[i]).

Finally, I don't entirely understand why you are doing this and in this way, but before understanding whether your logic is correct you need to fix the rest of the code as suggested.

In any case, I recommend you read this KB article that explains better how to set the StaticVars to be used in the backtest and additionally this past thread.

1 Like

Thanks @beppe for the feedback. I have updated my code as follows, but the same issue continues to persist of different backtest results.

Volatility = Ref(ATR(63),-1);
//volatility = StDev(Close, 14); // Adjust the period for volatility calculation 
positionrisk = 0.5; 

// POSITION SIZING //

if(Status("action") == actionPortfolio) {
    bo = GetBacktesterObject();
    bo.PreProcess();
    // Loop through bars
    for (i = 5; i < BarCount; i++) {
        for (sig = bo.GetFirstSignal(i); sig; sig = bo.GetNextSignal(i) )
        {
            // retrieve it INSIDE the loops - based on the symbol processed - as needed like:
            initialBuyOrShortPrice = StaticVarGet( "open_" + sig.Symbol ); 
            volatility = StaticVarGet("volatility_" + sig.Symbol); 
            currentCash = bo.Cash;

		    // Set position size for each signal
            sig.PosSize = positionrisk * (initialBuyOrShortPrice + currentCash) / volatility[i];

        }
        bo.ProcessTradeSignals(i);	//  Process trades at bar (always required)
    }

    bo.PostProcess();
}

// ENTRY SIGNALS // 
Buy = Close > Ref(HHV(High, 126), -1 ); //Long entry signal// 
Short = Close < Ref(LLV(Low, 126), -1 ); //Short entry signal// 
Sell = Cover = 0; 
 
// EXIT SIGNALS // 

InitialStopLength = (5 * ATR(63)); 
SubsequentStopLength = (8 * ATR(63)); 
 
ApplyStop(stopTypeLoss, stopModePoint, InitialStopLength, 2, False, 1); 
ApplyStop(stopTypeTrailing, stopModePoint, SubsequentStopLength, 2, True, 1); 

As for the raitonale behind this approach, I hope the following example will help clarify my logic:

Setting

  • Portfolio holds $100 cash
  • Portfolio only consists of stocks A and B, and both are $10/share
  • Position sizing is defined as 10% of Equity

Scenario 1: Position Sizing Based on Percentage of Equity

  1. A signal is given to buy stock A, leading to 1 (position sizing = 10% of Equity, in this case $100) share being purchased
  2. The share price of stock A then leaps to $110, leading to a new portfolio equity of $200
  3. A signal is given to buy stock B, leading to 2 (position sizing = 10% of Equity, now $200) shares being purchased

Scenario 2: Position Sizing Based on Cash+Initial Value of Open Positions

  1. A signal is given to buy stock A, leading to 1 (position sizing = 10% of Cash+Initial Value of Open Positions, in this case $100) share being purchased
  2. The share price of stock A then leaps to $110, leading to a new portfolio equity of $200, consisting of $90 cash and $110 value of stock A
  3. A signal is given to buy stock B, leading to 1 (position sizing = 10% of Cash+Initial Value of Open Positions, in this case $90 Cash+$10 Initial Purchase Value of Stock A) share being purchased

Conclusion: As the example seeks to demonstrate, pursuing a position sizing strategy using Percent of Equity leads to larger or smaller position sizing according to an increase or decrease in the open equity value of the portfolio. By contrast using a Cash+Initial Value of Open Positions enables the portfolio to seek a more consistent position sizing approach.

Did you read Knowledge Base article:

Risk-based VanTharp Position Sizing does not require custom backtester at all.

You should add some _TRACE statements to your CBT so that you can verify the values you're calculating there. One thing that looks very suspect is this line:

Not sure what you were trying to accomplish here, but adding the price of the trading instrument to the available cash in the account is unlikely to do what you intended.

In addition, the Open price and Volatility which you have stored in static variables (I assume, since you did not show this in your code) are arrays. To get the values for the current bar, you should specify an array index, for example:

sig.PosSize = ... volatility[i]
1 Like