amibroker

HomeKnowledge Base

Handling limit orders in the backtester

In order to simulate limit orders in backtesting it is necessary to check in the code if Low price of the entry bar is below the limit price we want to use. The following example shows an entry signal based on Close price crossing over 100-period simple moving average. The position is opened on the next bar if price drops 1% below the Close of signal bar.

BuySignal CrossCloseMA(Close100 ) );

// buy on the next bar
Buy RefBuySignal, -1);
BuyLimitPrice RefClose, -1) * 0.99;

// now we check if limit was hit
Buy Buy AND BuyLimitPrice;

// if Open price is below the limit, then we use Open for entry
BuyPrice MinOpenBuyLimitPrice )

If we want the order to be valid for more than one bar, then we can use Hold function for this purpose:

BuySignal CrossCloseMA(Close100 ) );

// buy on the next bar
Buy RefBuySignal, -1);
BuyLimitPrice ValueWhen(BuySignalClose) * 0.99;

// now we check if limit was hit
Buy HoldBuy) AND BuyLimitPrice;

// if Open price is below the limit, then we use Open for entry
BuyPrice MinOpenBuyLimitPrice )

In a portfolio-level backtest we usually advocate against using limit orders. Why? Simply because we may not have enough cash in your account to place limit orders for all possible entry candidates. If your trading system generates 100 possible entries, you would need to place 100 limit orders only to find out that eventually only few of them fired. With limited buying power, we may need to place limit orders only for the top N-scored tickers that have generated BuySignal and skip the others. To simulate the situation when we only place small set of limit orders for top ranked stocks we can use new ranking functionalities introduced in AmiBroker 5.70. Knowing the rank at this stage is required if we only want to allow orders for top-scored tickers. Let us say that we prefer symbols with smallest RSI values.

The code would look the following way: Formula first generates a ranking for all tickers included in the test (below example uses Watchlist 0), then when testing individual symbols – checks the pre-calculated rank and generates Buy signal based on that reading.

// we run the code on WatchList 0
List = CategoryGetSymbolscategoryWatchlist);
SetOption("MaxOpenPositions"3);

if ( 
Status("stocknum") == // Generate ranking when we are on the very first symbol
{
     
StaticVarRemove"values*" );

     for ( 
0; ( Symbol StrExtract( List, ) )  != "";  n++    )
     {
         
SetForeign symbol );

         
// value used for scoring
         
values 100 RSI();
         
RestorePriceArrays();
         
StaticVarSet (  "values"  +  symbolvalues );
         
_TRACEsymbol );
     }

     
StaticVarGenerateRanks"rank""values"01224 );
}

symbol Name();
values StaticVarGet "values" +  symbol );
rank StaticVarGet "rankvalues" +  symbol );

PositionScore values;

BuySignal CrossCloseMA(Close100 ) );

// buy on the next bar
Buy RefBuySignal, -1);
BuyLimitPrice RefClose, -1) * 0.999;

// now we check if limit was hit for the symbols ranked as top 3
Buy Buy AND BuyLimitPrice AND rank <= 3;
BuyPrice MinOpenBuyLimitPrice );

// sample exit rules - 5 - bar stop
Sell 0;
ApplyStopstopTypeNBarstopModeBars51)

Detailed description of the ranking functionality used above is available in the manual at: http://www.amibroker.com/guide/h_ranking.html

Comments are closed.