EA design: Trailing Stop and Take Profit

 

I have been using several different trading styles based on the recommendations I have seen for using Trailing Stops. This discussion is intended to be strategy validation and programming based on the strategies. The discussion is focused on limitations and functions in MetaTrader 4.

There are a few philosophies when people explain Trailing Stops. The first main concept is "locking in profits". The idea is that as your position becomes more and more profitable, you want to keep those profits in case the market reverses. A trailing stop of 15 would keep your profits - minus 15 pips.

The second philosophy is "don't leave profits on the table". If you enter a trade and it is winning that is great! but we often kick ourselves to enter a trade, take our profit at 30 pips and watch the charts keep zooming along. Sometimes we take 30 pips when we could have had 100+ by using trailing stops.

A third philosophy does not use the trailing stop signal/menu in MetaTrader. In Hendrick's code for Phoenix 5.3 mode 3 trading, he slowly moves the trailing stop up to the open price, ensuring that trades moving in the right direction don't turn into losses. I haven't seen this in discussions anymore, so I will call it, "If I'm winning, don't loose."

Trailing stops have 3 main weaknesses I see.

#1 The client is very limited on the types of executions it will do for you. Minimum 15pip, must be profitable, conflicts with set TP levels (trade exits at TP), ...

#2 Trailing stops in the client only work on profitable trades, excluding "If I'm winning, don't loose" or the option of "loosing less" as time goes on strategy.

#3 Small variations in the market can lead to early exit. By default, a TS of 15pip means that at 15 pip profit, the SL kicks in at your entry price, and slowly climbs up pip by pip as the profits increase. If you know a trade is good for 50 pips (or your strategy requires it), a TS15 may exit at 0pip profit, or 12 pip profit and then continue up to your intended TP level.

#4 You are limited to a minimum of 15 pips under peak value, there are no tighter trailing stop options available in the client. Brokers I have tested will allow SL as close as 5 pip (IBFX and MIG) to market price. (NorthFinance is 10 pip from market.)

Code proposals:

Mode1: The first and most obvious code is trying to overcome issue #4, the 15 pip minimum. If a broker supports 5 pip under market SL, then we should be able to modify open trades to use 5 pip TS. Smaller values are more susceptible to issue #3.

Mode2: The second solution is to prevent issue #3 from happening, by letting TS kick in at a specific price. A StartTS value of 30pip with a TS of 5pip would guarantee a 25pip profit if the 30pip mark is hit. This would still be enough room for straight line runs in news events, etc. Entering a news event with a SL10, TP50 is probably more risky than SL10, StartTS15 TS5. This would also be useful many other situations and will likely result in more winning trades, while keeping most all or more of the profit that a static TP level offered.

Mode3: The third code proposal is based on what Hendrick already accomplished - preventing losses once the signal moves your way. This may also be additionally useful when trading based on Support/Resistance styles, moving SL to support resist after a quick pivot. The timing should be based on a minimum 'safe zone' to ensure price will keep going beyond support/resist instead of hovering near it, and possibly an optional maximum 'no fly zone' where you don't want SL to be configured.

These should all be Expert Agents or a script tied to a hot key for button mashing manual trading. For example, different settings could be loaded into different hotkeys, like CTRL-ALT-X "Mode2: erase TP if exists and set SL(price-5pip) now" or CTRL-ALT-Z "mode3: obey safety settings, set SL" vs CTRL-SHIFT-Z "mode3: ignore safety settings, set SL". One time saver would probably be, "mode3: set SL=BuyPrice" for the "If I'm winning, don't loose" philosophy.

An EA without a magic number could automatically modify all trades for the currency that the EA is active for. This would allow similar functionality to the client side Trailing Stops, but automatically set for all trades in the currency. This may also be a helper EA, by loading 2 EA in 2 charts of the same currency. Similar results to manual trading assistance, but running alongside any EA and modifying the other EA's trades.

