November 26, 2014
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 = Cross( Close, MA(Close, 100 ) );
// buy on the next bar
Buy = Ref( BuySignal, -1);
BuyLimitPrice = Ref( Close, -1) * 0.99;
// now we check if limit was hit
Buy = Buy AND L < BuyLimitPrice;
// if Open price is below the limit, then we use Open for entry
BuyPrice = Min( Open, BuyLimitPrice )
If we want the order to be valid for more than one bar, then we can use Hold function for this purpose:
BuySignal = Cross( Close, MA(Close, 100 ) );
// buy on the next bar
Buy = Ref( BuySignal, -1);
BuyLimitPrice = ValueWhen(BuySignal, Close) * 0.99;
// now we check if limit was hit
Buy = Hold( Buy, 3 ) AND L < BuyLimitPrice;
// if Open price is below the limit, then we use Open for entry
BuyPrice = Min( Open, BuyLimitPrice )
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 = CategoryGetSymbols( categoryWatchlist, 0 );
SetOption("MaxOpenPositions", 3);
if ( Status("stocknum") == 0 ) // Generate ranking when we are on the very first symbol
{
StaticVarRemove( "values*" );
for ( n = 0; ( Symbol = StrExtract( List, n ) ) != ""; n++ )
{
SetForeign ( symbol );
// value used for scoring
values = 100 - RSI();
RestorePriceArrays();
StaticVarSet ( "values" + symbol, values );
_TRACE( symbol );
}
StaticVarGenerateRanks( "rank", "values", 0, 1224 );
}
symbol = Name();
values = StaticVarGet ( "values" + symbol );
rank = StaticVarGet ( "rankvalues" + symbol );
PositionScore = values;
BuySignal = Cross( Close, MA(Close, 100 ) );
// buy on the next bar
Buy = Ref( BuySignal, -1);
BuyLimitPrice = Ref( Close, -1) * 0.999;
// now we check if limit was hit for the symbols ranked as top 3
Buy = Buy AND L < BuyLimitPrice AND rank <= 3;
BuyPrice = Min( Open, BuyLimitPrice );
// sample exit rules - 5 - bar stop
Sell = 0;
ApplyStop( stopTypeNBar, stopModeBars, 5, 1)
Detailed description of the ranking functionality used above is available in the manual at: http://www.amibroker.com/guide/h_ranking.html
Filed by Tomasz Janeczko at 1:44 pm under Backtest
Comments Off on Handling limit orders in the backtester