Assistance requested PLEASE, acquire price on scaled entry using mid-level CBT

I am attempting to find the entry price on scaled trades. Current code returns N/A, could someone please provide some assistance?

// Original source
//https://forum.amibroker.com/t/scale-in-as-percent-of-allocated-equity/2524/3

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);

// modification to original below
Short = Cover =0;
 
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))
        {    
			inE = IIf(InitialEntry, BuyPrice, 0);
			scE = IIf(ScaleInEntry, BuyPrice, 0);
        }

		bo.ProcessTradeSignals(i);   
	}
		for( trade = bo.GetFirstTrade(); trade; trade = bo.GetNextTrade() )
		{
			inE = StaticVarGet( trade.Symbol + "inE" );
			trade.AddCustomMetric( "inE", Lookup( inE, trade.EntryDateTime ) );
			scE = StaticVarGet( trade.Symbol + "scE" );
			trade.AddCustomMetric( "scE", Lookup( scE, trade.EntryDateTime ) );
		}


	bo.PostProcess();  
	
	StaticVarSet( Name() + "inE", inE ); 
	StaticVarSet( Name() + "scE", scE );
}

Your CBT code is in correct.

InitialEntry, ScaleInEntry and BuyPrice are arrays of 1st phase of BT.

Take a look at CBT manual - Signal object... sig.IsScale, sig.Price, ...

Porfolio Backtester Interface Reference:

So e.g.

// Incomplete code snippet!
// ....
    for (i = 0; i < BarCount; i++)    
    {       
        for (sig = bo.GetFirstSignal(i); sig; sig = bo.GetNextSignal(i))
        {    
			if ( sig.IsScale() )
				_TRACEF( "Scale Price: %g", sig.Price);
        }

		bo.ProcessTradeSignals(i);   
   }
//...
4 Likes

Thank you very much! As always you are a life saver. Adding _TRACEF function was a huge help but I'm still trying to get data to show up on trade list via trade.AddCustomMetric

You should use RawTextOutput since there can be multiple scale in/out per result list row. Also scale in/out will be at different dates other than trade.EntryDateTime/ExitDateTime.

// Incomplete code snippet! Look below link for what it is about
/// @link https://forum.amibroker.com/t/assistance-requested-please-acquire-price-on-scaled-entry-using-mid-level-cbt/13219/4
// ....
    dt = DateTime();

    for (i = 0; i < BarCount; i++)    
    {  
        for (sig = bo.GetFirstSignal(i); sig; sig = bo.GetNextSignal(i))
        {    
			if ( sig.IsScale() ) {
				sig_dt = dt[i];
				bo.RawTextOutput( StrFormat("Date: %s, Scale Price: %g", DateTimeToStr(sig_dt), sig.Price));			
			}
        }

		bo.ProcessTradeSignals(i);   
	}
// ...
3 Likes

Is it possible to the have the data shown in the trade list in such a format where 2nd scale-in entry is shown in separate column along the same row as the initial entry? For example, in the image shown below it would be ideal if the 2nd scale-in price was listed under column heading E2nd followed by the 2nd scale-in date under column heading E2ndDt while still lining up with the initial entry Date of 01/20/2011. Data from the 3rd entry would like wise show up in a similar fashion.

My apologies, I am still a bit confused. Below is code snippet for the image above. Is it possible to some how get and store the data from the first phase to a variable which may be able to be show in via trade.AddCustomMetric on the trade list? I only ask because that currently seems like my best option to get the data to line up as mentioned above.

// Incomplete code snippet! Look below link for what it is about
/// @link https://forum.amibroker.com/t/assistance-requested-please-acquire-price-on-scaled-entry-using-mid-level-cbt/13219/4
// ....
SetCustomBacktestProc(""); 
if( Status( "action" ) == actionPortfolio )
{
    bo = GetBacktesterObject();
    bo.PreProcess(); 
    dt = DateTime();
    
    for (i = 0; i < BarCount; i++)    
    {       
        for (sig = bo.GetFirstSignal(i); sig; sig = bo.GetNextSignal(i))
        {    
				sig_dt = dt[i];
				//bo.RawTextOutput( StrFormat("Date: %s, Scale Price: %g", DateTimeToStr(sig_dt), sig.Price));	
        }

		bo.ProcessTradeSignals(i);   
	}
		for( trade = bo.GetFirstTrade(); trade; trade = bo.GetNextTrade() )
		{
			E2nd = StaticVarGet( trade.Symbol + "E2nd" );
			trade.AddCustomMetric( "E2nd", Lookup( E2nd, trade.EntryDateTime ) );
			E2ndDt = StaticVarGet( trade.Symbol + "E2ndDt" );
			trade.AddCustomMetric( "E2ndDt", Lookup( E2ndDt, trade.EntryDateTime ) );
			
			E3rd = StaticVarGet( trade.Symbol + "E3rd" );
			trade.AddCustomMetric( "E3rd", Lookup( E3rd, trade.EntryDateTime ) );
			E3rdDt = StaticVarGet( trade.Symbol + "E3rdDt" );
			trade.AddCustomMetric( "E3rdDt", Lookup( E3rdDt, trade.EntryDateTime ) );
		}


	bo.PostProcess();  
	
	StaticVarSet( Name() + "E2nd", E2nd ); 
	StaticVarSet( Name() + "E2ndDt", E2ndDt );
	StaticVarSet( Name() + "E3rd", E2nd ); 
	StaticVarSet( Name() + "E3rdDt", E2ndDt );
}

