Entering with Limit Orders and Applying Stops Correctly

Hi,

The below code calculates a signal based o the closing values of multiple securities. If the signal is >0 go long at a limit price and if <0 go short at limit price on the next bar.

First, am I correctly implementing the limit price? If the limit price is not met, no trade will occur, correct?

Second,assuming have the limit logic correct, the code will enter on the bar after the signal is generated with SetTradeDelays( 1,1,1,1). I am a bit confused here because I am using "Buy = Ref( Val, -1) > 0.0;" in the limit logic.

Third, once a trade is entered at a limit price, what are the correct settings for ApplyStops?

Thank you for your help.

Mike

_SECTION_BEGIN("");
SetChartOptions(0,chartShowArrows|chartShowDates);
_N(Title = StrFormat("{{NAME}} – {{INTERVAL}} {{DATE}} Open %g, Hi %g, Lo %g, Close %g (%.1f%%) {{VALUES}}", O, H, L, C, SelectedValue( ROC( C, 1 ) ) ));
SetOption("MaxOpenPositions", 14);
//SetTradeDelays( 0, 0, 0, 0 );
SetTradeDelays( 1,1,1,1);
SetOption( "InitialEquity", 200000);
SetOption("FuturesMode" ,True);
SetOption("MinShares",1);
Leverage=3.00;
SetOption("AccountMargin",1);
SetPositionSize (((100/14)*Leverage),spsPercentofEquity);
SetOption( "AllowPositionShrinking", True );

   
FirstTradeTime=    060000;
SquareOffTime =    150000;
DaySessionEndTime =160000;
NO_TRADE =         170000;
 
LookBack =1; 
function mROC( symbol, mode ) 
{ 
   fO = Foreign( symbol, "O"); 
   fC = Foreign( symbol, "C"); 
   fH = Foreign( symbol, "H");    
   fL = Foreign( symbol, "L"); 
    
   switch ( mode ) 
   { 
      case "CC1":       
         returnvalue = ROC( fC, LookBack ); 
      break; 
       
      case "OC0": 
         returnvalue = (fC- fO)/fO*100; 
      break; 
       
      default: 
         returnvalue = Null;          
      break;    
   } 
    
   return returnvalue; 
} 

n = Name();
AUDCAD_IDEALPRO_CASH_30Val		=mROC("NZDUSD.FXCM","CC1")*-1.48+mROC("EURUSD.FXCM","CC1")*-0.42+mROC("USDJPY.FXCM","CC1")*1.5+mROC("USDCHF.FXCM","CC1")*1.5+mROC("GBPNZD.FXCM","CC1")*0.30;
AUDCHF_IDEALPRO_CASH_30Val		=mROC("EURUSD.FXCM","CC1")*-1.5+mROC("USDCAD.FXCM","CC1")*1.5+mROC("EURNZD.FXCM","CC1")*-0.32+mROC("EURAUD.FXCM","CC1")*-1.48+mROC("EURGBP.FXCM","CC1")*0.27;


Val=IIF(n=="AUDCAD.FXCM",	AUDCAD_IDEALPRO_CASH_30Val,
IIF(n=="AUDCHF.FXCM",	AUDCHF_IDEALPRO_CASH_30Val	,
AUDchf_IDEALPRO_CASH_30Val));

//Long & Short Entrt LIMIT logic
// enter on the next bar if the close Val >0 or short <0 of previous and at limitPrice logic is met
Buy = Ref( Val, -1) > 0.0;
Short = Ref( Val, -1) < 0.0;
LimitMultiplier=10.0;
BuyLimitPrice = Open-(0.0001*LimitMultiplier);
ShortLimitPrice = Open+(0.0001*LimitMultiplier);
Buy = Buy AND L < BuyLimitPrice;
Short = Short AND H > ShortLimitPrice;

