// More information about this indicator can be found at: // http://fxcodebase.com/code/viewtopic.php?f=38&t=70676 //+------------------------------------------------------------------+ //| Copyright © 2020, Gehtsoft USA LLC | //| http://fxcodebase.com | //+------------------------------------------------------------------+ //| Developed by : Mario Jemic | //| mario.jemic@gmail.com | //| https://AppliedMachineLearning.systems | //+------------------------------------------------------------------+ //| Support our efforts by donating | //| Paypal : https://goo.gl/9Rj74e | //| Patreon : https://goo.gl/GdXWeN | //+------------------------------------------------------------------+ #property copyright "Copyright © 2020, Gehtsoft USA LLC" #property link "http://fxcodebase.com" #property version "1.5" // EA with actions on other trades v1.0 #property strict enum TradingSide { LongSideOnly, // Long ShortSideOnly, // Short BothSides // Both }; input TradingSide trade_side = BothSides; input double amount = 0.01; // Lots input double entry_price = 0; // Entry price. 0 for auto input double stop_loss = 10; // Stop loss, pips input double trailing_step = 10; // Trailing step input double trailing_start = 0; // Min distance to order to activate the trailing input double take_profit = 10; // Take profit, pips input double stop = 60; // Stop in Pips input double limit = 30; // Limit in Pips input double distance = 30; // Distance input int magic_number = 42; // Magic number input int slippage = 3; // Slippage, points input bool ecn_broker = false; // ECN Broker? // Trades monitor v.2.0 // Action v2.0 #ifndef IAction_IMP #define IAction_IMP interface IAction { public: virtual void AddRef() = 0; virtual void Release() = 0; virtual bool DoAction(const int period, const datetime date) = 0; }; #endif #ifndef TradingMonitor_IMP #define TradingMonitor_IMP class TradingMonitor { int active_ticket[1000]; double active_type[1000]; double active_price[1000]; double active_stoploss[1000]; double active_takeprofit[1000]; bool active_still_active[1000]; int active_total; IAction* _closedTradeAction; IAction* _tradeChangedAction; IAction* _newTradeAction; bool _initial; public: TradingMonitor() { _initial = true; _closedTradeAction = NULL; _tradeChangedAction = NULL; _newTradeAction = NULL; } ~TradingMonitor() { if (_closedTradeAction != NULL) _closedTradeAction.Release(); if (_tradeChangedAction != NULL) _tradeChangedAction.Release(); if (_newTradeAction != NULL) _newTradeAction.Release(); } void SetClosedTradeAction(IAction* action) { if (_closedTradeAction != NULL) _closedTradeAction.Release(); _closedTradeAction = action; if (_closedTradeAction != NULL) _closedTradeAction.AddRef(); } void SetOnTradeChanged(IAction* action) { if (_tradeChangedAction != NULL) _tradeChangedAction.Release(); _tradeChangedAction = action; if (_tradeChangedAction != NULL) _tradeChangedAction.AddRef(); } void SetOnNewTrade(IAction* action) { if (_newTradeAction != NULL) _newTradeAction.Release(); _newTradeAction = action; if (_newTradeAction != NULL) _newTradeAction.AddRef(); } void DoWork() { bool changed = false; int total = OrdersTotal(); for (int i = 0; i < total; i++) { if (!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue; int ticket = OrderTicket(); int index = getOrderCacheIndex(ticket); if (index == -1) { changed = true; if (_newTradeAction != NULL && !_initial) { Print("111"); _newTradeAction.DoAction(0, 0); } } else { active_still_active[index] = true; // order is still there if (OrderOpenPrice() != active_price[index] || OrderStopLoss() != active_stoploss[index] || OrderTakeProfit() != active_takeprofit[index] || OrderType() != active_type[index]) { changed = true; if (_tradeChangedAction != NULL && !_initial) _tradeChangedAction.DoAction(0, 0); } } } for (int index = 0; index < active_total; index++) { if (active_still_active[index] == false) { changed = true; if (_closedTradeAction != NULL && OrderSelect(active_ticket[index], MODE_HISTORY) && !_initial) _closedTradeAction.DoAction(0, 0); } active_still_active[index] = false; } if (changed) updateActiveOrders(); _initial = false; } private: int getOrderCacheIndex(const int ticket) { for (int i = 0; i < active_total; i++) { if (active_ticket[i] == ticket) return i; } return -1; } /** * read in the current state of all open orders * and trades so we can track any changes in the next tick */ void updateActiveOrders() { active_total = OrdersTotal(); for (int i = 0; i < active_total; i++) { if (!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue; active_ticket[i] = OrderTicket(); active_type[i] = OrderType(); active_price[i] = OrderOpenPrice(); active_stoploss[i] = OrderStopLoss(); active_takeprofit[i] = OrderTakeProfit(); active_still_active[i] = false; // filled in the next tick } } }; #endif // Action on condition logic v2.0 // ICondition v3.1 // More templates and snippets on https://github.com/sibvic/mq4-templates interface ICondition { public: virtual void AddRef() = 0; virtual void Release() = 0; virtual bool IsPass(const int period, const datetime date) = 0; virtual string GetLogMessage(const int period, const datetime date) = 0; }; // Action on condition controller interface v1.0 #ifndef IActionOnConditionController_IMP #define IActionOnConditionController_IMP class IActionOnConditionController { public: virtual bool Set(IAction* action, ICondition *condition) = 0; virtual void DoLogic(const int period, datetime date) = 0; }; #endif // Action on condition v3.0 #ifndef ActionOnConditionController_IMP #define ActionOnConditionController_IMP class ActionOnConditionController : public IActionOnConditionController { bool _finished; ICondition *_condition; IAction* _action; public: ActionOnConditionController() { _action = NULL; _condition = NULL; _finished = true; } ~ActionOnConditionController() { _action.Release(); _condition.Release(); } bool Set(IAction* action, ICondition *condition) { if (!_finished || action == NULL) return false; if (_action != NULL) _action.Release(); _action = action; _action.AddRef(); _finished = false; if (_condition != NULL) _condition.Release(); _condition = condition; _condition.AddRef(); return true; } void DoLogic(const int period, datetime date) { if (_finished) return; if (_condition.IsPass(period, date) && _action.DoAction(period, date)) { _finished = true; } } }; #endif // Multi action on condition v1.0 #ifndef MultiActionOnConditionController_IMP #define MultiActionOnConditionController_IMP class MultiActionOnConditionController : public IActionOnConditionController { ICondition *_condition; IAction* _action; public: MultiActionOnConditionController() { _action = NULL; _condition = NULL; } ~MultiActionOnConditionController() { _action.Release(); _condition.Release(); } bool Set(IAction* action, ICondition *condition) { if (action == NULL) { return false; } if (_action != NULL) { _action.Release(); } _action = action; _action.AddRef(); if (_condition != NULL) { _condition.Release(); } _condition = condition; _condition.AddRef(); return true; } void DoLogic(const int period, datetime date) { if (_condition.IsPass(period, date)) { _action.DoAction(period, date); } } }; #endif #ifndef ActionOnConditionLogic_IMP #define ActionOnConditionLogic_IMP class ActionOnConditionLogic { IActionOnConditionController* _controllers[]; public: ~ActionOnConditionLogic() { int count = ArraySize(_controllers); for (int i = 0; i < count; ++i) { delete _controllers[i]; } } void DoLogic(const int period, datetime date) { int count = ArraySize(_controllers); for (int i = 0; i < count; ++i) { _controllers[i].DoLogic(period, date); } } bool AddActionOnCondition(IAction* action, ICondition* condition) { int count = ArraySize(_controllers); for (int i = 0; i < count; ++i) { if (_controllers[i].Set(action, condition)) return true; } ArrayResize(_controllers, count + 1); _controllers[count] = new ActionOnConditionController(); return _controllers[count].Set(action, condition); } bool AddMultiActionOnCondition(IAction* action, ICondition* condition) { int count = ArraySize(_controllers); for (int i = 0; i < count; ++i) { if (_controllers[i].Set(action, condition)) return true; } ArrayResize(_controllers, count + 1); _controllers[count] = new MultiActionOnConditionController(); return _controllers[count].Set(action, condition); } }; #endif // AAction v1.0 #ifndef AAction_IMP #define AAction_IMP class AAction : public IAction { protected: int _references; AAction() { _references = 1; } public: void AddRef() { ++_references; } void Release() { --_references; if (_references == 0) delete &this; } }; #endif // Orders iterator v 1.12 // More templates and snippets on https://github.com/sibvic/mq4-templates #ifndef OrdersIterator_IMP #define OrdersIterator_IMP enum CompareType { CompareLessThan, CompareMoreThan }; // Order side enum v1.1 #ifndef OrderSide_IMP #define OrderSide_IMP enum OrderSide { BuySide, // Buy/long SellSide // Sell/short }; OrderSide GetOppositeSide(OrderSide side) { return side == BuySide ? SellSide : BuySide; } #endif class OrdersIterator { bool _useMagicNumber; int _magicNumber; bool _useOrderType; int _orderType; bool _trades; bool _useSide; bool _isBuySide; int _lastIndex; bool _useSymbol; string _symbol; bool _useProfit; double _profit; bool _useComment; string _comment; CompareType _profitCompare; bool _orders; public: OrdersIterator() { _useOrderType = false; _useMagicNumber = false; _useSide = false; _lastIndex = INT_MIN; _trades = false; _useSymbol = false; _useProfit = false; _orders = false; _useComment = false; } OrdersIterator *WhenSymbol(const string symbol) { _useSymbol = true; _symbol = symbol; return &this; } OrdersIterator *WhenProfit(const double profit, const CompareType compare) { _useProfit = true; _profit = profit; _profitCompare = compare; return &this; } OrdersIterator *WhenTrade() { _trades = true; return &this; } OrdersIterator *WhenOrder() { _orders = true; return &this; } OrdersIterator *WhenSide(const OrderSide side) { _useSide = true; _isBuySide = side == BuySide; return &this; } OrdersIterator *WhenOrderType(const int orderType) { _useOrderType = true; _orderType = orderType; return &this; } OrdersIterator *WhenMagicNumber(const int magicNumber) { _useMagicNumber = true; _magicNumber = magicNumber; return &this; } OrdersIterator *WhenComment(const string comment) { _useComment = true; _comment = comment; return &this; } int GetOrderType() { return OrderType(); } double GetProfit() { return OrderProfit(); } double IsBuy() { return OrderType() == OP_BUY; } double IsSell() { return OrderType() == OP_SELL; } int GetTicket() { return OrderTicket(); } datetime GetOpenTime() { return OrderOpenTime(); } double GetOpenPrice() { return OrderOpenPrice(); } double GetStopLoss() { return OrderStopLoss(); } double GetTakeProfit() { return OrderTakeProfit(); } string GetSymbol() { return OrderSymbol(); } int Count() { int count = 0; for (int i = OrdersTotal() - 1; i >= 0; i--) { if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && PassFilter()) count++; } return count; } bool Next() { if (_lastIndex == INT_MIN) _lastIndex = OrdersTotal() - 1; else _lastIndex = _lastIndex - 1; while (_lastIndex >= 0) { if (OrderSelect(_lastIndex, SELECT_BY_POS, MODE_TRADES) && PassFilter()) return true; _lastIndex = _lastIndex - 1; } return false; } bool Any() { for (int i = OrdersTotal() - 1; i >= 0; i--) { if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && PassFilter()) return true; } return false; } int First() { for (int i = OrdersTotal() - 1; i >= 0; i--) { if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && PassFilter()) return OrderTicket(); } return -1; } void Reset() { _lastIndex = INT_MIN; } private: bool PassFilter() { if (_useMagicNumber && OrderMagicNumber() != _magicNumber) return false; if (_useOrderType && OrderType() != _orderType) return false; if (_trades && !IsTrade()) return false; if (_orders && IsTrade()) return false; if (_useSymbol && OrderSymbol() != _symbol) return false; if (_useProfit) { switch (_profitCompare) { case CompareLessThan: if (OrderProfit() >= _profit) { return false; } break; case CompareMoreThan: if (OrderProfit() <= _profit) { return false; } break; } } if (_useSide) { if (_trades) { if (_isBuySide && !IsBuy()) return false; if (!_isBuySide && !IsSell()) return false; } else { //TODO: IMPLEMENT!!!! } } if (_useComment && OrderComment() != _comment) return false; return true; } bool IsTrade() { return (OrderType() == OP_BUY || OrderType() == OP_SELL) && OrderCloseTime() == 0.0; } }; #endif // Delete pending orders action v1.0 #ifndef DeletePendingOrdersAction_IMP #define DeletePendingOrdersAction_IMP class DeletePendingOrdersAction : public AAction { int _magicNumber; int _slippagePoints; public: DeletePendingOrdersAction(int magicNumber, int slippagePoints) { _magicNumber = magicNumber; _slippagePoints = slippagePoints; } virtual bool DoAction(const int period, const datetime date) { if (OrderType() == OP_BUY || OrderType() == OP_SELL) { OrdersIterator toClose(); toClose.WhenMagicNumber(_magicNumber).WhenOrder(); while (toClose.Next()) { OrderDelete(toClose.GetTicket()); } } return false; } }; #endif // Order builder v2.2 // Instrument info v.1.7 // More templates and snippets on https://github.com/sibvic/mq4-templates #ifndef InstrumentInfo_IMP #define InstrumentInfo_IMP class InstrumentInfo { string _symbol; double _mult; double _point; double _pipSize; int _digits; double _tickSize; public: InstrumentInfo(const string symbol) { _symbol = symbol; _point = MarketInfo(symbol, MODE_POINT); _digits = (int)MarketInfo(symbol, MODE_DIGITS); _mult = _digits == 3 || _digits == 5 ? 10 : 1; _pipSize = _point * _mult; _tickSize = MarketInfo(_symbol, MODE_TICKSIZE); } // Return < 0 when lot1 < lot2, > 0 when lot1 > lot2 and 0 owtherwise int CompareLots(double lot1, double lot2) { double lotStep = SymbolInfoDouble(_symbol, SYMBOL_VOLUME_STEP); if (lotStep == 0) { return lot1 < lot2 ? -1 : (lot1 > lot2 ? 1 : 0); } int lotSteps1 = (int)floor(lot1 / lotStep + 0.5); int lotSteps2 = (int)floor(lot2 / lotStep + 0.5); int res = lotSteps1 - lotSteps2; return res; } static double GetBid(const string symbol) { return MarketInfo(symbol, MODE_BID); } double GetBid() { return GetBid(_symbol); } static double GetAsk(const string symbol) { return MarketInfo(symbol, MODE_ASK); } double GetAsk() { return GetAsk(_symbol); } static double GetPipSize(const string symbol) { double point = MarketInfo(symbol, MODE_POINT); double digits = (int)MarketInfo(symbol, MODE_DIGITS); double mult = digits == 3 || digits == 5 ? 10 : 1; return point * mult; } double GetPipSize() { return _pipSize; } double GetPointSize() { return _point; } string GetSymbol() { return _symbol; } double GetSpread() { return (GetAsk() - GetBid()) / GetPipSize(); } int GetDigits() { return _digits; } double GetTickSize() { return _tickSize; } double GetMinLots() { return SymbolInfoDouble(_symbol, SYMBOL_VOLUME_MIN); }; double AddPips(const double rate, const double pips) { return RoundRate(rate + pips * _pipSize); } double RoundRate(const double rate) { return NormalizeDouble(MathFloor(rate / _tickSize + 0.5) * _tickSize, _digits); } double RoundLots(const double lots) { double lotStep = SymbolInfoDouble(_symbol, SYMBOL_VOLUME_STEP); if (lotStep == 0) { return 0.0; } return floor(lots / lotStep) * lotStep; } double LimitLots(const double lots) { double minVolume = GetMinLots(); if (minVolume > lots) { return 0.0; } double maxVolume = SymbolInfoDouble(_symbol, SYMBOL_VOLUME_MAX); if (maxVolume < lots) { return maxVolume; } return lots; } double NormalizeLots(const double lots) { return LimitLots(RoundLots(lots)); } }; #endif // Trading commands v.2.14 // More templates and snippets on https://github.com/sibvic/mq4-templates #ifndef TradingCommands_IMP #define TradingCommands_IMP class TradingCommands { public: static bool MoveSLTP(const int ticketId, const double newStopLoss, const double newTakeProfit, string &error) { if (!OrderSelect(ticketId, SELECT_BY_TICKET, MODE_TRADES) || OrderCloseTime() != 0) { error = "Trade not found"; return false; } double rate = OrderOpenPrice(); ResetLastError(); int res = OrderModify(ticketId, rate, newStopLoss, newTakeProfit, 0, CLR_NONE); int errorCode = GetLastError(); switch (errorCode) { case ERR_NO_ERROR: break; case ERR_NO_RESULT: error = "Broker returned no error but no confirmation as well"; break; case ERR_INVALID_TICKET: error = "Trade not found"; return false; case ERR_INVALID_STOPS: { string symbol = OrderSymbol(); InstrumentInfo instrument(symbol); double point = instrument.GetPointSize(); int minStopDistancePoints = (int)MarketInfo(symbol, MODE_STOPLEVEL); if (newStopLoss != 0.0 && MathRound(MathAbs(rate - newStopLoss) / point) < minStopDistancePoints) error = "Your stop loss level is too close. The minimal distance allowed is " + IntegerToString(minStopDistancePoints) + " points"; else if (newTakeProfit != 0.0 && MathRound(MathAbs(rate - newTakeProfit) / point) < minStopDistancePoints) error = "Your take profit level is too close. The minimal distance allowed is " + IntegerToString(minStopDistancePoints) + " points"; else { int orderType = OrderType(); bool isBuyOrder = orderType == OP_BUY || orderType == OP_BUYLIMIT || orderType == OP_BUYSTOP; double rateDistance = orderType ? MathAbs(rate - instrument.GetAsk()) / point : MathAbs(rate - instrument.GetBid()) / point; if (rateDistance < minStopDistancePoints) error = "Distance to the pending order rate is too close: " + DoubleToStr(rateDistance, 1) + ". Min. allowed distance: " + IntegerToString(minStopDistancePoints); else error = "Invalid stop loss or take profit in the request"; } } return false; default: error = "Last error: " + IntegerToString(errorCode); return false; } return true; } static bool MoveSL(const int ticketId, const double newStopLoss, string &error) { if (!OrderSelect(ticketId, SELECT_BY_TICKET, MODE_TRADES) || OrderCloseTime() != 0) { error = "Trade not found"; return false; } return MoveSLTP(ticketId, newStopLoss, OrderTakeProfit(), error); } static bool MoveTP(const int ticketId, const double newTakeProfit, string &error) { if (!OrderSelect(ticketId, SELECT_BY_TICKET, MODE_TRADES) || OrderCloseTime() != 0) { error = "Trade not found"; return false; } return MoveSLTP(ticketId, OrderStopLoss(), newTakeProfit, error); } static void DeleteOrders(const int magicNumber) { OrdersIterator orders(); orders.WhenMagicNumber(magicNumber); orders.WhenOrder(); DeleteOrders(orders); } static void DeleteOrders(OrdersIterator& orders) { while (orders.Next()) { int ticket = OrderTicket(); if (!OrderDelete(ticket)) Print("Failed to delete the order " + IntegerToString(ticket)); } } static bool DeleteCurrentOrder(string &error) { int ticket = OrderTicket(); if (!OrderDelete(ticket)) { error = "Failed to delete the order " + IntegerToString(ticket); return false; } return true; } static bool CloseCurrentOrder(const int slippage, const double amount, string &error) { int orderType = OrderType(); if (orderType == OP_BUY) return CloseCurrentOrder(InstrumentInfo::GetBid(OrderSymbol()), slippage, amount, error); if (orderType == OP_SELL) return CloseCurrentOrder(InstrumentInfo::GetAsk(OrderSymbol()), slippage, amount, error); return false; } static bool CloseCurrentOrder(const int slippage, string &error) { return CloseCurrentOrder(slippage, OrderLots(), error); } static bool CloseCurrentOrder(const double price, const int slippage, string &error) { return CloseCurrentOrder(price, slippage, OrderLots(), error); } static bool CloseCurrentOrder(const double price, const int slippage, const double amount, string &error) { bool closed = OrderClose(OrderTicket(), amount, price, slippage); if (closed) return true; int lastError = GetLastError(); switch (lastError) { case ERR_NOT_ENOUGH_MONEY: error = "Not enough money"; break; case ERR_TRADE_NOT_ALLOWED: error = "Trading is not allowed"; break; case ERR_INVALID_PRICE: error = "Invalid closing price: " + DoubleToStr(price); break; case ERR_INVALID_TRADE_VOLUME: error = "Invalid trade volume: " + DoubleToStr(amount); break; case ERR_TRADE_PROHIBITED_BY_FIFO: error = "Prohibited by FIFO"; break; case ERR_MARKET_CLOSED: error = "The market is closed"; break; default: error = "Last error: " + IntegerToString(lastError); break; } return false; } static int CloseTrades(OrdersIterator &it, const int slippage) { int failed = 0; return CloseTrades(it, slippage, failed); } static int CloseTrades(OrdersIterator &it, const int slippage, int& failed) { int closedPositions = 0; failed = 0; while (it.Next()) { string error; if (!CloseCurrentOrder(slippage, error)) { ++failed; Print("Failed to close positoin. ", error); } else ++closedPositions; } return closedPositions; } }; #endif // No stop loss or take profit condition v1.0 // Abstract condition v1.1 #ifndef AConditionBase_IMP #define AConditionBase_IMP class AConditionBase : public ICondition { int _references; string _conditionName; public: AConditionBase(string name = "") { _conditionName = name; _references = 1; } virtual void AddRef() { ++_references; } virtual void Release() { --_references; if (_references == 0) delete &this; } virtual string GetLogMessage(const int period, const datetime date) { if (_conditionName == "" || _conditionName == NULL) { return ""; } return _conditionName + ": " + (IsPass(period, date) ? "true" : "false"); } }; #endif #ifndef NoStopLossOrTakeProfitCondition_IMP #define NoStopLossOrTakeProfitCondition_IMP class NoStopLossOrTakeProfitCondition : public AConditionBase { int _currentTicket; public: NoStopLossOrTakeProfitCondition(int currentTicket) { _currentTicket = currentTicket; } virtual bool IsPass(const int period, const datetime date) { if (!OrderSelect(_currentTicket, SELECT_BY_TICKET, MODE_TRADES) || OrderCloseTime() != 0.0) return true; return OrderStopLoss() == 0 || OrderTakeProfit() == 0; } }; #endif // Set stop loss and/or take profit action v2.0 #ifndef SetStopLossAndTakeProfitAction_IMP #define SetStopLossAndTakeProfitAction_IMP class SetStopLossAndTakeProfitAction : public AAction { double _stopLoss; double _takeProfit; int _currentTicket; public: SetStopLossAndTakeProfitAction(double stopLoss, double takeProfit, int currentTicket) { _stopLoss = stopLoss; _takeProfit = takeProfit; _currentTicket = currentTicket; } ~SetStopLossAndTakeProfitAction() { } virtual bool DoAction(const int period, const datetime date) { if (!OrderSelect(_currentTicket, SELECT_BY_TICKET, MODE_TRADES) || OrderCloseTime() != 0.0) return true; if ((OrderStopLoss() != 0 || _stopLoss == 0) && (OrderTakeProfit() != 0 || _takeProfit == 0)) return true; string errorMessage; bool success = TradingCommands::MoveSLTP(_currentTicket, _stopLoss, _takeProfit, errorMessage); return success && (errorMessage == NULL || errorMessage == ""); } }; #endif #ifndef OrderBuilder_IMP #define OrderBuilder_IMP class OrderBuilder { OrderSide _orderSide; string _instrument; double _amount; double _rate; int _slippage; double _stopLoss; double _takeProfit; int _magicNumber; string _comment; bool _ecnBroker; int _orderType; ActionOnConditionLogic* _actions; public: OrderBuilder(ActionOnConditionLogic* actions) { _orderType = -1; _actions = actions; _ecnBroker = false; } OrderBuilder* SetOrderType(int orderType) { _orderType = orderType; return &this; } // Sets ECN broker flag OrderBuilder* SetECNBroker(bool isEcn) { _ecnBroker = isEcn; return &this; } OrderBuilder *SetSide(const OrderSide orderSide) { _orderSide = orderSide; return &this; } OrderBuilder *SetInstrument(const string instrument) { _instrument = instrument; return &this; } OrderBuilder *SetAmount(const double amount) { _amount = amount; return &this; } OrderBuilder *SetRate(const double rate) { _rate = rate; return &this; } OrderBuilder *SetSlippage(const int slippage) { _slippage = slippage; return &this; } OrderBuilder *SetStopLoss(const double stop) { _stopLoss = stop; return &this; } OrderBuilder *SetTakeProfit(const double limit) { _takeProfit = limit; return &this; } OrderBuilder *SetMagicNumber(const int magicNumber) { _magicNumber = magicNumber; return &this; } OrderBuilder *SetComment(const string comment) { _comment = comment; return &this; } int GetOrderType() { if (_orderType != -1) { return _orderType; } if (_orderSide == BuySide) { return _rate > InstrumentInfo::GetAsk(_instrument) ? OP_BUYSTOP : OP_BUYLIMIT; } return _rate < InstrumentInfo::GetBid(_instrument) ? OP_SELLSTOP : OP_SELLLIMIT; } int Execute(string &errorMessage) { InstrumentInfo instrument(_instrument); double rate = instrument.RoundRate(_rate); double sl = instrument.RoundRate(_stopLoss); double tp = instrument.RoundRate(_takeProfit); int orderType = GetOrderType(); int order; if (_ecnBroker) order = OrderSend(_instrument, orderType, _amount, rate, _slippage, 0, 0, _comment, _magicNumber); else order = OrderSend(_instrument, orderType, _amount, rate, _slippage, sl, tp, _comment, _magicNumber); if (order == -1) { int error = GetLastError(); switch (error) { case ERR_OFF_QUOTES: errorMessage = "No quotes"; return -1; case ERR_NOT_ENOUGH_MONEY: errorMessage = "Not enough money"; break; case ERR_TRADE_NOT_ALLOWED: errorMessage = "Trading is not allowed"; break; case ERR_TRADE_TOO_MANY_ORDERS: errorMessage = "Too many orders opened"; break; case ERR_INVALID_STOPS: { double point = SymbolInfoDouble(_instrument, SYMBOL_POINT); int minStopDistancePoints = (int)SymbolInfoInteger(_instrument, SYMBOL_TRADE_STOPS_LEVEL); if (_stopLoss != 0.0) { if (MathRound(MathAbs(rate - _stopLoss) / point) < minStopDistancePoints) errorMessage = "Your stop loss level is too close. The minimal distance allowed is " + IntegerToString(minStopDistancePoints) + " points"; else errorMessage = "Invalid stop loss in the request. Do you have ECN broker and forget to enable ECN?"; } else if (_takeProfit != 0.0) { if (MathRound(MathAbs(rate - _takeProfit) / point) < minStopDistancePoints) errorMessage = "Your take profit level is too close. The minimal distance allowed is " + IntegerToString(minStopDistancePoints) + " points"; else errorMessage = "Invalid take profit in the request. Do you have ECN broker and forget to enable ECN?"; } else errorMessage = "Invalid stop loss or take profit in the request. Do you have ECN broker and forget to enable ECN?"; } break; case ERR_INVALID_TRADE_PARAMETERS: errorMessage = "Incorrect trade parameters. Symbol: " + _instrument + " Order type: " + IntegerToString(orderType) + " Amount: " + DoubleToString(_amount) + " Rate: " + DoubleToString(rate) + " Slippage: " + DoubleToString(_slippage) + " SL: " + DoubleToString(sl) + " TP: " + DoubleToString(tp) + " Comment: " + _comment == NULL ? "" : _comment + " Magic number: " + IntegerToString(_magicNumber); break; default: errorMessage = "Failed to create order: " + IntegerToString(error); break; } } else if (_ecnBroker && (_stopLoss != 0 || _takeProfit != 0)) { NoStopLossOrTakeProfitCondition* condition = new NoStopLossOrTakeProfitCondition(order); SetStopLossAndTakeProfitAction* action = new SetStopLossAndTakeProfitAction(_stopLoss, _takeProfit, order); _actions.AddActionOnCondition(action, condition); condition.Release(); action.Release(); } return order; } }; #endif class AOrderAction : public AAction { protected: int _currentTicket; public: virtual bool DoAction(int ticket) { _currentTicket = ticket; return DoAction(0, 0); } }; interface IOrder { public: virtual void AddRef() = 0; virtual void Release() = 0; virtual bool Select() = 0; }; class OrderByMagicNumber : public IOrder { int _magicNumber; int _references; public: OrderByMagicNumber(int magicNumber) { _magicNumber = magicNumber; _references = 1; } void AddRef() { ++_references; } void Release() { --_references; if (_references == 0) delete &this; } virtual bool Select() { OrdersIterator it(); it.WhenMagicNumber(_magicNumber); int ticketId = it.First(); return OrderSelect(ticketId, SELECT_BY_TICKET, MODE_TRADES); } }; class ProfitInRangeCondition : public AConditionBase { IOrder* _order; InstrumentInfo* _instrument; double _minProfit; double _maxProfit; public: ProfitInRangeCondition(IOrder* order, double minProfit, double maxProfit) { _order = order; _order.AddRef(); _minProfit = minProfit; _maxProfit = maxProfit; _instrument = NULL; } ~ProfitInRangeCondition() { _order.Release(); delete _instrument; } virtual bool IsPass(const int period, const datetime date) { if (!_order.Select()) return true; string symbol = OrderSymbol(); if (_instrument == NULL) _instrument = new InstrumentInfo(symbol); double closePrice = iClose(symbol, PERIOD_M1, 0); int orderType = OrderType(); if (orderType == OP_BUY) { double profit = (closePrice - OrderOpenPrice()) / _instrument.GetPipSize(); return profit >= _minProfit && profit <= _maxProfit; } else if (orderType == OP_SELL) { double profit = (OrderOpenPrice() - closePrice) / _instrument.GetPipSize(); return profit >= _minProfit && profit <= _maxProfit; } return false; } }; class OrderByTicketId : public IOrder { int _ticket; int _references; public: OrderByTicketId(int ticket) { _ticket = ticket; _references = 1; } void AddRef() { ++_references; } void Release() { --_references; if (_references == 0) delete &this; } virtual bool Select() { return OrderSelect(_ticket, SELECT_BY_TICKET, MODE_TRADES); } }; class TrailingPipsAction : public AAction { IOrder* _order; InstrumentInfo* _instrument; double _distancePips; double _stepPips; double _distance; double _step; public: TrailingPipsAction(IOrder* order, double distancePips, double stepPips) { _distancePips = distancePips; _stepPips = stepPips; _distance = 0; _step = 0; _order = order; _order.AddRef(); _instrument = NULL; } ~TrailingPipsAction() { _order.Release(); delete _instrument; } virtual bool DoAction(const int period, const datetime date) { if (!_order.Select()) return true; string symbol = OrderSymbol(); double closePrice = iClose(symbol, PERIOD_M1, 0); if (_step == 0) { _instrument = new InstrumentInfo(symbol); _distance = _distancePips * _instrument.GetPipSize(); _step = _stepPips * _instrument.GetPipSize(); } double newStop = GetNewStopLoss(closePrice); if (newStop == 0.0) return false; string error; TradingCommands::MoveSL(OrderTicket(), newStop, error); return false; } private: double GetNewStopLoss(double closePrice) { double stopLoss = OrderStopLoss(); if (stopLoss == 0.0) return 0; double newStop = stopLoss; int orderType = OrderType(); if (orderType == OP_BUY) { while (_instrument.RoundRate(newStop + _step) < _instrument.RoundRate(closePrice - _distance)) { newStop = _instrument.RoundRate(newStop + _step); } if (newStop == stopLoss) return 0; } else if (orderType == OP_SELL) { while (_instrument.RoundRate(newStop - _step) > _instrument.RoundRate(closePrice + _distance)) { newStop = _instrument.RoundRate(newStop - _step); } if (newStop == stopLoss) return 0; } else return 0; return newStop; } }; class CreateTrailingAction : public AOrderAction { double _start; double _step; ActionOnConditionLogic* _actions; public: CreateTrailingAction(double start, double step, ActionOnConditionLogic* actions) { _start = start; _step = step; _actions = actions; } void RestoreActions(string symbol, int magicNumber) { OrdersIterator trades; trades.WhenSymbol(symbol).WhenTrade().WhenMagicNumber(magicNumber); while (trades.Next()) { int ticketId = trades.GetTicket(); string ticketIdStr = IntegerToString(ticketId); double step = GlobalVariableGet("tr_" + ticketIdStr + "_stp"); if (step == 0) { continue; } double start = GlobalVariableGet("tr_" + ticketIdStr + "_strt"); if (start == 0) { continue; } double distance = GlobalVariableGet("tr_" + ticketIdStr + "_d"); if (distance == 0) { continue; } OrderByTicketId* order = new OrderByTicketId(ticketId); TrailingPipsAction* action = new TrailingPipsAction(order, distance, step); ProfitInRangeCondition* condition = new ProfitInRangeCondition(order, start, 100000); _actions.AddActionOnCondition(action, condition); condition.Release(); action.Release(); order.Release(); } } virtual bool DoAction(const int period, const datetime date) { OrderByTicketId* order = new OrderByTicketId(_currentTicket); if (!order.Select() || OrderStopLoss() == 0) { order.Release(); return false; } double point = MarketInfo(OrderSymbol(), MODE_POINT); int digits = (int)MarketInfo(OrderSymbol(), MODE_DIGITS); int mult = digits == 3 || digits == 5 ? 10 : 1; double pipSize = point * mult; double distance = (OrderOpenPrice() - OrderStopLoss()) / pipSize; string ticketIdStr = IntegerToString(_currentTicket); GlobalVariableSet("tr_" + ticketIdStr + "_stp", _step); GlobalVariableSet("tr_" + ticketIdStr + "_strt", _start); GlobalVariableSet("tr_" + ticketIdStr + "_d", distance); TrailingPipsAction* action = new TrailingPipsAction(order, distance, _step); ProfitInRangeCondition* condition = new ProfitInRangeCondition(order, _start, 100000); _actions.AddActionOnCondition(action, condition); condition.Release(); action.Release(); order.Release(); return true; } }; TradingMonitor tradingMonitor; ActionOnConditionLogic* actions; CreateTrailingAction* trailingAction; int OnInit() { actions = new ActionOnConditionLogic(); DeletePendingOrdersAction* onNewTradeAction = new DeletePendingOrdersAction(magic_number, slippage); tradingMonitor.SetOnNewTrade(onNewTradeAction); tradingMonitor.SetOnTradeChanged(onNewTradeAction); onNewTradeAction.Release(); trailingAction = new CreateTrailingAction(trailing_start, trailing_step, actions); trailingAction.RestoreActions(_Symbol, magic_number); return INIT_SUCCEEDED; } void OnDeinit(const int reason) { delete trailingAction; delete actions; } bool first = true; void OnTick() { tradingMonitor.DoWork(); actions.DoLogic(0, 0); if (first) { double point = MarketInfo(_Symbol, MODE_POINT); int digits = (int)MarketInfo(_Symbol, MODE_DIGITS); int mult = digits == 3 || digits == 5 ? 10 : 1; double pipSize = point * mult; first = false; string error; OrderBuilder* orderBuilder; double entryPrice; int order; if (trade_side != ShortSideOnly) { orderBuilder = new OrderBuilder(actions); double price = entry_price == 0 ? iClose(_Symbol, PERIOD_D1, 1) : entry_price; entryPrice = price + distance * pipSize; order = orderBuilder .SetRate(entryPrice) .SetECNBroker(ecn_broker) .SetSide(BuySide) .SetInstrument(_Symbol) .SetAmount(amount) .SetSlippage(slippage) .SetMagicNumber(magic_number) .SetStopLoss(entryPrice - stop_loss * pipSize) .SetTakeProfit(entryPrice + take_profit * pipSize) .Execute(error); delete orderBuilder; if (order <= 0) { Print(error); return; } trailingAction.DoAction(order); } if (trade_side != LongSideOnly) { orderBuilder = new OrderBuilder(actions); double price = entry_price == 0 ? iClose(_Symbol, PERIOD_D1, 1) : entry_price; entryPrice = price - distance * pipSize; order = orderBuilder .SetRate(entryPrice) .SetECNBroker(ecn_broker) .SetSide(SellSide) .SetInstrument(_Symbol) .SetAmount(amount) .SetSlippage(slippage) .SetMagicNumber(magic_number) .SetStopLoss(entryPrice + stop_loss * pipSize) .SetTakeProfit(entryPrice - take_profit * pipSize) .Execute(error); delete orderBuilder; if (order <= 0) { Print(error); return; } trailingAction.DoAction(order); } } }