Any code posted here (by author) is assumed to be copyright free and available for all to use unless explicitly stated otherwise. (Keyphrase: "I retain copyright") If you post code for reference and are not the author, please tell us that you are not the author. (Keyphrase: "I am not the author") Alternatively, you can retain copyright but grant a specific license. If you do choose a license, please pick one of the standard OSI approved licenses (GPL, QPL, Mozilla, etc). A list is available at http://www.opensource.org/licenses/

 

To keep any code somewhat sane, lets use integers to store pip values in, and then scale all values by 'Point' to be combined with that price as a double.

For example (CheckTrailingStop copyright Hendrick, setTrailingStop a function wrapper around docs.mql.com example):

extern int TrailingStop=15; //default value, broker specific. could go as lower (5 pip confirmed so far)

void CheckTrailingStop()

{

for(int i=0;i<OrdersTotal();i++)

{

if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) break;

if(OrderSymbol()!=Symbol()) continue;

if(OrderType() == OP_BUY)

{

if(((Bid - OrderOpenPrice()) > (Point * TrailingStop)) && (OrderStopLoss() < (Bid - Point * TrailingStop)))

OrderModify(

OrderTicket(),

OrderOpenPrice(),

Bid - Point * TrailingStop,

OrderTakeProfit(),

0,

GreenYellow);

}

if(OrderType() == OP_SELL)

{

if(((OrderOpenPrice() - Ask) > (Point * TrailingStop)) && (OrderStopLoss() > (Ask + Point * TrailingStop)))

OrderModify(

OrderTicket(),

OrderOpenPrice(),

Ask + Point * TrailingStop,

OrderTakeProfit(),

0,

Red);

}

}

}

int setTrailingStop(){

if(TrailingStop>0)

{

OrderSelect(12345,SELECT_BY_TICKET);

if(Bid-OrderOpenPrice()>Point*TrailingStop)

{

if(OrderStopLoss()<Bid-Point*TrailingStop)

{

OrderModify(OrderTicket(),OrderOpenPrice(),Bid-Point*TrailingStop,OrderTakeProfit(),0,Blue);

return(0);

}

}

}

}

It would be nice if we could look up the minimum StopLoss level from Price, but I couldn't find it on docs.mql.com in StandardConstants, Predefined Variables, or AccountInformation.

 

To simplify terminology, I'm going to assume all trades are BUY orders.

Why have TakeProfit levels? We may be limiting our profit by getting out when staying in is good. With a trailing stop of 5-10pip after we hit our profit target, we might be able to stay in for another 5-15 pip. This isn't a traditional trailing stop, so a very close trailing stop matters.

Not having the order data storing our profit target makes data management harder, maybe if TP-Price <TS we delete the TP value and then we set SL based on TS. Every time we loop through the orders, if TP=0, run the TS code.

Each broker has their own limitations on entering SL near current prices (-5 and -10 pip common so far). In case the broker doesn't support the tight SL we want (<=10pip seems like the upper limit), we might set a "trailing close" of 5 pips since we are processed on the tick anyway. It isn't as good as the broker side SL, but we can emulate the behavior client side.

Maybe store a "max price since TSLevel active" variable? Sounds like a lot of data management, calculating it on the fly would likely be better if possible, but again we need to know what the profit target was - or when the TP was deleted. A possibly quiet bug would be the EA reaching back before the trade started, and selecting a max value. Actually ... our max value = max price since trade was opened. Might be redundant for a series of trades opened at the same time, but I can't think of a great way to do this since the timestamps may differ slightly. We could reduce processing by increasing memory usage through an external buffer - but it probably isn't worth it. It seems like "Trailing Close" is a viable method for breaking the 10 pip limit imposed by some brokers. If Live testing reveals 10pip is the best value anyway, then we don't have to worry about it. (I wish I could backtest tick data!!!!)

