CBT Retrieve indicator value for each stock

What i would like to do is to retrieve data (for example Close price, atr....) from all components of a certain watchlist, during every OpenPos() iteration.

I'm working on the following code:

SetPositionSize(1,spsPercentOfEquity);
SetOption("UseCustomBacktestProc", True ); 

if( Status("action") == actionPortfolio )
{
  bo = GetBacktesterObject();
  bo.PreProcess(); // Initialize backtester

  for(bar=0; bar < BarCount; bar++)
	{
	bo.ProcessTradeSignals( bar );
	CurEquity = bo.Equity;
		for( pos = bo.GetFirstOpenPos(); pos; pos = bo.GetNextOpenPos() )
		{ 
				for( pos1 = bo.GetFirstOpenPos(); pos1; pos1 = bo.GetNextOpenPos() )
				{
				symbolClose 		= 		StaticVarGet( pos1.Symbol+"Close" );
				pos.AddCustomMetric( "Entry Close ", Lookup( symbolClose, pos.EntryDateTime ) );
				}
		}
   }
  // bo.ListTrades( );
   bo.PostProcess(); // Finalize backtester
}

StaticVarSet(Name() + "Close",C );
Buy = 1;
Sell=Short=Cover=0;
ApplyStop(stopTypeNBar,stopModeBars,20);

But i retrieve the following result

image

Any help could be very usefull
NE

1 Like

You are re-adding the custom trade metrics on every bar. Try using a high-level CBT with this structure instead:

if (Status("action") == actionPortfolio)
{
	// Get backtester object
	bo = GetBacktesterObject(); 	

	// Run the default AB back test, suppressing the trade list
	bo.Backtest(true);
	
	////////////////////////////////////////////////////
	// Custom Trade Metrics 
	////////////////////////////////////////////////////
	

	// Process all closed trades
	for (trade = bo.GetFirstTrade(); trade; trade = bo.GetNextTrade()) 
	{ 
		// Get the array index for this trade's entry and exit date
		entryBar = Lookup(BarIndex(),trade.entrydatetime,0);
		exitBar = Lookup(BarIndex(),trade.exitdatetime,0);

		// Your custom trade metrics here
	}	// end for all closed trades

	// Process open trades
	for (trade = bo.GetFirstOpenPos(); trade; trade = bo.GetNextOpenPos())
	{
		// Get the array index for this trade's entry and exit date.
		// Since the trade is still open, the exit date is the last bar of the test
		entryBar = Lookup(BarIndex(),trade.entrydatetime,0);
		exitBar = BarCount-1;
		
		// Your custom trade metrics here				
	}	// end for all open positions
	

	// Output the trade list, including any custom metrics
	bo.ListTrades();
		
}

3 Likes

Thank you mradtke, i share the solution:

SetOption("UseCustomBacktestProc", True );
if (Status("action") == actionPortfolio)
{
 	bo = GetBacktesterObject();
 	bo.Backtest(true);
 	for (trade = bo.GetFirstTrade(); trade; trade = bo.GetNextTrade())
 	{
 			for ( trade1 = bo.GetFirstOpenPos(); trade1; trade1  =  bo.GetNextOpenPos() )
 			{
 				symbolClose = StaticVarGet( trade1.Symbol+"Close" );
				trade.AddCustomMetric( "Entry Close", Lookup( symbolClose, trade.EntryDateTime ) );
			}
	}	
	bo.ListTrades();
 }
StaticVarSet(Name() + "Close",C );

But in this way, can i use bo.ScaleTrade function ?

Why do you think you need the bo.ScaleTrade() method? Usually scaling in and out can be done from your Phase 1 code using sigScaleIn and sigScaleOut.

The bo.ScaleTrade() method is part of the low-level CBT. What I provided for you was a skeleton for a high-level CBT. You cannot mix high-level and low-level CBT methods. You can read about the different types of CBT here: https://www.amibroker.com/guide/a_custombacktest.html

1 Like

Because i would like to adjust components exposure using indicators values.

for (trade = bo.GetFirstTrade(); trade; trade = bo.GetNextTrade())
	{
			sum	=	0;
			for ( trade1 = bo.GetFirstOpenPos(); trade1; trade1  =  bo.GetNextOpenPos() )
			{
				symbolATR = StaticVarGet( trade1.Symbol+"ATR" );
				sum = sum+symbolATR;
			}
			for ( trade1 = bo.GetFirstOpenPos(); trade1; trade1  =  bo.GetNextOpenPos() )
			{
				symbolATR = StaticVarGet( trade1.Symbol+"ATR" );
				w = (symbolATR/somma)*100;
				//trade.AddCustomMetric( "Weight", Lookup( w, trade.EntryDateTime ) );
			}
		}

In the first loop i sum the indicators value for each stock.
In the second loop calculate weight% for each stock (indicator/sum).
Now i would like to adjust components exposure by Weight: w*bo.equity()
(But, like you said, obviously is impossibile because i cannot mix high/low level backtesting.)

StaticVarSet(Name() + "ATR",ATR(14) );
Buy 	= 	1;
Sell	=	Short	=	Cover	=	0;
ApplyStop(stopTypeNBar,stopModeBars,20);

So, every month, i do a "weight adjusted" rebalancing.

Is also possibile to do it with low level backtesting ? Or just on high level ?
I'm trying to find a solution but i'm blocked.
Any input will be very appreciated.
Thank you in any case
NE

The last code example that you provided doesn't make any sense in the context of your previous messages. If you're calling your nested loops after calling bo.Backtest(), then the backtest is complete and the only open trades that you will find with bo.GetFirstOpenPos() / bo.GetNextOpenPos() are the ones which were not closed before the end of your analysis date range. There are likely other issues there as well.