//entry/exit pricing
BuyPrice = Min( Open, BuyLimitPrice );
ShortPrice = Max( Open, ShortLimitPrice );
SellPrice=Open;
CoverPrice=Open;

//balance of entry/exit logic
Buy= Buy AND TimeNum()>= FirstTradeTime AND TimeNum()<SquareOffTime ;
Sell = Val < 0.0 OR TimeNum()>=DaySessionEndTime;//US hours
Short= Short AND TimeNum()>= FirstTradeTime AND TimeNum()<SquareOffTime ;
Cover=Val > 0.0  OR TimeNum()>=DaySessionEndTime; //US hours


//stops - what are settings if entering on next bar at a limit price?
StopLoss=optimize("SL",0.125,  0.125,0.25,0.0625);//2  0.125;
Target=optimize("Target",0.25,0.25,0.50,0.25);//0.2  0.75;

StopDelay=optimize("StopDelay",10,  0,10,2);//2  0.125;  16
TargetDelay=optimize("TargetDelay",2,0,10,2);//0.2  0.75; 4

SetOption("ActivateStopsImmediately", True) ;
IIf(TimeNum()== No_Trade,(ApplyStop(Type=0,Mode=1,Amount=50, 1,False,ReentryDelay=4,ValidFrom=0)),(ApplyStop(Type=0,Mode=1,Amount=StopLoss, 1,False,ReentryDelay=StopDelay,ValidFrom=0)));
ApplyStop(Type=1,Mode=1,Amount=Target ,1,False,ReentryDelay=TargetDelay,ValidFrom=0) ;//, ExitAtStop=1);


Filter= Buy OR Sell OR Short OR Cover;
filter = close > 0;
AddColumn( Close, "Close" );
AddColumn( Val, "Rule Val" );
AddColumn( IIf( Buy, 'B', 'S' ), "Signal", formatChar );
AddColumn( IIf( Short, 't', 'c' ), "Signal", formatChar );

@reds I suggest you read other posts on this forum regarding limit orders. I believe this one explains in detail the shortcomings of your current approach: PositionScore / Ranking for trades taken “next day at limit”. In particular, you are assuming that you will never have any capital limitations, or phrased another way, that you will always be able to place an unlimited number of limit orders.

Hi mradtke,

Thank you for the post. I trade only 14 securities, allocate 1/14th of capital to each, and allow only one position per security so I should not have any capital limitations. I am trading a version of the code and have never run into any capital limitations but I will be on the watch.

My concern is more to am I implementing the Limit price correctly in checking the High & Low and the correct settings for ApplyStop when implementing a conditional Limit order. Hopefully the post you directed me to will address these issues.

I did search the forum and could not find a posting re stop parameters with limit orders.

I did find a posting re handling limit orders in the backtester and modeled my code on that posting.
[AmiBroker Knowledge Base » Handling limit orders in the backtester]

(AmiBroker Knowledge Base » Handling limit orders in the backtester)

Thank you,

Mike

I don't see anything obviously wrong with your limit code or your third ApplyStop() call, but really you should be reviewing your trade list to see if all the entries and exits are taking place at the expected times using the expected prices.

Calling ApplyStop from inside an IIf() statement (i.e. your first and second ApplyStop calls) is not going to work. What is it that you're trying to achieve?

Hi Matt,

Thank you for taking the time to look at the code.

With AppyStop IIF() statement I am trying to set a very wide stop on the 5PM EST bar so that i do not get stopped out on that bar. I recheck a few minutes later but the 5PM bar can have some wide spreads and bad ticks.

Again, thank you for your sharing your expertise!

Best,

Mike

In that case, try putting the IIf() inside the ApplyStop() call instead of the other way around:

ApplyStop(stopTypeLoss, stopModePercent, IIf(TimeNum()== No_Trade, 50, StopLoss), 1, True, 4, 0);

Also, AFL does not use named arguments, so there is no need to use a construct like "Mode=1" inside your function calls. For readability, I suggest using the predefined constants as shown in the example above.

