amibroker

HomeKnowledge Base

Using redundant signals for entries

NOTE: THIS ARTICLE IS NOW OUTDATED AS AMIBROKER SUPPORTS NEW BACKTEST MODE THAT HANDLES THIS NATIVELY http://www.amibroker.com/f?setbacktestmode

The sample code below shows how to use custom portfolio backtester procedure to change the way backtester works. Normally buy is matched against sell and redundant buy signals between initial buy and matching sell are removed as shown in the picture there:
http://www.amibroker.com/gifs/bt_regular.gif

The procedure below changes this behaviour and allows to use redundant signals (they are not removed).

This is done by changing Buy array values from “true” to sigScaleIn (this prevents redundant signals from being removed because scale-in marks are kept untouched) and modifying standard procedure to treat scale-in signals as normal buys (no scaling).

Note that there are many ways to achieve the same effect. The technique presented here was choosen because it is easy-to-use (does not require changes in your core trading system code – all it needs is to plug-in the custom backtest part). Longer implementation would be required if you do not want to (ab)use scaling signals.

One thing worth mentioning is the fact that since scaling-in signals do not store position score this example formula does not support ranking of signals according to user-defined scores.

// YOUR TRADING SYSTEM HERE 

Buy== HHVH10 ); // REPLACE THIS WITH YOUR OWN BUY RULE
Sell == LLVL10 ); // REPLACE THIS WITH YOUR OWN SELL RULE

PositionSize = -20;
SetOption("MaxOpenPositions"); 

// END OF TRADING SYSTEM HERE 

// COMMON CODE PART
// TO BE COPY-PASTED if you want keep redundant signals
// This is long-only version.
// It is easy to extend to handle short trades as well 

Buy IIfBuysigScaleInFalse ); // replace regular buy signals by scale in
// so they do not get filtered 

SetOption("UseCustomBacktestProc"True ); 

if( 
Status("action") == actionPortfolio )
{
   
bo GetBacktesterObject(); 

   
bo.PreProcess(); // Initialize backtester 

   
for(bar=0bar<BarCountbar++) 
   { 
        for ( 
sig=bo.GetFirstSignal(bar); sigsig=bo.GetNextSignal(bar) ) 
        {     
           
// first handle exit signals 
           
if (sig.IsExit() AND sig.Price != -
           { 
            
// Exit Signal 
               
bo.ExitTrade(bar,sig.symbol,sig.Price); 
           } 
        } 


        
// update stats after closing trades 
        
bo.UpdateStats(bar); 
       
        
bContinue True
        for ( 
sig=bo.GetFirstSignal(bar); sig AND bContinue
              
sig=bo.GetNextSignal(bar)) 
        { 
          
         
// enter new trade when scale-in signal is found 
         // and we don't have already open position for given symbol 

          
if (sig.IsScale() AND sig.Price != -AND 
              
IsNullbo.FindOpenPossig.Symbol ) ) ) 
          { 
           
// Entry Signal 
            
if( bo.EnterTrade(barsig.symbolsig.IsLong(), 
                
sig.Price,sig.PosSize) == 
           { 
             
// if certain trade can not be entered due to insufficient funds 
             // or too small value (less than "MinPositionValue") or 
             // to few shares (less than "MinShares" 
             // then do NOT process any further signals 
             
bContinue False
           } 
      } 
    } 

     
bo.UpdateStats(bar,1); // MAE/MFE is updated when timeinbar is set to 1. 
     
bo.UpdateStats(bar,2);   
   } 
   
bo.PostProcess(); // Finalize backtester 
   

Adding custom metric: Average adverse excursion

Here is a sample that shows how to create custom metric based on per-trade statisitics.
In this example we will calculate the average value of MAE (maximum adverse excursion) from all trades.
(more…)