Another idea is detect the error code given to EA by the broker when the SL is too close to current price. Interactively it just greys out the button - I know this variable is somewhere but they just don't document it. Start off with SL=Price-(1*point) and then loop through until no error. Once that value is found, we should save it so we don't flood brokers with error codes. That would be the "tightestSL" level the broker supports. IF tightestSL>10 put in whatever SL we can and look for 5-10 pip manual close.

 

Courtesy of Bertbin in https://www.mql5.com/en/forum/175245

"bertbin":
in mode 3, you have 3 trade opened at the same time

and sometimes first TP is reached and price reverse....

A sort of trailing stop :

If first TP reached, SL of 2nd and 3 trade will be moved to the first TP

if second tp reached SL of third trade will be moved to second TP

I think this a SL strategy that wasn't listed before. In Phoenix some of this data is readily available, based on the Magic Quotes numbers. As a generic system, here is what we would need to do:

A. Group trades by some criteria (likely timestamp within tolerance value & same currency)

B. Prepare the list of Grouped Trades.

C. Store TP of Trade1 level for trades 2 and 3, Trade2 for 3, etc. (Writing this code for N trades would be an array of N elements)

D. Option if TP replaced by TS: increment SL of Trades 2 and 3 as TS of Trade1 increases. They can all be the same value, or lagging to allow for more fluctuations while still increasing the "locked in" profit (don't want short term trades always making long term trades SL out). With lagging increases in SL, a Short, Medium, Long strategy would be viable. (lagging regressive increasing SL?)

Programming ideas

A. Looks easy enough if we can process timestamps. Maybe truncate the timestamps to date:hour:min, get a range of minutes as a configured value, and then look for timelow<timestamp<timelow+min. Another option is to use unix time, and just process the same range without truncating.

B. The list of trades should be grouped once per loop through the order list. If we cache it for later the trades might be closed without us knowing. We can't process the list while we loop through it. So... we make an array out of each Group containing the ticket numbers. Groups=array(ticket1, ticket2, ticket3), array(ticket1, ticket2), etc For this code, we can filter out any tickets that would be in groups by themselves.

C. This array would match the structure of the Groups array, but would contain doubles for TP instead of int for ticket numbers. If a trade is closed, it would need to go somewhere else. It would be best if we could discard the data completely on close. (set it as SL for the group members and exit would work)

D. If we want to keep the regressive stoploss data in sync based on the original ticket structure, then we would need to keep the position in the group recorded somewhere. If the level of regression is reset every time a trade is closed, the last trades will catch up faster and faster. Actual programming on the regression may look like: Trade1...TradeN and the Trade(x) SL=(x*2). That would be easy to code if there was multidimensional arrays with name based keys. Annoying but doable if there was variable variables. MQ4 has neither. I guess labeling the Trades "Trade01" "Trade02" and then chopping off the last 2 characters, converting them to an int, and storing that as x. If we don't need to preserve the rate of SL change, then we can just read the current position in the current group.

Option D sounds hard, and I'm not confident it would be worthwile to keep them updated. More profit retained if the entire trade heads down, but without some sort of regressionally calculated SL on the group they would all close at the same time - effectively being a single trade.

 

Instant trailing stop

Hi Guys

I was wondering whether anyone might be able to kindly help me. I am finishing off a complex EA but cant seem to get around the trailing stop logic. In the past I have used parabolic SAR and this has worked fantastic for those systems but this one requires a hi response immediate follow trail. If the entry is 1.8000, the moment it moves 1 point forward it needs to pull up the trailing. no matter how many times this occurs within the 1 minute bar. This has been my problem, I cant seem to get it to operate more than once within the one minute bar and even then it never picks the high (if the position is a BUY).

Anybody able to help me out? Send me a direct reply to Inbox if you have a clever notion.

Thanks very much

FxNorth

 

Call RefreshRates() during your EA loop. If you stick it a few places, you will probably see more frequent updates.

 

If we can read tick data well, I'm going to try and do a Manual Close filter. Rather than close like a SL on a single tick of data, the MCFilter would wait for a configurable number of trades beyond a level before closing the trade. A good value may be 2-5 ticks. The reason for this is that brokers often intervene by sending a sharp spike, triggering SL but not TP (or they use slippage to readjust TP back to their target). Broker sees it as free money, and may do things like this to keep the number of BUY and SELL trades fairly even.

The Manual Close method may protect trades, but a filter would protect them even more. This would require the fastest and most reliable connection, and very quick processing of each tick.

The extra processing is a reason to have a small optimized SL/TS expert only.

 

Well my tests working with Tick Data further confirmed the necessity of a separate "exit" EA. If the EA takes 5 seconds to process a loop, then it ignores tick data until it wakes up again to look at open trades. In programming terms, tick data is an event trigger not an interrupt.

To avoid EA lag, especially on trailing stops, it is critical to have a fast EA. I see wasteful execution in many EA, but they don't pay the price even on slow computers. If they are trying to track tick data, it is important that the EA is "always available".

For example, if there is a currency with 5 ticks coming in, slightly staggered at the following times: 1 sec, 1.5 sec, 4 sec, 5 sec, 9 sec and the execution of the EA is slightly over 1 second, the EA would only have 3 trades in its tick data: 1 sec 4 sec 9 sec. This is not a serious problem for 1 open trade, because the loops are smaller. Processing 10 or 20 trades can become a serious execution problem.

Watching the level 2 data showed me that there is a lot of money traded in steady curves, halts, and then a major price jump. This is how the price often jumps by 2 or 3 pips in a second. In the example above, the 5 sec to 9 sec timeframe would be one of those stalls. If we filter our manual closes by tick data to remove spikes, the first 4 trades *must* all be present to properly account for the 5th trade movement that represents a drastic change.

Anyway, nothing conclusive but these experiments affirm to me that exit trades should be handled by a separate, smaller, tighter, faster EA.

 

Interesting thoughts that echo some of my own...

This is an excellent discussion to have - thank you for starting it!

First off - i think you were looking for what the minimum TS value is. If I understand what you are looking for, you can get it with:

MarketInfo(Symbol(),MODE_STOPLEVEL);

Secondly, this may be a moot point with EA separation, but you were discussing saving values verses dynamic calculation. While dynamic calc is always easier, it is very wasteful in terms of processing time. Yet, array access is very quick - so I think the best thing would be a 2 dimensional array of trades:

MyTrades[][n]; Where 2nd dim is {ticket, position, SL price, TS price, etc..}

The EA then simply has to compare values at close time, rather than computing them - these can be computed immediately after the trade, because it is unlikely we'll need to close then, so we have some "ticks" to spare so to speak. It could save a bundle of processing time.

You mentioned a 5 second EA cycle - was that serious, or were you just using an example? That seems huge to me.

I love the idea of the TS replacing a traditional TP - this is what I prefer to use myself. The value as you mentioned, is that it eeks out a few more pips in most situations. The interesting discussion comes in when it is set?

If you normally have a TP of say 20 do you set a 5 pip TS when you hit 20 (which of course has the potential to loose the 5 pips) or do you set the 5 pip TS at 25 so that your true potential profit is 20, but you are risking that profit for 5 extra pips.

This is where tick data optimization is a must, and it should be frequently updated for the most recent periods (ala Phoenix I believe).

Mauro's e.trailing has the ability to set these already (the levels, not the testing! ) in the BreakEven.Lock.Pips and the Start.Trail.After.BreakEven. This is primarily used in your #4 scenario (if I'm winning, don't loose), but I use it more for the SL replacement for TP.

If I want a 20pip SL, I'll set Start.Trail.After.BreakEven to true, BreakEven.Lock.Pips to 20 and the SL value of 5. This means that once I'm 25 pips in profit, my SL is moved to the 20 pip profit mark, and will now trail 5 pips behind. It works very nicely. You can always split the difference too, meaning lock pips of 22 or 23 - that means you need to be 27 pips in profit to set the 5 SL, but it also ensures more actual TP (17 compared to the 15). Obviously, these are just broad numbers to work with.

Mauro originally designed e.trailing as a separate EA, but I changed it into an include file for my purposes. You are making me doubt that choice.

Again, this is an excellent discussion, and I'm pleased that you've chosen to ruminate about it openly.

Very best regards,

CS

 
cubesteak:
This is an excellent discussion to have - thank you for starting it!

I'm glad someone finally joined in!!

Secondly, this may be a moot point with EA separation, but you were discussing saving values verses dynamic calculation. While dynamic calc is always easier, it is very wasteful in terms of processing time. Yet, array access is very quick - so I think the best thing would be a 2 dimensional array of trades: The EA then simply has to compare values at close time, rather than computing them - these can be computed immediately after the trade, because it is unlikely we'll need to close then, so we have some "ticks" to spare so to speak. It could save a bundle of processing time.

The initial tagging would need to be dynamic if we are running this as a separate process, but arrays are definitely the way to go after they have been tagged.

You mentioned a 5 second EA cycle - was that serious, or were you just using an example? That seems huge to me.

I was serious. This was a program written a chinese programmer for pay, and every tick, whether he needed to or not, he checked the entire history file. No early termination or anything. He already had the ticket number too, so I just changed the call to get order data by ticket instead of running through 3 separate loops he wrote. I also moved the price adjustment code he was using as a scalar on an input value into init(). The time to backtest a month of data went from 6+ minutes to under 6 seconds. Simple programming mistakes can waste a ton of CPU cycles.

I love the idea of the TS replacing a traditional TP - this is what I prefer to use myself. The value as you mentioned, is that it eeks out a few more pips in most situations. The interesting discussion comes in when it is set?

Well a lot of people do forced exits before news events, why not do the smallest TS possible instead? Same thing for time expiration. Or I could have met my TP target and want to see how much I can ride the current trend but don't want to give up more than a few pips when it swings around because I know it will reverse sharp without any more up down cycles.

If you normally have a TP of say 20 do you set a 5 pip TS when you hit 20 (which of course has the potential to loose the 5 pips) or do you set the 5 pip TS at 25 so that your true potential profit is 20, but you are risking that profit for 5 extra pips.
If I want a 20pip SL, I'll set Start.Trail.After.BreakEven to true, BreakEven.Lock.Pips to 20 and the SL value of 5. This means that once I'm 25 pips in profit, my SL is moved to the 20 pip profit mark, and will now trail 5 pips behind. It works very nicely. You can always split the difference too, meaning lock pips of 22 or 23 - that means you need to be 27 pips in profit to set the 5 SL, but it also ensures more actual TP (17 compared to the 15). Obviously, these are just broad numbers to work with.

Mauro originally designed e.trailing as a separate EA, but I changed it into an include file for my purposes. You are making me doubt that choice.

You need to integrate in order to backtest, having 2 EA on the same chart means twice as much network activity, and if you have a fast EA you don't loose anything by integrating. As a separate EA, you have it assist manual trading as well. Any time I want to test a strategy before I write the EA, I manually trade it for a few days. This tells me if the system is viable and I usually tinker with it a bit. Would be nice if I could have my trailing stop exits and so on active during that testing.

Again, this is an excellent discussion, and I'm pleased that you've chosen to ruminate about it openly.

I was rather disappointed when nobody joined. Even more disappointed when I saw several EA programmers have a "new great idea" that matched one or more of my exit strategies a few days after I posted here. Not all of these are my ideas, but I gave credit to the originator of the idea.

Thanks for joining!

 

Yeah, I must admit that I lean towards a single EA rather than multiple ones. It just seems more neat.

Oddly enough, my latest EA seems to perform better without a stop loss at all. I don't know that I have the stomach for that kind of trading though.

Reason: