Is it possible to use a Limit Buy Order with EOD data?


#1

The explore report shows good buys at the green arrow points but buying above the calculated limit price at the red arrows. Here is the explore report . . .
< />

And here is the afl coding used . . . .
< />

And here is the trade history . . .
< />

Recommendations ? Thanks, R Buck Gray


#2

Your formula is incorrect.

For details how to write correct code see Knowledge Base
http://www.amibroker.com/kb/2014/11/26/handling-limit-orders-in-the-backtester/


#3

Thanks Tomasz.

Buck


#4

Hi Tomasz,

I have difficulty in coding Larry Connors mean reversion system on stocks on EOD prices which uses limit orders.
//Rules//
Watchlist = FnO(Custom watchlist)
Initial capital = 1000000;
Setup1 = C > 5 and MA(VC,21) > 500000;
Setup2 = C > MA(c,200);
Setup3 = RSI(2) < 10;
buysignal = setup1 and setup2 and setup3;
buy = ref(buysignal,-1);
buylimitprice = ref(c,-1)
.97;
buy = buy and L < buylimitprice and O!=L;
buyprice = min(open,buylimitprice);
sell = c > ma(c,5);
Setoptions(“maxopenpositions”,5);
Setpositionsize(20, SpsPercentofEquity);

I want to rank stocks on the basis of ADX(10) and only send limit orders for 5 top ranked stocks if signals are more than the capital or maximum number of positions.

It would be highly appreciated if you can help me code this…
Thanks
Hardik Upadhyay


#5

When you make a post on this forum, always remember to enter the AFL code properly. Improperly formatted code might result in syntax errors! Tomasz has written about it hundreds of times - and you should respect it! Read here:


StaticVarGenerateRanks() is the function you are after. You can find many examples of applying it. Use the Forum Search option.

http://amibroker.com/guide/afl/staticvargenerateranks.html


#6

Hi,

Thanks for the prompt response. I used the following formula but it still shows the trade which is not in top 5 ranking. I check detailed log report.

// we run the code on WatchList 0
List = CategoryGetSymbols( categoryWatchlist, 1);
SetOption("MaxOpenPositions", 5);
SetPositionSize(20,spsPercentOfEquity);
 
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 = StDev(log(C/Ref(C,-1)),100)*(252^.5)*100;  
         RestorePriceArrays();
         StaticVarSet (  "values"  +  symbol, values );
         _TRACE( symbol );
     }

     StaticVarGenerateRanks( "rank", "values", 0, 1224 ); 
}

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

PositionScore = values;
setup1 = C > 5 AND MA(V,21) > 500000; 
setup2 = C > MA(C,200);
setup3 = RSI(2) < 10; 
BuySignal = setup1 AND setup2 AND setup3;  //Cross( Close, MA(Close, 100 ) );

// buy on the next bar
Buy = Ref( BuySignal, -1);
BuyLimitPrice = Ref( Close, -1) * 0.97;

// now we check if limit was hit for the symbols ranked as top 3
Buy = Buy AND L < BuyLimitPrice AND rank <= 5;
BuyPrice = Min( Open, BuyLimitPrice );

// sample exit rules - 5 - bar stop
Sell = C > MA(C,5);
//ApplyStop( stopTypeNBar, stopModeBars, 5, 1);

Please help me out on this…

Thanks,
Hardik Upadhyay


#7

I can’t find any instances of a Buy signal with a rank greater than 5.

Using the steps in How do I debug my formula? I added the following Explore code:

Filter = Buy;
AddColumn(rank, "rank", 1);
SetSortColumns(-3);

And found the highest rank at Buy was 5, no greater.

image

What it could be is your definition of List. Your comment says you run it against Watchlist 0, but the List definition has watchlist number 1 hard coded. What you could use instead is this code, which picks up the watchlist used in the Analyser Filter.

WLNumber  = GetOption( "FilterIncludeWatchlist" );
List = CategoryGetSymbols( categoryWatchlist, WLNumber );

#8

Hi
For example I ran a following exploration for the date 05/18/2017… on this particular day setups are more than my maximum position limit of 5… how can I ask Amibroker to check whether limit is hit or not for the top 5 stocks ranked in terms of 100 day historical volatility or any other ranking method for example 5 most over sold stocks in terms of RSI(2)…

Ticker Date/Time Buy Sell HV100 RSI2
70002634 5/18/2017 1 0 43.05 8.73
70001414 5/18/2017 1 0 42.56 6.80
70001619 5/18/2017 1 0 40.89 7.72
70002926 5/18/2017 1 0 39.57 9.18
70000400 5/18/2017 1 0 38.90 9.53
70002239 5/18/2017 1 0 37.86 6.25
70000026 5/18/2017 1 0 37.67 6.17
70000501 5/18/2017 1 0 34.95 7.46
70003065 5/18/2017 1 0 34.94 6.04
70000250 5/18/2017 1 0 34.69 0.94
70003392 5/18/2017 1 0 33.95 6.36
70000062 5/18/2017 1 0 33.93 6.41
70002130 5/18/2017 1 0 33.48 3.21
70002709 5/18/2017 1 0 32.36 4.08
70000040 5/18/2017 1 0 32.08 8.13
70002195 5/18/2017 1 0 31.91 8.88
70001800 5/18/2017 1 0 31.63 8.89
70002684 5/18/2017 1 0 30.60 8.34
70002722 5/18/2017 1 0 30.55 4.40
70000207 5/18/2017 1 0 30.10 2.62
70002811 5/18/2017 1 0 30.09 8.55
70000010 5/18/2017 1 0 29.31 7.89
70001994 5/18/2017 1 0 28.91 7.72
70002663 5/18/2017 1 0 28.54 5.69
70000008 5/18/2017 1 0 27.50 2.94
70001675 5/18/2017 1 0 27.47 3.46
70000009 5/18/2017 1 0 26.51 9.76
70001984 5/18/2017 1 0 26.06 3.64
70000260 5/18/2017 1 0 26.05 6.38
70000978 5/18/2017 1 0 25.32 2.22
70002936 5/18/2017 1 0 24.95 4.11
70002202 5/18/2017 1 0 24.92 5.78
70001667 5/18/2017 1 0 23.91 8.87
70000018 5/18/2017 1 0 22.51 9.20
70001190 5/18/2017 1 0 22.39 9.45

Thanks,
Hardik Upadhyay


#9

Hi All,

I have tried using following formula to rank stocks in terms of lowest RSI(2)…

if ( GetOption( "ApplyTo" ) == 2 ) 
{ 
     wlnum = GetOption( "FilterIncludeWatchlist" ); 
     List = CategoryGetSymbols( categoryWatchlist, wlnum ) ; 
} 
else 
if ( GetOption( "ApplyTo" ) == 0 ) 
{ 
     List = CategoryGetSymbols( categoryAll, 0 ); 
} 
else 
{ 
     Error( "The formula works fine if your ApplyTo setting is 'Filter' or 'All' " ); 
} 

HV100 = StDev(log(C/Ref(C,-1)),100)*(252^.5)*100;

if ( Status("stocknum") == 0 ) // GENERATE RANKING WHEN WE ARE ON VERY FIRST SYMBOL 
{ 
    StaticVarRemove( "values*" ); 

    for ( n = 0; ( Symbol = StrExtract( List, n ) )  != "";  n++    ) 
    { 
        SetForeign ( symbol ); 
        values = RSI(2); 
        RestorePriceArrays(); 
        StaticVarSet (  "values"  +  symbol, values ); 
        _TRACE( symbol ); 
    } 

    StaticVarGenerateRanks( "rank", "values", 0, 1224 ); 
    //StaticVarGenerateRanks( "bot", "values", 5, 1224 );
} 

symbol = Name(); 

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

PositionScore = values;
setup1 = C > 5 AND MA(V,21) > 500000; 
setup2 = C > MA(C,200);
setup3 = RSI(2) < 10; 
BuySignal = setup1 AND setup2 AND setup3;  //Cross( Close, MA(Close, 100 ) );

// buy on the next bar
Buy = Ref( BuySignal, -1);
BuyLimitPrice = Ref( Close, -1) * 0.97;

// now we check if limit was hit for the symbols ranked as top 3
Buy = Buy AND L < BuyLimitPrice AND rank <= 5;
BuyPrice = Min( Open, BuyLimitPrice );

// sample exit rules - 5 - bar stop
Sell = C > MA(C,5);
//ApplyStop( stopTypeNBar, stopModeBars, 5, 1);

SetOption("MaxOpenPositions", 5);
SetPositionSize(20,spsPercentOfEquity);

//SellSig = ExRem(Sell,buysignal);
//Filter = buysignal OR SellSig;
//AddColumn(buysignal, "Buy", 1.0, colorWhite,IIf(buysignal, colorGreen, colorDefault) );
//AddColumn(SellSig,"Sell", 1.0, colorWhite,IIf(SellSig, colorRed, colorDefault) );
//AddColumn(HV100,"HV100", 1.2, IIf(buysignal, colorBlack, colorWhite ));
//AddColumn(rank, "rank", 1, IIf(buysignal,colorBlack,colorWhite));
//SetSortColumns( 2, -4, -5, 6);    

AddColumn ( values, "values");           
AddColumn ( rank, "rank");    


Filter = 1; 

SetSortColumns( 2, -4 );


Running the above code for exploration I get correct rankings as below…

Ticker Date/Time values rank
70000530 9/18/2017 0.24 213.00
70003365 9/18/2017 0.31 212.00
70003405 9/18/2017 1.04 211.00
70000011 9/18/2017 1.21 210.00
70003527 9/18/2017 1.73 209.00
70000140 9/18/2017 2.27 208.00
70003460 9/18/2017 2.83 207.00
70002049 9/18/2017 3.45 206.00
70003407 9/18/2017 4.07 205.00
70000835 9/18/2017 7.55 204.00
70001949 9/18/2017 7.72 203.00
70000031 9/18/2017 9.09 202.00
70000184 9/18/2017 9.92 201.00
70002217 9/18/2017 10.47 200.00
70000044 9/18/2017 98.47 10.00
70001800 9/18/2017 98.61 9.00
70002212 9/18/2017 98.64 8.00
70003195 9/18/2017 98.77 7.00
70001973 9/18/2017 99.07 6.00
70003403 9/18/2017 99.08 5.00
70001671 9/18/2017 99.57 4.00
70001588 9/18/2017 99.75 3.00
70001598 9/18/2017 99.77 2.00
70000055 9/18/2017 99.83 1.00

Now How do I run back test ? Is following correct ?

Buy = Buy AND L < BuyLimitPrice AND rank <= 5;
//Or shoul I use ?
Buy = Buy AND L < BuyLimitPrice AND rank >= 209;

I have run the back test the both ways but its not giving me correct trades…

Can any one please help me out on this ? Also how do I use 100 day Historical volatility for ranking instead of RSI(2)

Thanks,
Hardik Upadhyay.


#10

If you’re going to use this solution, then you have to consider whether each symbol has met the Setup conditions before you call StaticVarGenerateRanks(). Right now, you are ranking ALL symbols in your watchlist, so the top five (ranked by RSI(2)) on any given day may not actually be Setups.

Personally, I find it easier to handle all this in the CBT, and when I worked for Larry Connors that’s the way we built all our limit order strategies. However that’s a very different road than the one you’re going down right now and either solution should work if coded properly.


#11

StaticVarGenerateRanks ranks highest to lowest, meaning the highest input value is ranked #1, the next highest #2 etc. So, since you want the lowest value of RSI to be ranked highest, you can either invert it, or deduct it from 100, to make the lowest the highest, as it were.

values = 1 / RSI(2); // use the inverse OR
values = 100 - RSI(2); // deducted from 100

Note deducting from 100 works with RSI as it’s bounded at max 100. But for unbounded indicators where you want to prioritise the smallest, you’re better to use the inversion approach.

And as @mradtke says, you’re far better processing limit orders in the CBT, since there you can limit the number of signals processed. Consider the example where you have MaxOpenPositions of 5, and 3 trades open. In real life you’d only send 2 orders to the broker and see which are executed. With rank <= 5 you’re always simulating sending up to 5 orders, and the backtester will open trades on any that meet the entry criteria, up to a maximum of MaxOpenPositions. In effect the backtester has the benefit of processing all the signals to see which of them met their limit price and taking trades in any that do, and “cancelling” any it doesn’t need. In real life you can’t know which orders will hit their limit price, and if you’re not there at the time to cancel any over your max holdings, you’d run the risk of being over exposed in that situation. In backtesting terms it’s a type of future leak, and it’s one that may over estimate your results.


#12

Hi,

Thanks for the reply. I tried using HV 100 instead of RSI(2) to rank I get following results…

HV100 = StDev(log(C/Ref(C,-1)),100)*(252^.5)*100;
values = HV100;

Ticker Date/Time values rank
70000002 9/19/2017 21.28 1.00
70000004 9/19/2017 21.28 1.00
70000005 9/19/2017 21.28 1.00
70000006 9/19/2017 21.28 1.00
70000008 9/19/2017 21.28 1.00
70000009 9/19/2017 21.28 1.00
70000010 9/19/2017 21.28 1.00
70000011 9/19/2017 21.28 1.00
70000013 9/19/2017 21.28 1.00
70000017 9/19/2017 21.28 1.00
70000018 9/19/2017 21.28 1.00
70000019 9/19/2017 21.28 1.00

It assigns same HV100 values to all the symbols…what am I missing ?

Thanks,
Hardik Upadhyay.


#13

You’d need to post your full formula for anyone to be able to troubleshoot it. What you’ve posted woks fine for me.

HV100 = StDev(log(C/Ref(C,-1)),100)*(252^.5)*100;
values = HV100;

Filter = 1;
AddColumn(values, "Values");

image

PS It’s worthwhile using a screen capture tool such as Snipping Tool or Pickpic to capture screenshots for posting, such as for Explore listings.


#14

I think you have to be very careful when using limit orders with end of day stock data. You can get some very good results backtesting with limit orders than do not transpire in real trading.

US markets are very fragmented, there are over 60 market centers. This means the exact price at which your order is filled depends on the exchange your order gets routed to. You cannot simulate this properly without very granular data.

If you have read Flash Boys by Michael Lewis, you will realize the shenanigans that goes on in intraday trading. I would be very conservative and use hefty slippage amount. Or go down to 1-minute bars for more realistic price fills.

A good article is here from Ernie Chan > http://epchan.blogspot.co.nz/2015/04/beware-of-low-frequency-data.html


#15

I agree with @marwood. The safest bet with EOD data is to use market on open or market on close when it comes to reliability.


#16

Hi,

Thanks for the information. Appreciate it.


#17

Thanks a lot Tomasz.