PositionSize in CBT based on the previous close Price

Hello,
I would like to calculate PositionSize / Shares in CBT based on the previous Close price. The problem is that I can't get access to the title's close price.

I searched both the forum and the documentation. Don't find a solution. It's very likely a stupid rookie mistake I'm making.

Maybe someone can give me a hint.

This is my Code:

SetCustomBacktestProc("");
if (Status("action") == actionPortfolio) 
{
    cash = Null;
    OpenPosition = Null;
    StaticVarRemove("Cash");
    StaticVarRemove("GesamtWert");
    StaticVarRemove("OpenPosition");
    bo = GetBacktesterObject();
    bo.PreProcess();
    for (i = 0; i < BarCount; i++)
    {
        for (sig = bo.GetFirstSignal( i ); sig; sig = bo.GetNextSignal( i ) )
        {	
			if (sig.IsEntry())
			{
				CashAvailable = bo.Cash / (MaxPos - bo.GetOpenPosQty());
				SharesNeed = CashAvailable / Ref(Close, -1);
				SharesNeed = int(SharesNeed);
				_TRACE("sharesNeed  " + NumToStr(SharesNeed));
				_TRACE("Close  Ref - 1" + NumToStr(Ref(Close, -1)));
				_TRACE("Price  " + NumToStr(Sig.Price));
				_TRACE("Price Ref - 1  " + NumToStr(Ref(Sig.Price, -1)));
				
				//sig.PosSize = -2000 - SharesNeed; // failed
				//sig.PosSize = -2020;   // Test .... works
				sig.PosSize = bo.Cash / (MaxPos - bo.GetOpenPosQty()); // works
			}
        }	
        bo.ProcessTradeSignals( i );
        OpenPosition[i] = bo.GetOpenPosQty();
        cash[ i ] = bo.cash;
    }
    bo.PostProcess();
    StaticVarSet("Cash",cash);
    StaticVarSet("GesamtWert",bo.EquityArray);
    StaticVarSet("OpenPosition",OpenPosition);
}

Ref() is array function!
You should not use array within barcount loop.
You have to access element of array.
Please read Understanding how AFL works.

StaticVarSet("PrevClose_"+Name(), Ref(C, -1));

SetCustomBacktestProc("");
if (Status("action") == actionPortfolio) 
{
    cash = Null;
    OpenPosition = Null;
    StaticVarRemove("Cash");
    StaticVarRemove("GesamtWert");
    StaticVarRemove("OpenPosition");
    bo = GetBacktesterObject();
    bo.PreProcess();
    for (i = 0; i < BarCount; i++)
    {
        for (sig = bo.GetFirstSignal( i ); sig; sig = bo.GetNextSignal( i ) )
        {	
			if (sig.IsEntry())
			{
				prev_close = StaticVarGet("PrevClose_"+sig.Symbol);
				CashAvailable = bo.Cash / (MaxPos - bo.GetOpenPosQty());
				SharesNeed = CashAvailable / prev_close[ i ];
				SharesNeed = int(SharesNeed);
				_TRACE("sharesNeed  " + NumToStr(SharesNeed));
				_TRACE("Close  Ref - 1" + NumToStr(prev_close[i]));
				_TRACE("Price  " + NumToStr(Sig.Price));
				
				//sig.PosSize = -2000 - SharesNeed; // failed
				//sig.PosSize = -2020;   // Test .... works
				sig.PosSize = bo.Cash / (MaxPos - bo.GetOpenPosQty()); // works
			}
        }	
        bo.ProcessTradeSignals( i );
        OpenPosition[i] = bo.GetOpenPosQty();
        cash[ i ] = bo.cash;
    }
    bo.PostProcess();
    StaticVarSet("Cash",cash);
    StaticVarSet("GesamtWert",bo.EquityArray);
    StaticVarSet("OpenPosition",OpenPosition);
}
3 Likes

Hello fxShrat,
First of all, thank you very much for your quick solution. Perfect ...

I think I understand how AFL works. Worked for a long time with other backtesting software with a similar approach. But without such a good portfolio backtesting option as AB has. I have also been familiar with some programming languages for a long time. Starting with the first Basic, DBase, Clipper, Delphi, VB.Net and now C #.

What I did not understand correctly was that Barcount has a different meaning in CBT than in Barcount Loop to generate signals. But it's actually logical. Why should Barcount be run through countless times. Costs computing time. What I also did not understand correctly is that the access via StaticVarGet is probably not via the array [i] but apparently via DateTime and is therefore always perfectly synchronized. Before I read your post I had found another approach. Also works, but very cumbersome. All the code needs to be synchronized.

MyClose = Foreign(sig.Symbol,"C");
MyClosePr = MyClose[i-1];

Your approach is better, faster, and less error-prone. Thanks again to you for your help in understanding CBT better.

Foreign inside custom backtester is really bad (slow) and should never be used.

Storing Close in static variable is better, but in that particular case also sub-optimal
because if you backtest thousands of securities you would need A LOT OF RAM to store data in all those static variables.

To find out optimum solution that is fastest and requires least amount of memory,
you need to start thinking out of the box.

If you are NOT using PositionSize because you are calculating the size on your own in custom backtester, why don't you just use PositionSize variable to store required data? That way, you won't need to use static variables AT ALL and save heaps of RAM. Plus it will be faster too. And the code is shorter

PositionSize = Ref(C, -1); // you don't use pos size so just reuse it!

SetCustomBacktestProc("");
if (Status("action") == actionPortfolio) 
{
    bo = GetBacktesterObject();
    bo.PreProcess();
    for (i = 0; i < BarCount; i++)
    {
        for (sig = bo.GetFirstSignal( i ); sig; sig = bo.GetNextSignal( i ) )
        {	
			if (sig.IsEntry())
			{
				CashAvailable = bo.Cash / (MaxPos - bo.GetOpenPosQty());
				SharesNeed = floor( CashAvailable / sig.PosSize ); // at this moment PosSize really holds our prev close !
				sig.PosSize = -2000 - SharesNeed; // calc actual pos size !
			}
        }	
        bo.ProcessTradeSignals( i );
    }
    bo.PostProcess();
}
7 Likes

Hello Tomasz,

First of all, thank you for your answer.

I also noticed that Foreign is very awkward to use in the CBT.

Your approach will definitely save a lot of RAM. Every title already has the Positionize field.
At the moment I still use Positionsize in the following standard form.

SetPositionSize( Ref(PosScaleOut,-Delays), IIf( Buy == sigScaleOut, spsPercentOfPosition, spsNoChange ) );

PosScaleOut is an Array generated in Code.

So I'm going to use Signal.isScale () to process ScaleOut / ScaleIn here. I don't need it at the moment, but it is a good exercise to understand CBT better. _Trace will help quickly as always.

Thanks again.