Signal not appearing in CBT

Below is the screenshot of the detailed log. There are duplicate signals.

Better yet you upload APX, from the first post alot of context is missing.

What is the Qty, SetPositionSize() value in this case?
Detailed log clearly says: above the bar vol limit ie. the 10% bar volume, and subsequent orders are being sliced to get order fill.

In a thinly traded symbol, AB will logically BT.
It would be unlikely to get all the volume traded to your 1 trade everytime.

1 Like

Ok I am able to reproduce the problem with below code. This code is runnable.

_TRACE( "!CLEAR!" );   
SetBarsRequired(sbrAll, sbrAll);
SetBacktestMode(backtestRegularRaw);   

SetCustomBacktestProc("");

function applyCustomBacktesterRules()
{

    bo = GetBacktesterObject(); // Get backtester object
    bo.PreProcess();
    dt = DateTime();

    for( i = 0; i < BarCount; i++ )
    {
     
		_TRACE("bar="+i+", bo.GetSignalQty(i, 0)="+bo.GetSignalQty(i, 0));
		for( sig = bo.GetFirstSignal( i ); sig; sig = bo.GetNextSignal( i ) ) 
		_TRACE("sig.Symbol= '"+sig.Symbol+"' "+sig.Type);      

        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
    }

    bo.PostProcess();
}

Short = BarIndex()==1;
Short = IIf(BarIndex()==2, sigScaleIn, Short);
Cover = BarIndex()==3;


if (Status("action") == actionPortfolio)
applyCustomBacktesterRules();

 Filter = 1;   
   
    if( Status( "actionex" ) != actionExAAParameters && Status( "actionex" ) == actionExplore )   
    {   
        AddColumn( BarIndex(), "BarIndex()" );   
        AddColumn( Short, "Short" );   
        AddColumn( cover, "cover" );  
    }   

backtest it on one stock.

  1. Why is bo.GetSignalQty(i, 0) for bar=2 equals 3?
  2. Two signals are traced one is short other is scalein for bar=2. Why is there a short signal here?
  3. Where is the 3rd signal (becuase bo.GetSignalQty(i, 0) is 3)?

Ok, I think i found the culprit.
SetOption( "AllowSameBarExit", True); was causing all this. Once I set it to false the missing trade came back on. I think the moral of the story for me is I am doing low level CBT then best to handle 'same bar entry exit' scenario explicitly and not through setoption.

that is why you share APX, then all settings are known. Otherwise, people are just left guessing.
some settings may not be in code but in the AA UI settings.

The extra signals reported bo.getsignalqty() in backtesting mode backtestRegularRaw still remains unexplained. I think amibroker adds these extra signals by itself.

  1. You don't listen to what is being told. I told you many times to use DETAILED LOG.
    And you just ignore advice and send TRACE output that you wrote yourself that is missing huge number of information that is BUILT-IN when you use DETAILED LOG.

  2. Also saying "code is too long to post here" is nonsense as forum accepts any length of code (just use code tags). You might also upload .APX file that @nsm51 wrote you.

  3. AmiBroker doesn't add any signals by itself. Such utter nonsense sent by some on this forum is reason why I lose my motivation to work on AmiBroker at all. What's the point in spending thousands of hours perfecting the software when people like you spread such nonsense about it. No, AmiBroker does not do anything such utterly stupid as "adding signals by itself"

It is really waste of other people time if you just ignore what is being told.

2 Likes

@Tomasz

  1. No detailed log is generated for this afl backtest. because no trades are generated.
  2. I have uploaded .apx file here Download TestMissingSignal.apx | LimeWire
    because forum does allow me to upload files.
  3. There is extra signal in trace log. A short signal (which i have not added) appears in the same bar as the scaleIn signal. check log window for bar no.3 and 12.
    The code with more helpful trace is as below. Also screenshot is attached.
  4. Also bo.GetSignalQty(i, 0) returns 3 barNo.2. Why?
  5. The below code is runnable. Run backtest with 1 stock.
_TRACE( "!CLEAR!" );   
SetBarsRequired(sbrAll, sbrAll);
SetBacktestMode(backtestRegularRaw);   

SetCustomBacktestProc("");
Short=cover=0;
function applyCustomBacktesterRules()
{

    bo = GetBacktesterObject(); // Get backtester object
    bo.PreProcess();
    dt = DateTime();

    for( i = 0; i < BarCount; i++ )
    {
     
		_TRACE("BarIndex="+i+", bo.GetSignalQty(i, 0)="+bo.GetSignalQty(i, 0));
		for( sig = bo.GetFirstSignal( i ); sig; sig = bo.GetNextSignal( i ) ) 
		_TRACE("sig.Symbol= '"+sig.Symbol+"' sig.Type = '"+sig.Type+"'");      

        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
    }

    bo.PostProcess();
}