Using the above complete code, if I implement a Trailing Stop it looks like this,

amount = 10; // 10% loss (trailing)
ApplyStop( stopTypeTrailing, stopModePercent, amount, True );

If i am trading FX on an hourly basis the results look good at 10 but that seems too wide for FX. Prior to implementing the Trailing Stop, I ahad been testing with a fixed stop-loss of 0.10%. When I tried the 0.10 for the Trailing Stop function the returns are exceptional.

I do not have practical experience with Trailing Stops. With FX on an hourly basis, is 0.10 too tight for Trailing?

Thanks,

Mike

I'm not an FX trader, so can't really comment on whether 0.1% is too tight for the trailing stop. However, a colleague taught me early on that if your backtest results seem Too Good To Be True (TGTBT), then you should start looking for your mistake. :slight_smile:

1 Like

Your colleague is absolutely correct! That is why I am posting this question to try to get some reference.

If I use a Trailing Stop with 0.10 and Volatile=T, and comment out the other stops, results are very good in term sof absolute returns. Batting average improves modestly. Typically, if it a look ahead issue, batting average is much too high, in this case improves from 50% to 57%, which is good and realistic.

if there were no obvious error in the body of the code and I am just adding a canned Trailing Stop, I do not see a new problem introduced. Trade sequence also looks correct.

Do you have an understanding of what Trailing Stop is doing with Volatile=T to replicate a Chandelier stop? What is the formula being used and how ATR is incorporated?

Thanks,

Mike

Since I am entering at Limit, I need to have the Trailing stop activated on the next bar, not same bar as order entry. I am entering correctly at Limit but the Trail does not know the sequence of H-L in the hourly bar so I buy at limit and if it continues lower, the Trail SL thinks I had the opportunity exit higher, which is incorrect.

A colleague once told me all things being equal, go with simplicity over complexity. I'll stick with a tight stoploss.

Thanks for your help1

Mike

You can use the ValidFrom parameter of ApplyStop to specify that your stops should not be enforced on the entry bar.

Thanks Matt but I need to have the stop active from entry.

Going with the straightforward and simple, stop loss.

Best,

Mike

Hi Matt,

It looks like I have an error in my code. The below will correctly calculate the BuyLimitPrice and the ShortLimitPrice . However, in the Buy and Short conditions when it checks the LimitPrice there is an issue. If the BuyLimitPrice <Low, it will enter on the Low of the bar. If the ShortLimitPrice > H, it will enter on the High. In both cases, the trades should not be entered at all. Any idea how I can properly condition the check?
Thanks,

Mike

BuyLimitPrice = Open-(0.0001*LimitMultiplier);
ShortLimitPrice = Open+(0.0001*LimitMultiplier);
Buy = Buy AND L < BuyLimitPrice;
Short = Short AND H > ShortLimitPrice;

Not based on the limited code you have shared here, because if BuyLimitPrice < Low then Buy will be false and there will be no long trade entry. I suggest you use an Exploration to look at all the relevant variables (Buy, BuyPrice, BuyLimitPrice, Low) on the bars where this error occurs. I expect that you will find that Buy is True when it should not be. Then expand your Explanation to look at the variables that affect the value of Buy.

If you're adept with the built-in AmiBroker debugger, you could use that to find your error as well.

1 Like

I added some Exploration code and a model for EURJPY. It is a bit clearer to see with JPY, less decimals.

The Exploration table as the val above 0 but the signal for the 5am bar is a sell (not a buy) because I now see it is checking the Low.

However, it is still entering the trade on the 5AM bar, at the Low of the bar when the buy condition is false. How/why does it enter and why at the Low?

Thanks for your help!

Mike

image

image

You have not set BuyPrice and ShortPrice in upper code.
Either in code or UI settings you have set it to Low price.

If you do as here
http://www.amibroker.com/kb/2014/11/26/handling-limit-orders-in-the-backtester/

