//+------------------------------------------------------------------+ //| Position.mqh | //| Copyright 2015, Vasiliy Sokolov. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2015, Vasiliy Sokolov." #property link "http://www.mql5.com" #include #include "Message.mqh" #include "Logs.mqh" #include "Trailings\Trailing.mqh" //+------------------------------------------------------------------+ //| Класс активной позиции, для классических стратегий | //+------------------------------------------------------------------+ class CPosition : public CObject { private: ulong m_id; // Уникальный идентификатор позиции uint m_magic; // Уникальный идентификатор эксперта, к которому принадлежит данная позиция. ENUM_POSITION_TYPE m_direction; // Направление позиции double m_entry_price; // Цена входа в позицию string m_symbol; // Инструмент, по которому открыта позиция datetime m_time_open; // Время открытия string m_entry_comment; // Входящий комментарий bool m_is_closed; // Истина, если позиция была закрыта CLog* Log; // Логирование public: CPosition(void); bool IsActive(); uint ExpertMagic(void); ulong ID(void); ENUM_POSITION_TYPE Direction(void); double EntryPrice(void); string EntryComment(void); double Profit(void); double ProfitInPips(void); double Volume(void); string Symbol(void); datetime TimeOpen(void); bool CloseAtMarket(string comment=""); bool CloseAtMarket(double volume, ulong deviation, string comment); bool CloseAtMarket(double volume, ulong deviation, string comment, bool asynch = false); double StopLossValue(void); bool StopLossValue(double sl); double StopLossPercent(void); bool StopLossPercent(double sl); double TakeProfitValue(void); bool TakeProfitValue(double tp); double TakeProfitPercent(void); bool TakeProfitPercent(double tp); bool IsComplex(void); bool CheckStopLevel(double stoplevel); CTrailing* Trailing; CObject* ExpertData; }; CPosition::CPosition(void) : m_id(0), m_entry_price(0.0), m_symbol(""), m_time_open(0) { Log = CLog::GetLog(); #ifdef __MQL5__ #ifdef __HT__ m_id = HedgePositionGetInteger(HEDGE_POSITION_ID); m_magic = (uint)HedgePositionGetInteger(HEDGE_POSITION_MAGIC); ENUM_DIRECTION_TYPE type = (ENUM_DIRECTION_TYPE)HedgePositionGetInteger(HEDGE_POSITION_DIRECTION); m_direction = type == DIRECTION_LONG ? POSITION_TYPE_BUY : POSITION_TYPE_SELL; m_entry_price = HedgePositionGetDouble(HEDGE_POSITION_PRICE_OPEN); m_symbol = HedgePositionGetString(HEDGE_POSITION_SYMBOL); m_time_open = (datetime)HedgePositionGetInteger(HEDGE_POSITION_ENTRY_TIME_EXECUTED_MSC)/1000; m_entry_comment = HedgePositionGetString(HEDGE_POSITION_ENTRY_COMMENT); #else m_id = PositionGetInteger(POSITION_IDENTIFIER); m_maigic = (uint)PositionGetInteger(POSITION_MAGIC); m_direction = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); m_entry_price = PositionGetDouble(POSITION_PRICE_OPEN); m_symbol = PositionGetString(POSITION_SYMBOL); m_time_open = (datetime)PositionGetInteger(POSITION_TIME); m_entry_comment = PositionGetString(POSITION_COMMENT); #endif #else #ifdef __MQL4__ m_id = OrderTicket(); m_magic = OrderMagic(); if(OrderType() == ORDER_TYPE_BUY) m_direction = POSITION_TYPE_BUY; else m_direction = POSITION_TYPE_SELL; m_entry_price = OrderOpenPrice(); m_profit = OrderProfit(); m_volume = OrderLots(); m_symbol = OrderSymbol(); m_time_open = OrderOpenTime(); m_sl = OrderStopLoss(); m_tp = OrderTakeProfit(); m_entry_comment = OrderComment(); #endif #endif } //+------------------------------------------------------------------+ //| Возвращает истину, если текущая позиция активна. Возвращает ложь | //| в противном случае. | //+------------------------------------------------------------------+ bool CPosition::IsActive(void) { return m_time_open > 0 && !m_is_closed; } //+------------------------------------------------------------------+ //| Возвращает направление позиции. | //+------------------------------------------------------------------+ ENUM_POSITION_TYPE CPosition::Direction(void) { return m_direction; } //+------------------------------------------------------------------+ //| Возвращает уникальный идентификатор эксперта, которому | //| принадлежит текущая позиция. | //+------------------------------------------------------------------+ uint CPosition::ExpertMagic(void) { return m_magic; } //+------------------------------------------------------------------+ //| Возвращает уникальный идентификатор позиции. | //+------------------------------------------------------------------+ ulong CPosition::ID(void) { return m_id; } //+------------------------------------------------------------------+ //| Возвращает цену входа в позицию. | //+------------------------------------------------------------------+ double CPosition::EntryPrice(void) { return m_entry_price; } //+------------------------------------------------------------------+ //| Возвращает входящий комментарий активной позиции. | //+------------------------------------------------------------------+ string CPosition::EntryComment(void) { return m_entry_comment; } //+------------------------------------------------------------------+ //| Возвращает название инструмента, по которому открыта текущая | //| позиция. | //+------------------------------------------------------------------+ string CPosition::Symbol(void) { return m_symbol; } //+------------------------------------------------------------------+ //| Возвращает время открытия позиции. | //+------------------------------------------------------------------+ datetime CPosition::TimeOpen(void) { return m_time_open; } //+------------------------------------------------------------------+ //| Возвращает истину, если текущая позиция является частью | //| составной, рыночно-нейтральной комплексной позиции. Возвращает | //| ложь в противном случае. | //+------------------------------------------------------------------+ bool CPosition::IsComplex(void) { string comm = EntryComment(); string cp = StringSubstr(comm, 0, 3); if(cp != "CP[")return false; cp = StringSubstr(comm, StringLen(comm)-1, 1); if(cp != "]")return false; return true; } //+------------------------------------------------------------------+ //| Возвращает абсолютный уровень стоп-лосса для текущей позиции. | //| Если уровень стоп-лосса не установлен возвращает 0.0 | //+------------------------------------------------------------------+ double CPosition::StopLossValue(void) { double value = 0.0; ulong id = ID(); #ifdef __HT__ if(!TransactionSelect(ID(), SELECT_BY_TICKET, MODE_TRADES)) { string text = "Position #" + (string)ID() + " not find. Get StopLoss failed."; CMessage* msg_err = new CMessage(MESSAGE_ERROR, __FUNCTION__, text); Log.AddMessage(msg_err); } value = HedgePositionGetDouble(HEDGE_POSITION_SL); #endif return value; } //+------------------------------------------------------------------+ //| Устанавливает абсолютный уровень стоп-лосса | //+------------------------------------------------------------------+ bool CPosition::StopLossValue(double sl) { #ifdef __HT__ if(!TransactionSelect(ID(), SELECT_BY_TICKET, MODE_TRADES)) { string text = "Position #" + (string)ID() + " not find. Set StopLoss failed."; CMessage* msg_err = new CMessage(MESSAGE_ERROR, __FUNCTION__, text); Log.AddMessage(msg_err); return false; } double tp = HedgePositionGetDouble(HEDGE_POSITION_TP); HedgeTradeRequest request; request.action = REQUEST_MODIFY_SLTP; request.sl = sl; request.tp = tp; bool res = SendTradeRequest(request); if(res) { string text = "Position #" + (string)ID() + ": Set new S/L successfully at " + DoubleToString(sl); CMessage* msg_info = new CMessage(MESSAGE_INFO, __FUNCTION__, text); Log.AddMessage(msg_info); } else { string err = EnumToString(GetHedgeError()); string text = "Position #" + (string)ID() + ": Set new S/L failed. Reason: " + err; CMessage* msg_err = new CMessage(MESSAGE_INFO, __FUNCTION__, text); Log.AddMessage(msg_err); } return res; #endif } //+------------------------------------------------------------------+ //| Возвращает абсолютный уровень тейк-профита для текущей позиции. | //| Если уровень тейк-профита не установлен возвращает 0.0 | //+------------------------------------------------------------------+ double CPosition::TakeProfitValue(void) { double value = 0.0; #ifdef __HT__ if(!TransactionSelect(ID(), SELECT_BY_TICKET, MODE_TRADES)) { string text = "Position #" + (string)ID() + " not find. Get TakeProfit failed."; CMessage* msg_err = new CMessage(MESSAGE_ERROR, __FUNCTION__, text); Log.AddMessage(msg_err); } value = HedgePositionGetDouble(HEDGE_POSITION_TP); #endif return value; } //+------------------------------------------------------------------+ //| Устанавливает абсолютный уровень тейк-профита | //+------------------------------------------------------------------+ bool CPosition::TakeProfitValue(double tp) { #ifdef __HT__ if(!TransactionSelect(ID(), SELECT_BY_TICKET, MODE_TRADES)) { string text = "Position #" + (string)ID() + " not find. Set TakeProfit failed."; CMessage* msg_err = new CMessage(MESSAGE_ERROR, __FUNCTION__, text); Log.AddMessage(msg_err); return false; } double sl = HedgePositionGetDouble(HEDGE_POSITION_SL); HedgeTradeRequest request; request.action = REQUEST_MODIFY_SLTP; request.sl = sl; request.tp = tp; bool res = SendTradeRequest(request); if(res) { string text = "Position #" + (string)ID() + ": Set new T/P successfully at " + DoubleToString(tp); CMessage* msg_info = new CMessage(MESSAGE_INFO, __FUNCTION__, text); Log.AddMessage(msg_info); } else { string err = EnumToString(GetHedgeError()); string text = "Position #" + (string)ID() + ": Set new T/P failed. Reason: " + err; CMessage* msg_err = new CMessage(MESSAGE_INFO, __FUNCTION__, text); Log.AddMessage(msg_err); } return res; #endif } //+------------------------------------------------------------------+ //| Устанавливает стоп-лосс в процентах от текущей цены. | //| ПАРАМЕТРЫ: | //| sl_percent - Уровень в процентах от цены входа в позицию, | //| например sl_percent=3.2 = 3,2% от цены входа. | //+------------------------------------------------------------------+ bool CPosition::StopLossPercent(double sl_percent) { if(sl_percent < 0.0 || sl_percent > 100.0) { string text = "Position #" + (string)ID() + ": Set S/L must be greater than 0.0 and less than 100 %"; CMessage* msg = new CMessage(MESSAGE_WARNING, __FUNCTION__, text); Log.AddMessage(msg); return false; } double delta = EntryPrice()*sl_percent/100.0; double sl = 0.0; if(Direction() == POSITION_TYPE_BUY) sl = EntryPrice() - delta; else sl = EntryPrice() + delta; return StopLossValue(sl); } //+------------------------------------------------------------------+ //| Устанавливает тейк-профит в процентах от текущей цены. | //| ПАРАМЕТРЫ: | //| tp_percent - Уровень в процентах от цены входа в позицию, | //| например tp_percent=3.2 = 3,2% от цены входа. | //+------------------------------------------------------------------+ bool CPosition::TakeProfitPercent(double tp_percent) { if(tp_percent < 0.0 || tp_percent > 100.0) { string text = "Position #" + (string)ID() + ": Set T/P must be greater than 0.0 and less than 100 %"; CMessage* msg = new CMessage(MESSAGE_WARNING, __FUNCTION__, text); Log.AddMessage(msg); return false; } double delta = EntryPrice()*tp_percent/100.0; double tp = 0.0; if(Direction() == POSITION_TYPE_BUY) tp = EntryPrice() + delta; else tp = EntryPrice() - delta; return TakeProfitValue(tp); } //+------------------------------------------------------------------+ //| Закрывает текущую позицию по рынку, устанавливая закрывающий | //| комментарий равный comment | //+------------------------------------------------------------------+ bool CPosition::CloseAtMarket(string comment = "") { return CloseAtMarket(Volume(), 0, comment, false); } //+------------------------------------------------------------------+ //| Закрывает текущую позицию по рынку, устанавливая закрывающий | //| комментарий, объем и предельное отклонение цены. | //+------------------------------------------------------------------+ bool CPosition::CloseAtMarket(double volume, ulong deviation, string comment="") { return CloseAtMarket(Volume(), 0, comment, false); } //+------------------------------------------------------------------+ //| Закрывает текущую позицию по рынку. | //| Параметры: | //| volume - объем который требуется закрыть. Может быть равен или | //| меньше текущего объема позиции. | //| deviation - предельное отклонение цены в прайсстепах. | //| comment - Закрывающий комментарий. | //| Возвращаемое значение: Истина, если закрытие позиции прошло | //| успешно и ложь в противном случае. | //+------------------------------------------------------------------+ bool CPosition::CloseAtMarket(double volume, ulong deviation, string comment="", bool asynch=false) { #ifdef __HT__ if(!TransactionSelect(ID(), SELECT_BY_TICKET, MODE_TRADES)) { string text = "Position #" + (string)ID()+ "(ExpertMagic = "+ (string)ExpertMagic() + ") not find. Close at market failed."; CMessage* msg_err = new CMessage(MESSAGE_ERROR, __FUNCTION__, text); Log.AddMessage(msg_err); return false; } HedgeTradeRequest request; request.action = REQUEST_CLOSE_POSITION; request.volume = volume; request.exit_comment = comment; request.deviation = deviation; request.asynch_mode = asynch; bool res = SendTradeRequest(request); if(res) { string text = "Position #" + (string)ID()+ "(ExpertMagic = "+ (string)ExpertMagic() + ") was successfully closed"; CMessage* msg = new CMessage(MESSAGE_INFO, __FUNCTION__, text); Log.AddMessage(msg); } else { string text = "Position #" + (string)ID()+ "(ExpertMagic = "+ (string)ExpertMagic() + ") closed failed. Reason: " + EnumToString(GetHedgeError()); CMessage* msg = new CMessage(MESSAGE_WARNING, __FUNCTION__, text); Log.AddMessage(msg); } m_is_closed = res; return res; #endif return false; } //+------------------------------------------------------------------+ //| Возвращает текущий объем позиции. | //+------------------------------------------------------------------+ double CPosition::Volume(void) { #ifdef __HT__ if(!TransactionSelect(ID(), SELECT_BY_TICKET, MODE_TRADES)) { string text = "Position #" + (string)ID()+ "(ExpertMagic = "+ (string)ExpertMagic() + ") not find. Get volume failed."; CMessage* msg_err = new CMessage(MESSAGE_ERROR, __FUNCTION__, text); Log.AddMessage(msg_err); return 0.0; } double vol = HedgePositionGetDouble(HEDGE_POSITION_VOLUME); return vol; #endif } //+------------------------------------------------------------------+ //| Возвращает текущую прибыль позиции в валюте депозита. | //+------------------------------------------------------------------+ double CPosition::Profit(void) { #ifdef __HT__ if(!TransactionSelect(ID(), SELECT_BY_TICKET, MODE_TRADES)) { string text = "Position #" + (string)ID()+ "(ExpertMagic = "+ (string)ExpertMagic() + ") not find. Get profit in currency failed."; CMessage* msg_err = new CMessage(MESSAGE_ERROR, __FUNCTION__, text); Log.AddMessage(msg_err); return 0.0; } double profit = HedgePositionGetDouble(HEDGE_POSITION_PROFIT_CURRENCY); return profit; #endif } //+------------------------------------------------------------------+ //| Возвращает текущую прибыль в пунктах инструмента. | //+------------------------------------------------------------------+ double CPosition::ProfitInPips(void) { #ifdef __HT__ if(!TransactionSelect(ID(), SELECT_BY_TICKET, MODE_TRADES)) { string text = "Position #" + (string)ID()+ "(ExpertMagic = "+ (string)ExpertMagic() + ") not find. Get profit in pips failed."; CMessage* msg_err = new CMessage(MESSAGE_ERROR, __FUNCTION__, text); Log.AddMessage(msg_err); return 0.0; } double profit = HedgePositionGetDouble(HEDGE_POSITION_PROFIT_POINTS); return profit; #endif } //+------------------------------------------------------------------+ //| Проверяет корректность переданного уровня stoplevel. Возвращает | //| истину, если уровень SL является корректным и ложь в противном | //| случае. | //+------------------------------------------------------------------+ bool CPosition::CheckStopLevel(double stoplevel) { double last = 0.0; double max = SymbolInfoDouble(m_symbol, SYMBOL_SESSION_PRICE_LIMIT_MAX); double min = SymbolInfoDouble(m_symbol, SYMBOL_SESSION_PRICE_LIMIT_MIN); if(stoplevel >= max && max != 0.0) return false; if(stoplevel <= min) return false; if(m_direction == POSITION_TYPE_BUY) { if(stoplevel >= SymbolInfoDouble(m_symbol, SYMBOL_BID)) return false; return true; } else { if(stoplevel <= SymbolInfoDouble(m_symbol, SYMBOL_ASK)) return false; } return true; }