As to your question question: yes it is possible to pass data from first phase to second phase and it is described in the Knowledge Base:
https://www.amibroker.com/kb/2014/11/20/how-to-show-indicator-values-in-backtest-trade-list/

2 Likes

Despite several weeks in attempting to find a solution, I am still stuck? Could someone PLEASE be kind enough to provide some assistance on how to display the second signal price followed by the second signal entry date on the trade list via AddCustomMetric function.

I can not get the data to line up correctly in the trade list:

not%20lined%20up%20correctly

// Original source
//https://forum.amibroker.com/t/scale-in-as-percent-of-allocated-equity/2524/3

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);

// modification to original follows below
Short = Cover =0;
 
SigPri1 = C;
AddToComposite(SigPri1, "~close1_"+Name(), "C", 1+2+8);
SigPri2 = C;
AddToComposite(SigPri2, "~close2_"+Name(), "C", 1+2+8);

SetCustomBacktestProc(""); 
if( Status( "action" ) == actionPortfolio )
{
    bo = GetBacktesterObject();
    bo.PreProcess(); 
    dt = DateTime();
	TE1st = E1st = E1stDt = TE2nd = E2nd = E2ndDt = NumTrades = 0;
        
    for (i = 0; i < BarCount; i++)    
    {       
        for (sig = bo.GetFirstSignal(i); sig; sig = bo.GetNextSignal(i))
        {    
      		if ( sig.Type != 5 && sig.IsEntry())
      		{
      		    NumTrades++;
				TE1st = Foreign("~close1_"+sig.Symbol, "C");
				VarSet("E1stDt" + NumTrades, dt[i]);
				VarSet("E1st" + NumTrades, TE1st[i]);
				_TRACE( StrFormat("Date: %s, 1stEnt P: %g", DateTimeToStr(dt[i]), sig.Price));
			}
			else
			{
      			NumTrades++;
				TE2nd = Foreign("~close2_"+sig.Symbol, "C");
				VarSet("E2ndDt" + NumTrades, dt[i]);
				VarSet("E2nd" + NumTrades, TE2nd[i]);
				_TRACE( StrFormat("Date: %s, 2ndEnt P: %g", DateTimeToStr(dt[i]), sig.Price));
			}
	
        }

		bo.ProcessTradeSignals(i);   
	}
	
	NumTrades = 0;
		for( trade = bo.GetFirstTrade(); trade; trade = bo.GetNextTrade() )
		{
			NumTrades++;
			EE1stDt = VarGet ("E1stDt" + NumTrades);
			EE1st = VarGet ("E1st" + NumTrades);
			EE2ndDt = VarGet ("E2ndDt" + NumTrades);
			EE2nd = VarGet ("E2nd" + NumTrades);
			
			trade.AddCustomMetric("E1stDt", DateTimeToStr(EE1stDt));
			trade.AddCustomMetric("E1st", EE1st);
			
			trade.AddCustomMetric("E2ndDt", DateTimeToStr(EE2ndDt));
			trade.AddCustomMetric("E2nd", EE2nd);
		}

	bo.PostProcess();  
}

Any help would be greatly appreciated.

The problem is that in your main bar-by-bar loop (the one with the bo.ProcessTradeSignals() call), you are generating a trade number (NumTrades) which is incremented every time you get a new entry. In other words, you are numbering your trade based on the order of entry.

In the bo.GetFirstTrade()/GetNextTrade() loop, the trades will be retrieved in the order that they were exited, not the order they were entered. Therefore, trying to match up the trades in this way won't work.

There may be multiple ways to solve this problem, but personally I would switch to a low-level CBT so that I could call bo.EnterTrade(), immediately retrieve the open trade object, and then call trade.AddCustomMetric() to add the initial entry date and price. When the trade scales, I would call trade.AddCustomMetric() again with the scale-in date and price.

Another way to do this would be to save static or dynamic variables as you are now, but instead of naming them something like "E1stDt1", "E1stDt2", etc. give them names that include the symbol and entry datenum so you can more easily retrieve them later. For example, "AAPL_1190705_E1stDt" would be the first entry date (which you already have in the trade list, incidentally) for the AAPL trade that entered on July 5, 2019.

A third alternative would be to abandon the concept of scaling and simply have multiple open trades on the same symbol, which would mean the trade list would show all the prices and dates you're interested in. However, that may introduce other complications that you don't want to deal with.

8 Likes