/*[[ Name := Flat Range Breakout Author := David W. Thomas Link := davidwt@usa.net Notes := This is Ross Todd's ranging market breakout strategy. Notes := If the market ranges within 20 pips for 5 hours, then initiate straddle entry orders 30 pips away. Lots := 1.00 Stop Loss := 30 Take Profit := 10 Trailing Stop := 20 ]]*/ /* range_width is the width the prices can range within to qualify initiating trade orders. min_hours_for_range is the minimum time in hours for which the ranging needs to occur. max_bar_size is the maximum (high - low) that is acceptable during the range. breakout_distance is the number of pips from the last bar close price to set the entry stop orders to. slippage is the number of pips to allow the price to slip to open/close the orders. split_order, set to non-zero (eg, set it to one) if the number of lots is to be split in half between two orders: one with take profit, one with only trailing stop. move_ord2_amount, if less than -999, will just delete the second order, when the first is filled; otherwise the second order stop entry is the first order stop loss adjusted by move_ord2_amount. eg, if a buy stop is filled and the stop loss is at 1.2300, then the second order entry sell stop will be moved to 1.2300+move_ord2_amount; if, instead, a sell stop is filled at 1.2400, then the second order entry buy stop will be moved to 1.2400-move_ord2_amount. */ Defines: range_width(20), max_bar_size(10), min_hours_for_range(5), breakout_distance(30), slippage(3), split_order(0), move_ord2_amount(-1000); Variables: prevtime(0), mode(0), cnt(0), first(true), index_buystop(0), index_sellstop(0), index_buy(0), index_sell(0), moved_entry_order(false), diff(0), np(0), nsl(0), ntp(0), ordercount(0), orderlots(0), priorbartime(0), state(0), barcount(0), hoc(0), loc(0), tmp(0), hb(0), lb(0), hsl(0), lsl(0), htp(0), ltp(0), isbarsizeok(false); // expert only for charts of 1hr or shorter. if Period > 60 then { print("This expert can only be used on time frames of 1 hour or shorter."); exit; }; // either take profit or trailing stop has to be non-zero. if not(TakeProfit > 0 or TrailingStop > 0) then { print("This expert requires either (or both) a Take Profit or Trailing Stop to be non-zero."); exit; }; // either range_width or max_bar_size has to be non-zero. if not(range_width > 0 or max_bar_size > 0) then { print("This expert requires either (or both) a range_width or max_bar_size to be non-zero."); exit; }; // at least 5 secs between executions. if (CurTime - prevtime) < 5 then exit; prevtime = CurTime; if state > 0 then { // do the other trades: if state == 4 then { SetOrder(OP_SELLSTOP, orderlots, lb, slippage, lsl, 0, RED); state = 0; }; if state == 3 then { orderlots = Lots - orderlots; SetOrder(OP_BUYSTOP, orderlots, hb, slippage, hsl, 0, BLUE); state = 4; }; if state == 2 then { SetOrder(OP_SELLSTOP, orderlots, lb, slippage, lsl, ltp, RED); if orderlots < Lots then state = 3 else state = 0; }; }; if barcount == 0 then barcount = Floor((60/Period)*min_hours_for_range); if bars <= barcount then exit; if first then { print("'Flat Range Breakout' conditions: for ", barcount, " bars, range size<=", range_width, " pips, bar size<=", max_bar_size, " pips."); first = false; }; index_buy = 0; index_sell = 0; index_buystop = 0; index_sellstop = 0; ordercount = 0; for cnt=1 to TotalTrades { if (OrderValue(cnt,VAL_SYMBOL) == Symbol) then { mode = Ord(cnt, VAL_TYPE); switch mode { case OP_BUY: index_buy = cnt; ordercount++; // if profitable and greater than trailing stop amount, // adjust trailing stop or take a profit. if TrailingStop > 0 and (Bid-Ord(cnt,VAL_OPENPRICE)) > (TrailingStop*Point) then { // trailing stop adjustment. if Ord(cnt,VAL_STOPLOSS) < (Bid-TrailingStop*Point) then { /* we change stoploss by the amount of Bid - Trailing Stop */ ModifyOrder(Ord(cnt,VAL_TICKET), Ord(cnt,VAL_OPENPRICE), Bid-TrailingStop*Point, Ord(cnt,VAL_TAKEPROFIT), Blue); }; }; case OP_SELL: index_sell = cnt; ordercount++; // if profitable and greater than trailing stop amount, // adjust trailing stop or take a profit. if TrailingStop > 0 and (Ord(cnt,VAL_OPENPRICE)-Ask) > (TrailingStop*Point) then { // trailing stop adjustment. if Ord(cnt,VAL_STOPLOSS) > (Ask+TrailingStop*Point) or Ord(cnt,VAL_STOPLOSS) = 0 then /* necessary condition! */ { /* we change stoploss by the amount of Ask + Trailing Stop */ ModifyOrder(Ord(cnt,VAL_TICKET), Ord(cnt,VAL_OPENPRICE), Ask+TrailingStop*Point, Ord(cnt,VAL_TAKEPROFIT), Red); }; }; case OP_BUYSTOP: index_buystop = cnt; ordercount++; case OP_SELLSTOP: index_sellstop = cnt; ordercount++; }; }; }; if index_buy and index_sellstop then { if move_ord2_amount < -999 then { DeleteOrder(Ord(index_sellstop,VAL_TICKET), LightPink); } else if not moved_entry_order then { np = Ord(index_buy,VAL_STOPLOSS) + move_ord2_amount*Point; diff = np - Ord(index_sellstop,VAL_OPENPRICE); nsl = Ord(index_sellstop,VAL_STOPLOSS) + diff; ntp = Ord(index_sellstop,VAL_TAKEPROFIT) + diff; ModifyOrder(Ord(index_sellstop,VAL_TICKET), np, nsl, ntp, Red); moved_entry_order = true; }; }; if index_sell and index_buystop then { if move_ord2_amount < -999 then { DeleteOrder(Ord(index_buystop,VAL_TICKET), LightBlue); } else if not moved_entry_order then { np = Ord(index_sell,VAL_STOPLOSS) - move_ord2_amount*Point; diff = np - Ord(index_buystop,VAL_OPENPRICE); nsl = Ord(index_buystop,VAL_STOPLOSS) + diff; ntp = Ord(index_buystop,VAL_TAKEPROFIT) + diff; ModifyOrder(Ord(index_buystop,VAL_TICKET), np, nsl, ntp, Red); moved_entry_order = true; }; }; // don't do anymore during the same bar or if already have orders. if priorbartime == Time[0] or ordercount then exit; priorbartime = Time[0]; barcount = (60/Period)*min_hours_for_range; htp = 0; ltp = 0; hsl = 0; lsl = 0; orderlots = Lots; isbarsizeok = max_bar_size <= 0; // ignore max bar size if zero or less. // determine highest and lowest during the last min_hours_for_range hours. hoc = 0; loc = 9999; if not isbarsizeok then { for cnt = barcount Downto 1 { tmp = max(Open[cnt], Close[cnt]); if tmp > hoc then hoc = tmp; tmp = min(Open[cnt], Close[cnt]); if tmp < loc then loc = tmp; isbarsizeok = (High[cnt]-Low[cnt]) <= (max_bar_size*Point); }; }; if isbarsizeok then { hoc = max(Close[Highest(MODE_CLOSE, 1+barcount, barcount)], Open[Highest(MODE_OPEN, 1+barcount, barcount)]); loc = min(Close[Lowest(MODE_CLOSE, 1+barcount, barcount)], Open[Lowest(MODE_OPEN, 1+barcount, barcount)]); //hoc = Close[Highest(MODE_CLOSE, 1+barcount, barcount)]; //loc = Close[Lowest(MODE_CLOSE, 1+barcount, barcount)]; }; // if the highest minus the lowest is within range_width, then initiate two stop orders. if isbarsizeok and (range_width <= 0 or (hoc - loc) <= range_width*Point) then { hb = Close[1] + breakout_distance*Point; lb = Close[1] - breakout_distance*Point; if TakeProfit > 0 then { htp = hb + TakeProfit*Point; ltp = lb - TakeProfit*Point; } if StopLoss > 0 then { hsl = hb - StopLoss*Point; lsl = lb + StopLoss*Point; }; // if splitting order, then half will have a take profit, the other half will not, but exit on trailing stop. if split_order and TakeProfit > 0 then { orderlots = Floor((Lots + 1)/2); // +1 in case odd number of lots, so take the safest course and close most. }; SetOrder(OP_BUYSTOP, orderlots, hb, slippage, hsl, htp, BLUE); state = 2; if (not IsTesting) and (not IsTradeAllowed) then Alert("Straddle trade of ", Symbol, ": buy stop at ", hb, ", sl=", hsl, ", tp=", htp, "; sell stop at ", lb, ", sl=", lsl, ", tp=", ltp, "."); };