// if Open price is below the limit, then we use Open for entry
BuyPrice = Min(Open, BuyLimitPrice);

then it should not enter at Low (except it has same value as BuyPrice).

If you have done so then where is your full code?!
You should make others to play guessing games.


BTW, that one is incorrect!

It is for trading stocks not for futures/forex/cfds
Set to 100 and use margindeposit etc.
Read here
http://www.amibroker.com/guide/h_futbacktest.html

I had the full code posted in the first posting. Re-posting here with the Exploration code.

I am not sure what you mean by "have not set BuyPrice and ShortPrice in upper code.

I have not set anything in UI and the code does not point to Low anywhere.

Appreciate your help here.

Thanks,

Mike

t_SECTION_BEGIN("");
SetChartOptions(0,chartShowArrows|chartShowDates);
_N(Title = StrFormat("{{NAME}} – {{INTERVAL}} {{DATE}} Open %g, Hi %g, Lo %g, Close %g (%.1f%%) {{VALUES}}", O, H, L, C, SelectedValue( ROC( C, 1 ) ) ));
SetOption("MaxOpenPositions", 14);
//SetTradeDelays( 0, 0, 0, 0 );
SetTradeDelays( 1,1,1,1);
SetOption( "InitialEquity", 200000);
SetOption("FuturesMode" ,True);
SetOption("MinShares",1);
Leverage=3.00;
SetOption("AccountMargin",1);
SetPositionSize (((100/14)*Leverage),spsPercentofEquity);
SetOption( "AllowPositionShrinking", True );

   
FirstTradeTime=    040000;
SquareOffTime =    150000;
DaySessionEndTime =160000;
NO_TRADE =         170000;
 
LookBack =1; 
function mROC( symbol, mode ) 
{ 
   fO = Foreign( symbol, "O"); 
   fC = Foreign( symbol, "C"); 
   fH = Foreign( symbol, "H");    
   fL = Foreign( symbol, "L"); 
    
   switch ( mode ) 
   { 
      case "CC1":       
         returnvalue = ROC( fC, LookBack ); 
      break; 
       
      case "OC0": 
         returnvalue = (fC- fO)/fO*100; 
      break; 
       
      default: 
         returnvalue = Null;          
      break;    
   } 
    
   return returnvalue; 
} 

n = Name();
AUDCAD_IDEALPRO_CASH_30Val		=mROC("NZDUSD.FXCM","CC1")*-1.48+mROC("EURUSD.FXCM","CC1")*-0.42+mROC("USDJPY.FXCM","CC1")*1.5+mROC("USDCHF.FXCM","CC1")*1.5+mROC("GBPNZD.FXCM","CC1")*0.30;
AUDCHF_IDEALPRO_CASH_30Val		=mROC("EURUSD.FXCM","CC1")*-1.5+mROC("USDCAD.FXCM","CC1")*1.5+mROC("EURNZD.FXCM","CC1")*-0.32+mROC("EURAUD.FXCM","CC1")*-1.48+mROC("EURGBP.FXCM","CC1")*0.27;
EURJPY_IDEALPRO_CASH_30Val		=mROC("GBPCAD.FXCM","CC1")*0.909391770109398+mROC("AUDCAD.FXCM","CC1")*1.49866666442738+mROC("USDCAD.FXCM","CC1")*1.4915030663403+mROC("CHFJPY.FXCM","CC1")*1.47152770113675+mROC("CADCHF.FXCM","CC1")*-0.924371893465315;

Val=IIF(n=="AUDCAD.FXCM",	AUDCAD_IDEALPRO_CASH_30Val,
IIF(n=="AUDCHF.FXCM",	AUDCHF_IDEALPRO_CASH_30Val	,
IIF(n=="EURJPY.FXCM",	EURJPY_IDEALPRO_CASH_30Val	,
AUDchf_IDEALPRO_CASH_30Val)));