To answer your question, anything that you can do with a high-level CBT can also be done with a mid-level or low-level CBT. Each new level of the CBT from high to low provides more control but also introduces more complexity, and therefore more opportunity for error. Whether you really need a CBT depends on your weighting and rebalancing requirements. But it is certainly possible, for example, to evaluate indicator values for all symbols in your watchlist during your Phase 1 code and to use the sum to determine position sizes. The rebalancing might be pretty difficult without a CBT unless you're willing to fully exit and then reenter positions rather than scaling in and out.

1 Like

Thank you again for helps.
It's ok for me to fully exit and reenter, and calculate separately the commission impact.
I don't find a convenience to scalein/out during a simulation. Currently, seems more easy to manage for me. I'm trying to work on it.

I really cannot understand why i retrieve an error if my log is 100% correct.
Why bo.scaletrade does not accept W as variable ?
W is a number like "1000" that change every iteration.

SetPositionSize(1,spsPercentOfEquity);
SetOption("UseCustomBacktestProc", True );

SetCustomBacktestProc("");
if (Status("action") == actionPortfolio) 
{
    bo = GetBacktesterObject();
    bo.PreProcess();
    
    for (i = 0; i < BarCount; i++)
    {       
     bo.ProcessTradeSignals( i );
		sums=0;

             for ( pos = bo.GetFirstOpenPos(); pos ; pos = bo.GetNextOpenPos())
				{
        		symbolClose = StaticVarGet( pos.Symbol+"ATR" );
				sums = sums+symbolClose;
				}		
        
					for ( pos = bo.GetFirstOpenPos(); pos ; pos = bo.GetNextOpenPos())
						{
						symbolATR = StaticVarGet( pos.Symbol+"ATR" );
						w = (symbolATR/sums)*bo.Equity();
						_TRACE(" "+w);
						//desired_value = W;
						desired_value = 1000;
						price = pos.GetPrice ( i , "C" ); 
						current_value = pos.GetPositionValue;
						diff = current_value - desired_value ; 
						bo.ScaleTrade( i, pos.Symbol, diff < 0, price, abs( diff ) );
						}
										   
    }
    bo.PostProcess();
}

StaticVarSet(Name() + "ATR",ATR(14) );
Buy 	= 	1;
Sell	=	Short	=	Cover	=	0;
ApplyStop(stopTypeNBar,stopModeBars,20);

You are wrong.

StaticVarGet( pos.Symbol + "ATR" ) 

is an array.

So w is array too.

Check yourself

_TRACEF( "type of w: %s", typeof(w) );

What is array and what is element of array?
Read basic here.

2 Likes

Thank you for response fxshrat.
And how can i put my desired value ?
Im trying with bo.EnterTrade( i, sig.Symbol, sig.IsLong(), sig.Price,.... );
But the problem seems the same...

To access elements of array you have to use subscripts.

e.g.

array = some_array;// this is an array of numbers -> "row" of multiple numbers.
element_of_array = array[i];// this is an element of upper array being type number
1 Like

Working on it, seems that i did what i wanted.
The following code works similar to Rotational, with the difference that every component of a watchlist is used, giving a weight proportionated to "positionscore", that in our case is ROC.

SetPositionSize(1,spsPercentOfEquity);
SetCustomBacktestProc("");
if (Status("action") == actionPortfolio)
{
    bo = GetBacktesterObject();    //  Get backtester object
    bo.PreProcess();    //  Do pre-processing
    for (i = 0; i < BarCount; i++)    //  Loop through all bars
    {
		eq 				= 		bo.Equity();
		sums 			= 		0;
		symbolROC		=		0;
		w				=		0;
		
				for (sig = bo.GetFirstSignal(i); sig; sig = bo.GetNextSignal(i))
					{
			
					if (sig.isEntry()) 				 
						{ 
						symbolROC = StaticVarGet( sig.Symbol+"ROC" );
						sums = (sums+symbolROC);
						}
					}

					for (sig = bo.GetFirstSignal(i); sig; sig = bo.GetNextSignal(i))
						{
							if (sig.isEntry())
							{
								symbolROC = StaticVarGet( sig.Symbol+"ROC" );
								w = (symbolROC/sums)*eq;
								bo.EnterTrade(i, sig.Symbol, True, sig.Price, w[i]);
							}
						}

					for (sig = bo.GetFirstSignal(i+1); sig; sig = bo.GetNextSignal(i+1))
						{
							if (sig.isExit())
							{
								bo.ExitTrade(i+1,sig.symbol,sig.Price);
							}
						}

        bo.HandleStops(i);    //  Handle programmed stops at this bar
        bo.UpdateStats(i, 1);    //  Update MAE/MFE stats for bar
        bo.UpdateStats(i, 2);    //  Update stats at bar's end
			
    }    //  End of for loop over bars
    bo.PostProcess();    //  Do post-processing
}

StaticVarSet(Name() + "ROC",100-ROC(14));
Buy 	= 	1;
Sell	=   0;
Short	=	Cover	=	0;
ApplyStop(stopTypeNBar,stopModeBars,10);

The only question is why, i've to refere to the next signal to let my code works:

for (sig = bo.GetFirstSignal(i+1); sig; sig = bo.GetNextSignal(i+1))
						{
							if (sig.isExit())
							{
								bo.ExitTrade(i+1,sig.symbol,sig.Price);
							}
						}

For the rest, it is working well.
NE

It has been all covered long time ago (2014) in very detail and with the code in the Knowledge Base
http://www.amibroker.com/kb/2014/11/20/how-to-show-indicator-values-in-backtest-trade-list/

No need to reinvent the wheel. Web search is all that is needed to answer pretty much every question on this forum.

1 Like