Short = IIf(BarIndex()==1, 1, Short);
Short = IIf(BarIndex()==2, sigScaleIn, Short);
Cover = IIf(BarIndex()==3, 1, Cover);

Short = IIf(BarIndex()==11, 1, Short);
Short = IIf(BarIndex()==12, sigScaleIn, Short);
Cover = IIf(BarIndex()==13, 1, Cover);

if (Status("action") == actionPortfolio)
applyCustomBacktesterRules();

 Filter = 1;   
   
    if( Status( "actionex" ) != actionExAAParameters && Status( "actionex" ) == actionExplore )   
    {   
        AddColumn( BarIndex(), "BarIndex()" );   
        AddColumn( Short, "Short" );   
        AddColumn( cover, "cover" );  
    }   

Typing mistake in pt.3. it should read

There is extra signal in trace log. A short signal (which i have not added) appears in the same bar as the scaleIn signal. check log window for bar no.2 and 12.

Detailed log is generated EVEN if there are no trades. That is the crux. You did not even try it.

Your custom backtest code is simply wrong.

REMOVE ALL YOUR CODE and write this:

Buy=Sell=0;
Short=cover=0;
Short = IIf(BarIndex()==1, 1, Short);
Short = IIf(BarIndex()==2, sigScaleIn, Short);
Cover = IIf(BarIndex()==3, 1, Cover);

Short = IIf(BarIndex()==11, 1, Short);
Short = IIf(BarIndex()==12, sigScaleIn, Short);
Cover = IIf(BarIndex()==13, 1, Cover);

and run backtest with DETAILED LOG turned ON.

Then you will see your TRUE SIGNALS, not some output that you have generated artificially.

If you are writing custom backtest, YOU MUST HAVE bo.ProcessTradeSignals(i); calls for every bar. It MUST be included so detailed log can display signals.

1 Like

I checked what is happening and I have the answer for your "why" question.

  1. There are NO EXTRA SIGNALS added by AmiBroker. Even your log is showing TWO signals when you iterate using GetFirstSignal/GetNextSignal.
  2. But bo.GetSignalQty() returns bigger number if those signals are SCALE In/Out. Why? It is because of the fact that it returns the number of SIGNAL SLOTS, not signals, the scaling in/out signal is occupying two entries in the signal array because it must hold more information.

AmiBroker goes into extreme when trying to conserve memory. Since people are running backtests with tens of millions of bars on thousands of symbols there can be say 1e6 * 1e4 = 1e10 signals (that is 1 and 10 zeros = 10 billion). If AmiBroker did not conserve memory in extreme ways, you won't be able to backtest large setups. The exit signal has to store information about long/short, reason of exit, sequential number of even in a bar and symbol ID. Normamlly you would need 4 numbers to store that (16 bytes) and your memory consumption in above case would be 160GB. But AmiBroker uses super compact bit field structure of only 2 bytes, so memory consumption in such case would be 20GB only. 8 times less. The problem is that this structure is insufficient for SCALE In/Out as they need to hold twice as much information. For this reason Scale In/Out signal takes TWO SLOTS in the array.
IT IS REPORTED AND ACTED UPON AS SINGLE SIGNAL when using GetFirstSignal/GetNextSignal and by all internal code. It is just GetSignalQty that reports it as 2, because I did not want to waste another 4 bytes just to keep track of something that is not really used by anybody (you should be using GetFirstSignal/GetNextSignal to iterate thru trades, there is NO way to access by index therefore GetSignalQty is NOT the way to iterate other than correct GetFirstSignal/GetNextSignal). I don't even know if I am going to "fix" that as I am not willing to sacrifice those 4 bytes (potentially multiplied by 10 billion), just to make GetSignalQty reporting scale in/out as single signal.
For now I adjusted the documentation.

The point I was making before was IF YOU USED DETAILED LOG FROM THE START, you would see CORRECT SIGNALS in first place.

1 Like

I have used GetFirstSignal() GetNextSignal() to iterate. and this loop runs twice for bar no.2. That means there are 2 signals on this bar (forget getSignalQty() which reports 3 signals). I put in the scaleInSignal but it also shows a short signal on this bar which i did not add. Why is there a short signal on bar no.2?

And like i said NO DETAILED LOG is generated when i run the code. I don't use bo.ProcessTradeSignals(i); on purpose. I handle the trades through code i.e low level CBT.

for( sig = bo.GetFirstSignal( i ); sig; sig = bo.GetNextSignal( i ) ) 
		_TRACE("sig.Symbol= '"+sig.Symbol+"' sig.Type = '"+sig.Type+"'"); 

And that is wrong.
Let me once again quote what I wrote before:

If you are writing custom backtest, YOU MUST HAVE bo.ProcessTradeSignals(i); calls for every bar. It MUST be included so detailed log can display signals.

Ignoring instructions is NOT the way to ask for support.

You should FOLLOW INSTRUCTIONS and USE DETAILED LOG TO DISPLAY SIGNALS.

2 Likes

Why is it a problem if there is both a Short signal and a Scale-In signal on the same bar? If you are not in a trade, then you will process the Short signal and ignore the Scale-In. If you are already in a trade, then you will ignore the Short signal and process the Scale-In.

If you could kindly add your blockquote about bo.ProcessTradeSignals(i); to the CBT Doc.

From the docs, it would appear that it is for Mid-level CBT, although in this thread you have categorically specified its proper use.
It would be difficult to gather such info from there.

Following on the quote above from @nsm51 , could you please confirm if bo.ProcessTradeSignals(i); is always required or only in mid level approach like it is in the user guide: Porfolio Backtester Interface Reference

The porfolio backtester interface supports various approaches to customization of backtest process that suit different applications.

  • high-level approach (the easiest)- using Backtest() method and it runs default backtest procedure (as in old versions) - allows simple implementation of custom metrics
  • mid-level approach - using PreProcess()/ProcessTradeSignal()/PostProcess() methods - allows to modify signals, query open positions (good for advanced position sizing)
  • low-level approach (the most complex) - using PreProcess()/EnterTrade()/ExitTrade()/ScaleTrade()/UpdateStats()/HandleStops()/PostProcess() methods - provides full control over entire backtest process for hard-code programmers only

thanks

It is required for DETAILED LOG to work. Detailed log is crucial tool for beginner to understand what he/she is doing. Detailed LOG is outputted inside ProcessTradeSignals. It also calls HandleStops and does other things in addition to just processing the signals, therefore it is BAD idea not to call it, even though technically you don't need to call it if you accept the consequences. Consequences are that you are on your own without any great help that comes from having DETAILED LOG and STOPS handled automatically. The trouble is that AmiBroker gives the power in hands but that power means responsibility for everything. You can't use low level, skip all recommendations and then ask for help because you broke things.
It is like C developer writing to random memory location using some random uninitialized pointer and being surprised that program behaves in unpredictable ways. Low level CBT is NOT for beginners.

Docs don't need update. They clearly say that low level CBT is for hardcore programmers. Docs just require reading and making proper judgement when someone isn't really a hardcore programmer. Hardcore programmers figure out everything by themselves. I don't go to Microsoft asking questions. I solve problems myself.

4 Likes

I have added bo.ProcessTradeSignals(i); and now getting the detailed log. I wonder if it is right approach though. I am saying that because on adding the above signals will be processed twice. Once by AB and then by the low-level-cbt code that handles entry/exit.
BTW I am still getting 2 signals (Short and scaleIn) on barNo.2 and barNo. 12. can you tell us why there is a short signal where only scaleIn signal must exist? It is highly confusing for low level cbt programmer if you have an addition signal. The runnable code is as below.

_TRACE( "!CLEAR!" );
SetBarsRequired( sbrAll, sbrAll );
SetBacktestMode( backtestRegularRaw );

SetCustomBacktestProc( "" );
Short = cover = 0;
function applyCustomBacktesterRules()
{

    bo = GetBacktesterObject(); // Get backtester object
    bo.PreProcess();
    dt = DateTime();

    for( i = 0; i < BarCount; i++ )
    {
        bo.ProcessTradeSignals( i );
        _TRACE( "BarIndex=" + i + ", bo.GetSignalQty(i, 0)=" + bo.GetSignalQty( i, 0 ) );

        for( sig = bo.GetFirstSignal( i ); sig; sig = bo.GetNextSignal( i ) )
            _TRACE( "sig.Symbol= '" + sig.Symbol + "' sig.Type = '" + sig.Type + "'" );

        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
    }

    bo.PostProcess();
}

Short = IIf( BarIndex() == 1, 1, Short );
Short = IIf( BarIndex() == 2, sigScaleIn, Short );
Cover = IIf( BarIndex() == 3, 1, Cover );

Short = IIf( BarIndex() == 11, 1, Short );
Short = IIf( BarIndex() == 12, sigScaleIn, Short );
Cover = IIf( BarIndex() == 13, 1, Cover );

if( Status( "action" ) == actionPortfolio )
    applyCustomBacktesterRules();

Filter = 1;

if( Status( "actionex" ) != actionExAAParameters && Status( "actionex" ) == actionExplore )
{
    AddColumn( BarIndex(), "BarIndex()" );
    AddColumn( Short, "Short" );
    AddColumn( cover, "cover" );
}

1 Like