enum dir_enum { Buy, Sell, Both, Random }; enum trailing_enum { Breakeven, Full, None }; // Trading parameters input datetime NewsTime = __DATE__; // News date/time. input int StopLoss = 15; // Stop-loss in broker's pips. input int TakeProfit = 75; // Take-profit in broker's pips. input dir_enum Direction = Both; // Direction of the trade to open. input trailing_enum TrailingStop = None; // Type of trailing stop based on stop-loss. input bool PreAdjustSLTP = false; // Preadjust SL/TP until news is out. input int SecondsBefore = 18; // Open trade X seconds before the news. input int CloseAfterSeconds = 3600; // Close trade X seconds after the news, 0 - turn the feature off. input bool SpreadFuse = true; // SpreadFuse - prevent trading if spread >= stop-loss. // ATR input bool UseATR = false; // Use ATR-based stop-loss and take-profit levels. input int ATR_Period = 14; // ATR Period. input double ATR_Multiplier_SL = 1; // ATR multiplier for SL. input double ATR_Multiplier_TP = 5; // ATR multiplier for TP. // Money management input double Lots = 0.01; input bool MM = true; // Money Management, if true - position sizing based on stop-loss. input double Risk = 1; // Risk - Risk tolerance in percentage points. input double FixedBalance = 0; // FixedBalance - If greater than 0, position size calculator will use it instead of actual account balance. input double MoneyRisk = 0; // MoneyRisk - Risk tolerance in base currency. input bool UseMoneyInsteadOfPercentage = false; input bool UseEquityInsteadOfBalance = false; input int LotDigits = 2; // LotDigits - How many digits after dot supported in lot size. For example, 2 for 0.01, 1 for 0.1, 3 for 0.001, etc. // Timer input bool ShowTimer = true; // Show timer before and after news. input int FontSize = 18; input string Font = "Arial"; input color FontColor = clrRed; // Miscellaneous input int Slippage = 3; input int Magic = 794823491; input string Commentary = "NewsTrader"; // Comment - trade description (e.g. "US CPI", "EU GDP", etc.). // Global variables bool HaveLongPosition, HaveShortPosition; bool ECN_Mode; int news_time; bool CanTrade = false; bool Terminal_Trade_Allowed = true; double SL, TP; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int init() { news_time = (int)NewsTime; double min_lot = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN); double lot_step = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_STEP); Print("Minimum lot: ", DoubleToString(min_lot, 2), ", lot step: ", DoubleToString(lot_step, 2), "."); if ((Lots < min_lot) && (!MM)) Alert("Lots should be not less than: ", DoubleToString(min_lot, 2), "."); else CanTrade = true; if (ShowTimer) { ObjectCreate("NewsTraderTimer", OBJ_LABEL, 0, 0, 0); ObjectSet("NewsTraderTimer", OBJPROP_CORNER, 0); ObjectSet("NewsTraderTimer", OBJPROP_XDISTANCE, 10); ObjectSet("NewsTraderTimer", OBJPROP_YDISTANCE, 130); EventSetMillisecondTimer(100); // For smooth updates. } // If UseATR = false, these values will be used. Otherwise, ATR values will be calculated later. SL = StopLoss; TP = TakeProfit; return(0); } //+------------------------------------------------------------------+ //| Stops timer and deletes graphical object if needed. | //+------------------------------------------------------------------+ int deinit() { if (ShowTimer) { EventKillTimer(); ObjectDelete("NewsTraderTimer"); } return(0); } //+------------------------------------------------------------------+ //| Updates text about time left to news or passed after news. | //+------------------------------------------------------------------+ void OnTimer() { DoTrading(); string text; int difference = (int)TimeCurrent() - news_time; if (difference <= 0) text = "Time to news: " + TimeDistance(-difference); else text = "Time after news: " + TimeDistance(difference); ObjectSetText("NewsTraderTimer", text + ".", FontSize, Font, FontColor); } //+------------------------------------------------------------------+ //| Format time distance from the number of seconds to normal string | //| of years, days, hours, minutes, and seconds. | //| t - number of seconds | //| Returns: formatted string. | //+------------------------------------------------------------------+ string TimeDistance(int t) { if (t == 0) return("0 seconds"); string s = ""; int y = 0; int d = 0; int h = 0; int m = 0; y = t / 31536000; t -= y * 31536000; d = t / 86400; t -= d * 86400; h = t / 3600; t -= h * 3600; m = t / 60; t -= m * 60; if (y) s += IntegerToString(y) + " year"; if (y > 1) s += "s"; if (d) s += " " + IntegerToString(d) + " day"; if (d > 1) s += "s"; if (h) s += " " + IntegerToString(h) + " hour"; if (h > 1) s += "s"; if (m) s += " " + IntegerToString(m) + " minute"; if (m > 1) s += "s"; if (t) s += " " + IntegerToString(t) + " second"; if (t > 1) s += "s"; return(StringTrimLeft(s)); } //+------------------------------------------------------------------+ //| Check every tick. | //+------------------------------------------------------------------+ int start() { return(DoTrading()); } //+------------------------------------------------------------------+ //| Main execution procedure. | //+------------------------------------------------------------------+ int DoTrading() { if ((TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) == false) || (!CanTrade)) { if ((TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) == false) && (Terminal_Trade_Allowed == true)) { Print("Trading not allowed."); Terminal_Trade_Allowed = false; } else if ((TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) == true) && (Terminal_Trade_Allowed == false)) { Print("Trading allowed."); Terminal_Trade_Allowed = true; } return(0); } ENUM_SYMBOL_TRADE_EXECUTION Execution_Mode = (ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(Symbol(), SYMBOL_TRADE_EXEMODE); if (Execution_Mode == SYMBOL_TRADE_EXECUTION_MARKET) ECN_Mode = true; else ECN_Mode = false; // Do nothing if it is too early. int time = (int)TimeCurrent(); if (time < news_time - SecondsBefore) return(0); if (UseATR) { // Getting the ATR values double ATR = iATR(NULL, 0, ATR_Period, 0); SL = ATR * ATR_Multiplier_SL; if (SL <= (SymbolInfoInteger(Symbol(), SYMBOL_TRADE_STOPS_LEVEL) + SymbolInfoInteger(Symbol(), SYMBOL_SPREAD)) * Point) SL = (SymbolInfoInteger(Symbol(), SYMBOL_TRADE_STOPS_LEVEL) + SymbolInfoInteger(Symbol(), SYMBOL_SPREAD)) * Point; TP = ATR * ATR_Multiplier_TP; if (TP <= (SymbolInfoInteger(Symbol(), SYMBOL_TRADE_STOPS_LEVEL) + SymbolInfoInteger(Symbol(), SYMBOL_SPREAD)) * Point) TP = (SymbolInfoInteger(Symbol(), SYMBOL_TRADE_STOPS_LEVEL) + SymbolInfoInteger(Symbol(), SYMBOL_SPREAD)) * Point; SL /= Point; TP /= Point; } // Check what position is currently open GetPositionStates(); // Adjust SL and TP of the current position if ((HaveLongPosition) || (HaveShortPosition)) ControlPosition(); else { // Time to news is less or equal to SecondsBefore but is not negative. if ((news_time - time <= SecondsBefore) && (news_time > time)) { // Prevent position opening when spreads are too wide (bigger than StopLoss input). int spread = (int)MarketInfo(Symbol(), MODE_SPREAD); if ((SpreadFuse) && (spread >= StopLoss)) { Print(Symbol(), ": Spread fuse prevents positions from opening. Current spread: ", spread, " points."); return(0); } if (Direction == Buy) fBuy(); else if (Direction == Sell) fSell(); else if (Direction == Both) { fBuy(); fSell(); } else if (Direction == Random) { MathSrand((uint)TimeCurrent()); if (MathRand() % 2 == 1) fBuy(); else fSell(); } if (ECN_Mode) ControlPosition(); } } return(0); } //+------------------------------------------------------------------+ //| Check what positions are currently open. | //+------------------------------------------------------------------+ void GetPositionStates() { HaveLongPosition = false; HaveShortPosition = false; int total = OrdersTotal(); for (int cnt = 0; cnt < total; cnt++) { if (OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES) == false) continue; if (OrderMagicNumber() != Magic) continue; if (OrderSymbol() != Symbol()) continue; if (OrderType() == OP_BUY) HaveLongPosition = true; else if (OrderType() == OP_SELL) HaveShortPosition = true; } } //+------------------------------------------------------------------+ //| Add SL/TP, adjust SL/TP, set breakeven, close trade. | //+------------------------------------------------------------------+ void ControlPosition() { int total = OrdersTotal(); for (int cnt = total - 1; cnt >= 0; cnt--) { if (OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES) == false) continue; if (OrderMagicNumber() != Magic) continue; if (OrderSymbol() != Symbol()) continue; if ((OrderType() == OP_BUY) || (OrderType() == OP_SELL)) { int time = (int)TimeCurrent(); double new_sl, new_tp; if (SL < MarketInfo(Symbol(), MODE_STOPLEVEL) + MarketInfo(Symbol(), MODE_SPREAD)) SL = MarketInfo(Symbol(), MODE_STOPLEVEL) + MarketInfo(Symbol(), MODE_SPREAD); if (TP < MarketInfo(Symbol(), MODE_STOPLEVEL) + MarketInfo(Symbol(), MODE_SPREAD)) TP = MarketInfo(Symbol(), MODE_STOPLEVEL) + MarketInfo(Symbol(), MODE_SPREAD); if (OrderType() == OP_BUY) { RefreshRates(); new_sl = NormalizeDouble(Ask - SL * Point, Digits); new_tp = NormalizeDouble(Ask + TP * Point, Digits); } else if (OrderType() == OP_SELL) { RefreshRates(); new_sl = NormalizeDouble(Bid + SL * Point, Digits); new_tp = NormalizeDouble(Bid - TP * Point, Digits); } // Need to adjust or add SL/TP if (time < news_time) { // Adjust only if parameter is set or if in ECN mode and need to assign SL/TP first time. if ((((new_sl != NormalizeDouble(OrderStopLoss(), Digits)) || (new_tp != NormalizeDouble(OrderTakeProfit(), Digits))) && (PreAdjustSLTP)) || (((OrderStopLoss() == 0) || (OrderTakeProfit() == 0)) && (ECN_Mode))) { Print("Adjusting SL: ", new_sl, " and TP: ", new_tp, "."); for (int i = 0; i < 10; i++) { bool result = OrderModify(OrderTicket(), OrderOpenPrice(), new_sl, new_tp, 0); if (result) return; else Print("Error modifying the order: ", GetLastError()); } } } // Check for breakeven or trade time out. Plus, sometimes, in ECN mode, it is necessary to check if SL/TP was set even after the news. else { RefreshRates(); // Adjust only if in ECN mode and need to assign SL/TP first time. if (((OrderStopLoss() == 0) || (OrderTakeProfit() == 0)) && (ECN_Mode)) { Print("Adjusting SL: ", new_sl, " and TP: ", new_tp, "."); for (int i = 0; i < 10; i++) { bool result = OrderModify(OrderTicket(), OrderOpenPrice(), new_sl, new_tp, 0); if (result) return; else Print("Error modifying the order: ", GetLastError()); } } if ((TrailingStop == Breakeven) && ((((OrderType() == OP_BUY) && (Ask - OrderOpenPrice() >= SL * Point)) || ((OrderType() == OP_SELL) && (OrderOpenPrice() - Bid >= SL * Point))))) { new_sl = NormalizeDouble(OrderOpenPrice(), Digits); if (new_sl != NormalizeDouble(OrderStopLoss(), Digits)) { Print("Moving SL to breakeven: ", new_sl, "."); for (int i = 0; i < 10; i++) { bool result = OrderModify(OrderTicket(), OrderOpenPrice(), new_sl, OrderTakeProfit(), 0); if (result) break; } } } else if ((TrailingStop == Full) && ((((OrderType() == OP_BUY) && (Ask - OrderStopLoss() >= SL * Point)) || ((OrderType() == OP_SELL) && (OrderStopLoss() - Bid >= SL * Point))))) { if (OrderType() == OP_BUY) new_sl = NormalizeDouble(Ask - SL * Point, Digits); else if (OrderType() == OP_SELL) new_sl = NormalizeDouble(Bid + SL * Point, Digits); if (((OrderType() == OP_BUY) && (new_sl > NormalizeDouble(OrderStopLoss(), Digits))) || ((OrderType() == OP_SELL) && (new_sl < NormalizeDouble(OrderStopLoss(), Digits)))) { Print("Moving trailing SL to ", new_sl, "."); for (int i = 0; i < 10; i++) { bool result = OrderModify(OrderTicket(), OrderOpenPrice(), new_sl, OrderTakeProfit(), 0); if (result) break; } } } if (CloseAfterSeconds > 0) { if (time - news_time >= CloseAfterSeconds) { Print("Closing trade by time out."); double price; RefreshRates(); if (OrderType() == OP_BUY) price = Bid; else if (OrderType() == OP_SELL) price = Ask; if (!OrderClose(OrderTicket(), OrderLots(), price, Slippage, clrBlue)) { Print("OrderClose() failed: ", GetLastError()); } } } } } } } //+------------------------------------------------------------------+ //| Generic buy. | //+------------------------------------------------------------------+ void fBuy() { Print("Opening Buy."); for (int i = 0; i < 10; i++) { double new_sl = 0, new_tp = 0; double lots = LotsOptimized(); RefreshRates(); // Bid and Ask are swapped to preserve the probabilities and decrease/increase profit/loss size if (!ECN_Mode) { new_sl = NormalizeDouble(Ask - SL * Point, Digits); new_tp = NormalizeDouble(Ask + TP * Point, Digits); } int result = OrderSend(Symbol(), OP_BUY, lots, Ask, Slippage, new_sl, new_tp, Commentary, Magic, 0, clrBlue); Sleep(1000); if (result == -1) { int e = GetLastError(); Print(e); } else return; } } //+------------------------------------------------------------------+ //| Generic sell. | //+------------------------------------------------------------------+ void fSell() { Print("Opening Sell."); for (int i = 0; i < 10; i++) { double new_sl = 0, new_tp = 0; double lots = LotsOptimized(); RefreshRates(); // Bid and Ask are swapped to preserve the probabilities and decrease/increase profit/loss size if (!ECN_Mode) { new_sl = NormalizeDouble(Bid + SL * Point, Digits); new_tp = NormalizeDouble(Bid - TP * Point, Digits); } int result = OrderSend(Symbol(), OP_SELL, lots, Bid, Slippage, new_sl, new_tp, Commentary, Magic, 0, clrRed); Sleep(1000); if (result == -1) { int e = GetLastError(); Print(e); } else return; } } //+------------------------------------------------------------------+ //| Calculate position size depending on money management parameters.| //+------------------------------------------------------------------+ double LotsOptimized() { if (!MM) return (Lots); double Size, RiskMoney, PositionSize = 0; if (AccountCurrency() == "") return(0); if (FixedBalance > 0) { Size = FixedBalance; } else if (UseEquityInsteadOfBalance) { Size = AccountEquity(); } else { Size = AccountBalance(); } if (!UseMoneyInsteadOfPercentage) RiskMoney = Size * Risk / 100; else RiskMoney = MoneyRisk; double UnitCost = MarketInfo(Symbol(), MODE_TICKVALUE); double TickSize = MarketInfo(Symbol(), MODE_TICKSIZE); if ((SL != 0) && (UnitCost != 0) && (TickSize != 0)) PositionSize = NormalizeDouble(RiskMoney / (SL * Point * UnitCost / TickSize), LotDigits); if (PositionSize < MarketInfo(Symbol(), MODE_MINLOT)) { Print("Calculated position size (" + DoubleToString(PositionSize, 2) + ") is less than minimum position size (" + DoubleToString(MarketInfo(Symbol(), MODE_MINLOT), 2) + "). Setting position size to minimum."); PositionSize = MarketInfo(Symbol(), MODE_MINLOT); } else if (PositionSize > MarketInfo(Symbol(), MODE_MAXLOT)) { Print("Calculated position size (" + DoubleToString(PositionSize, 2) + ") is greater than maximum position size (" + DoubleToString(MarketInfo(Symbol(), MODE_MAXLOT), 2) + "). Setting position size to maximum."); PositionSize = MarketInfo(Symbol(), MODE_MAXLOT); } double LotStep = MarketInfo(Symbol(), MODE_LOTSTEP); double steps = PositionSize / LotStep; if (MathFloor(steps) < steps) { Print("Calculated position size (" + DoubleToString(PositionSize, 2) + ") uses uneven step size. Allowed step size = " + DoubleToString(MarketInfo(Symbol(), MODE_LOTSTEP), 2) + ". Setting position size to " + DoubleToString(MathFloor(steps) * LotStep, 2) + "."); PositionSize = MathFloor(steps) * LotStep; } return(PositionSize); } //+------------------------------------------------------------------+