//Long & Short Entrt LIMIT logic
// enter on the next bar if the close Val >0 or short <0 of previous and at limitPrice logic is met

//entry signal
Buy = Ref( Val, -1) > 0.0;
Short = Ref( Val, -1) < 0.0;
LimitMultiplier=1000.0;
BuyLimitPrice = Open-(0.0001*LimitMultiplier);
ShortLimitPrice = Open+(0.0001*LimitMultiplier);
Buy = Buy AND L < BuyLimitPrice;
Short = Short AND H > ShortLimitPrice;

//balance of entry/exit logic
Buy= Buy AND TimeNum()>= FirstTradeTime AND TimeNum()<SquareOffTime ;
Sell = Val < 0.0 OR TimeNum()>=DaySessionEndTime;//US hours
Short= Short AND TimeNum()>= FirstTradeTime AND TimeNum()<SquareOffTime ;
Cover=Val > 0.0  OR TimeNum()>=DaySessionEndTime; //US hours

//entry/exit pricing
BuyPrice = Min( Open, BuyLimitPrice );
ShortPrice = Max( Open, ShortLimitPrice );
SellPrice=Open;
CoverPrice=Open;




//stops - what are settings if entering on next bar at a limit price?
StopLoss=optimize("SL",0.125,  0.125,0.25,0.0625);//2  0.125;
Target=optimize("Target",0.25,0.25,0.50,0.25);//0.2  0.75;

StopDelay=optimize("StopDelay",10,  0,10,2);//2  0.125;  16
TargetDelay=optimize("TargetDelay",2,0,10,2);//0.2  0.75; 4

SetOption("ActivateStopsImmediately", True) ;
//IIf(TimeNum()== No_Trade,(ApplyStop(Type=0,Mode=1,Amount=50, 1,False,ReentryDelay=4,ValidFrom=0)),(ApplyStop(Type=0,Mode=1,Amount=StopLoss, 1,False,ReentryDelay=StopDelay,ValidFrom=0)));
//ApplyStop(Type=1,Mode=1,Amount=Target ,1,False,ReentryDelay=TargetDelay,ValidFrom=0) ;//, ExitAtStop=1);


Filter= Buy OR Sell OR Short OR Cover;
filter = close > 0;
AddColumn( Close, "Close" );
//AddColumn( Val, "Rule Val" );
AddColumn( BuyPrice, "Buy Price" );
AddColumn( ShortPrice, "Short Price" );
AddColumn( Low, "Low Price" );
AddColumn( Val, "Rule Value");
AddColumn( IIf( Buy, 'B', 'S' ), "Rule w Check", formatChar );ype or paste code here

Your code of first post is obsolete, as far as I can see. It has changed. No one knows (knew) the state of update.

You have not posted full code here. Since first post code is obsolete -> No one had known what's you new complete code til having been noticed above.


It is because of SetTradeDelays being greater zero and your trading rules being delayed but your BuyLimitPrice of BuyPrice not being delayed -> L < BuyLimitPrice of Buy rule and H > ShortLimitPrice; of Short rule are delayed. So it checks previous bar. Buy/Shortprice use value of current bar.

Apparently your BuyLimitPrice is supposed to be of current bar. So instead of SetTradeDelays you may use Ref() function for your entry rules. And setting SetTradeDelays to zero.


And again... please correct this one


Another hint to your code
Settings belong at top not all over the place in the code.
Etc.
Add some structure to your code


Correction: You should not make others to play guessing games.
E.g. If you update code then post that updated code.

fxshrat,

You were spot on regarding SetTradeDelays to zero. That fixed it. I still need to clean up the code per your suggestions. I will post code in its entirety once I clean it up.

Thank you for your help!

Mike

In the backtester, if I do not have a code to "hold" a limit order for a certain number of bars, if an order is generated at 10am hourly bar and goes unfilled, is that order cancelled and then a new order entered on the 11am bar?

Thank you for your help!

Mike