//+------------------------------------------------------------------+ //| TradeCustom.mqh | //| Copyright 2016, Vasiliy Sokolov, St-Petersburg, Russia | //| https://www.mql5.com/ru/users/c-4 | //+------------------------------------------------------------------+ #property copyright "Copyright 2016, Vasiliy Sokolov." #property link "https://www.mql5.com/ru/users/c-4" #include #include #include #include #include #include //+------------------------------------------------------------------+ //| enumerations | //+------------------------------------------------------------------+ enum ENUM_LOG_LEVELS_CUST { LOG_LEVEL_CUST_NO =0, LOG_LEVEL_CUST_ERRORS=1, LOG_LEVEL_CUST_ALL =2 }; //+------------------------------------------------------------------+ //| Class CTradeCustom. | //| Appointment: Class trade operations. | //| Derives from class CObject. | //+------------------------------------------------------------------+ class CTradeCustom : public CObject { protected: MqlTradeRequest m_request; // request data MqlTradeResult m_result; // result data MqlTradeCheckResult m_check_result; // result check data bool m_async_mode; // trade mode ulong m_magic; // expert magic number ulong m_deviation; // deviation default ENUM_ORDER_TYPE_FILLING m_type_filling; //--- ENUM_LOG_LEVELS_CUST m_log_level; bool PositionOpen(const string symbol,const ENUM_ORDER_TYPE order_type,const double volume, const double price,const double sl,const double tp,const string comment=""); bool PositionClose(const string symbol,const ulong deviation=ULONG_MAX); public: CTradeCustom(void); ~CTradeCustom(void); //--- methods of access to protected data void LogLevel(const ENUM_LOG_LEVELS_CUST log_level) { m_log_level=log_level; } void Request(MqlTradeRequest &request) const; ENUM_TRADE_REQUEST_ACTIONS RequestAction(void) const { return(m_request.action); } string RequestActionDescription(void) const; ulong RequestMagic(void) const { return(m_request.magic); } ulong RequestOrder(void) const { return(m_request.order); } string RequestSymbol(void) const { return(m_request.symbol); } double RequestVolume(void) const { return(m_request.volume); } double RequestPrice(void) const { return(m_request.price); } double RequestStopLimit(void) const { return(m_request.stoplimit); } double RequestSL(void) const { return(m_request.sl); } double RequestTP(void) const { return(m_request.tp); } ulong RequestDeviation(void) const { return(m_request.deviation); } ENUM_ORDER_TYPE RequestType(void) const { return(m_request.type); } string RequestTypeDescription(void) const; ENUM_ORDER_TYPE_FILLING RequestTypeFilling(void) const { return(m_request.type_filling); } string RequestTypeFillingDescription(void) const; ENUM_ORDER_TYPE_TIME RequestTypeTime(void) const { return(m_request.type_time); } string RequestTypeTimeDescription(void) const; datetime RequestExpiration(void) const { return(m_request.expiration); } string RequestComment(void) const { return(m_request.comment); } //--- void Result(MqlTradeResult &result) const; uint ResultRetcode(void) const { return(m_result.retcode); } string ResultRetcodeDescription(void) const; ulong ResultDeal(void) const { return(m_result.deal); } ulong ResultOrder(void) const { return(m_result.order); } double ResultVolume(void) const { return(m_result.volume); } double ResultPrice(void) const { return(m_result.price); } double ResultBid(void) const { return(m_result.bid); } double ResultAsk(void) const { return(m_result.ask); } string ResultComment(void) const { return(m_result.comment); } //--- void CheckResult(MqlTradeCheckResult &check_result) const; uint CheckResultRetcode(void) const { return(m_check_result.retcode); } string CheckResultRetcodeDescription(void) const; double CheckResultBalance(void) const { return(m_check_result.balance); } double CheckResultEquity(void) const { return(m_check_result.equity); } double CheckResultProfit(void) const { return(m_check_result.profit); } double CheckResultMargin(void) const { return(m_check_result.margin); } double CheckResultMarginFree(void) const { return(m_check_result.margin_free); } double CheckResultMarginLevel(void) const { return(m_check_result.margin_level); } string CheckResultComment(void) const { return(m_check_result.comment); } //--- trade methods void SetAsyncMode(const bool mode) { m_async_mode=mode; } void SetExpertMagicNumber(const ulong magic) { m_magic=magic; } void SetDeviationInPoints(const ulong deviation) { m_deviation=deviation; } void SetTypeFilling(const ENUM_ORDER_TYPE_FILLING filling) { m_type_filling=filling; } //--- methods for working with positions //--- methods for working with pending orders bool OrderOpen(const string symbol,const ENUM_ORDER_TYPE order_type,const double volume, const double limit_price,const double price, ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC,const datetime expiration=0, const string comment=""); bool OrderModify(const ulong ticket,const double price, const ENUM_ORDER_TYPE_TIME type_time,const datetime expiration,const double stoplimit=0.0); bool OrderDelete(const ulong ticket); //--- additions methods virtual bool Buy(const double volume,const string symbol=NULL,const string comment=""); virtual bool Sell(const double volume,const string symbol=NULL,const string comment=""); virtual bool BuyLimit(const double volume,const double price,const string symbol=NULL,const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC, const datetime expiration=0,const string comment=""); virtual bool BuyStop(const double volume,const double price,const string symbol=NULL, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC,const datetime expiration=0,const string comment=""); virtual bool SellLimit(const double volume,const double price,const string symbol=NULL, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC,const datetime expiration=0,const string comment=""); virtual bool SellStop(const double volume,const double price,const string symbol=NULL, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC,const datetime expiration=0,const string comment=""); //--- method check virtual bool OrderCheck(const MqlTradeRequest &request,MqlTradeCheckResult &check_result); virtual bool OrderSend(const MqlTradeRequest &request,MqlTradeResult &result); //--- info methods void PrintRequest(void) const; void PrintResult(void) const; //--- positions string FormatPositionType(string &str,const uint type) const; //--- orders string FormatOrderType(string &str,const uint type) const; string FormatOrderStatus(string &str,const uint status) const; string FormatOrderTypeFilling(string &str,const uint type) const; string FormatOrderTypeTime(string &str,const uint type) const; string FormatOrderPrice(string &str,const double price_order,const double price_trigger,const uint digits) const; //--- trade request string FormatRequest(string &str,const MqlTradeRequest &request) const; string FormatRequestResult(string &str,const MqlTradeRequest &request,const MqlTradeResult &result) const; protected: bool FillingCheck(const string symbol); bool ExpirationCheck(const string symbol); bool OrderTypeCheck(const string symbol); void ClearStructures(void); bool IsStopped(const string function); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CTradeCustom::CTradeCustom(void) : m_async_mode(false), m_magic(0), m_deviation(10), m_type_filling(ORDER_FILLING_FOK), m_log_level(LOG_LEVEL_CUST_ERRORS) { //--- initialize protected data ClearStructures(); //--- check program mode if(MQL5InfoInteger(MQL5_OPTIMIZATION)) m_log_level=LOG_LEVEL_CUST_NO; if(MQL5InfoInteger(MQL5_TESTING)) m_log_level=LOG_LEVEL_CUST_ALL; } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTradeCustom::~CTradeCustom(void) { } //+------------------------------------------------------------------+ //| Get the request structure | //+------------------------------------------------------------------+ void CTradeCustom::Request(MqlTradeRequest &request) const { request.action =m_request.action; request.magic =m_request.magic; request.order =m_request.order; request.symbol =m_request.symbol; request.volume =m_request.volume; request.price =m_request.price; request.stoplimit =m_request.stoplimit; request.sl =m_request.sl; request.tp =m_request.tp; request.deviation =m_request.deviation; request.type =m_request.type; request.type_filling=m_request.type_filling; request.type_time =m_request.type_time; request.expiration =m_request.expiration; request.comment =m_request.comment; } //+------------------------------------------------------------------+ //| Get the trade action as string | //+------------------------------------------------------------------+ string CTradeCustom::RequestActionDescription(void) const { string str; //--- FormatRequest(str,m_request); //--- return(str); } //+------------------------------------------------------------------+ //| Get the order type as string | //+------------------------------------------------------------------+ string CTradeCustom::RequestTypeDescription(void) const { string str; //--- FormatOrderType(str,(uint)m_request.order); //--- return(str); } //+------------------------------------------------------------------+ //| Get the order type filling as string | //+------------------------------------------------------------------+ string CTradeCustom::RequestTypeFillingDescription(void) const { string str; //--- FormatOrderTypeFilling(str,m_request.type_filling); //--- return(str); } //+------------------------------------------------------------------+ //| Get the order type time as string | //+------------------------------------------------------------------+ string CTradeCustom::RequestTypeTimeDescription(void) const { string str; //--- FormatOrderTypeTime(str,m_request.type_time); //--- return(str); } //+------------------------------------------------------------------+ //| Get the result structure | //+------------------------------------------------------------------+ void CTradeCustom::Result(MqlTradeResult &result) const { result.retcode =m_result.retcode; result.deal =m_result.deal; result.order =m_result.order; result.volume =m_result.volume; result.price =m_result.price; result.bid =m_result.bid; result.ask =m_result.ask; result.comment =m_result.comment; result.request_id=m_result.request_id; } //+------------------------------------------------------------------+ //| Get the retcode value as string | //+------------------------------------------------------------------+ string CTradeCustom::ResultRetcodeDescription(void) const { string str; //--- FormatRequestResult(str,m_request,m_result); //--- return(str); } //+------------------------------------------------------------------+ //| Get the check result structure | //+------------------------------------------------------------------+ void CTradeCustom::CheckResult(MqlTradeCheckResult &check_result) const { //--- copy structure check_result.retcode =m_check_result.retcode; check_result.balance =m_check_result.balance; check_result.equity =m_check_result.equity; check_result.profit =m_check_result.profit; check_result.margin =m_check_result.margin; check_result.margin_free =m_check_result.margin_free; check_result.margin_level=m_check_result.margin_level; check_result.comment =m_check_result.comment; } //+------------------------------------------------------------------+ //| Get the check retcode value as string | //+------------------------------------------------------------------+ string CTradeCustom::CheckResultRetcodeDescription(void) const { string str; MqlTradeResult result; //--- result.retcode=m_check_result.retcode; FormatRequestResult(str,m_request,result); //--- return(str); } //+------------------------------------------------------------------+ //| Open position | //+------------------------------------------------------------------+ bool CTradeCustom::PositionOpen(const string symbol,const ENUM_ORDER_TYPE order_type,const double volume, const double price,const double sl,const double tp,const string comment) { //--- check stopped if(IsStopped(__FUNCTION__)) return(false); //--- clean ClearStructures(); //--- check if(order_type!=ORDER_TYPE_BUY && order_type!=ORDER_TYPE_SELL) { m_result.retcode=TRADE_RETCODE_INVALID; m_result.comment="Invalid order type"; return(false); } //--- setting request m_request.action =TRADE_ACTION_DEAL; m_request.symbol =symbol; m_request.magic =m_magic; m_request.volume =volume; m_request.type =order_type; m_request.price =price; m_request.sl =sl; m_request.tp =tp; m_request.deviation =m_deviation; //--- check order type if(!OrderTypeCheck(symbol)) return(false); //--- check filling if(!FillingCheck(symbol)) return(false); m_request.comment=comment; //--- action and return the result return(OrderSend(m_request,m_result)); } //+------------------------------------------------------------------+ //| Close specified opened position | //+------------------------------------------------------------------+ bool CTradeCustom::PositionClose(const string symbol,const ulong deviation) { bool partial_close=false; int retry_count =10; uint retcode =TRADE_RETCODE_REJECT; //--- check stopped if(IsStopped(__FUNCTION__)) return(false); //--- clean ClearStructures(); do { //--- check if(PositionSelect(symbol)) { if((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY) { //--- prepare request for close BUY position m_request.type =ORDER_TYPE_SELL; m_request.price=SymbolInfoDouble(symbol,SYMBOL_BID); } else { //--- prepare request for close SELL position m_request.type =ORDER_TYPE_BUY; m_request.price=SymbolInfoDouble(symbol,SYMBOL_ASK); } } else { //--- position not found m_result.retcode=retcode; return(false); } //--- setting request m_request.action =TRADE_ACTION_DEAL; m_request.symbol =symbol; m_request.magic =m_magic; m_request.deviation =(deviation==ULONG_MAX) ? m_deviation : deviation; //--- check filling if(!FillingCheck(symbol)) return(false); m_request.volume=PositionGetDouble(POSITION_VOLUME); //--- check volume double max_volume=SymbolInfoDouble(symbol,SYMBOL_VOLUME_MAX); if(m_request.volume>max_volume) { m_request.volume=max_volume; partial_close=true; } else partial_close=false; //--- order send if(!OrderSend(m_request,m_result)) { if(--retry_count!=0) continue; if(retcode==TRADE_RETCODE_DONE_PARTIAL) m_result.retcode=retcode; return(false); } //--- WARNING. If position volume exceeds the maximum volume allowed for deal, //--- and when the asynchronous trade mode is on, for safety reasons, position is closed not completely, //--- but partially. It is decreased by the maximum volume allowed for deal. if(m_async_mode) break; retcode=TRADE_RETCODE_DONE_PARTIAL; if(partial_close) Sleep(1000); } while(partial_close); //--- succeed return(true); } //+------------------------------------------------------------------+ //| Installation pending order | //+------------------------------------------------------------------+ bool CTradeCustom::OrderOpen(const string symbol,const ENUM_ORDER_TYPE order_type,const double volume,const double limit_price, const double price, ENUM_ORDER_TYPE_TIME type_time,const datetime expiration,const string comment) { //--- check stopped if(IsStopped(__FUNCTION__)) return(false); //--- clean ClearStructures(); //--- check order type if(order_type==ORDER_TYPE_BUY || order_type==ORDER_TYPE_SELL) { m_result.retcode=TRADE_RETCODE_INVALID; m_result.comment="Invalid order type"; return(false); } //--- check order expiration if(type_time==ORDER_TIME_GTC && expiration==0) { int exp=(int)SymbolInfoInteger(symbol,SYMBOL_EXPIRATION_MODE); if((exp&SYMBOL_EXPIRATION_GTC)!=SYMBOL_EXPIRATION_GTC) { //--- if you place order for an unlimited time and if placing of such orders is prohibited //--- try to place order with expiration at the end of the day if((exp&SYMBOL_EXPIRATION_DAY)!=SYMBOL_EXPIRATION_DAY) { //--- if even this is not possible - error Print(__FUNCTION__,": Error: Unable to place order without explicitly specified expiration time"); m_result.retcode=TRADE_RETCODE_INVALID_EXPIRATION; m_result.comment="Invalid expiration type"; return(false); } type_time=ORDER_TIME_DAY; } } //--- setting request m_request.action =TRADE_ACTION_PENDING; m_request.symbol =symbol; m_request.magic =m_magic; m_request.volume =volume; m_request.type =order_type; m_request.stoplimit =limit_price; m_request.price =price; m_request.sl =0.0; m_request.tp =0.0; m_request.type_filling=m_type_filling; m_request.type_time =type_time; m_request.expiration =expiration; //--- check order type if(!OrderTypeCheck(symbol)) return(false); //--- check filling if(!FillingCheck(symbol)) { m_result.retcode=TRADE_RETCODE_INVALID_FILL; Print(__FUNCTION__+": Invalid filling type"); return(false); } //--- check expiration if(!ExpirationCheck(symbol)) { m_result.retcode=TRADE_RETCODE_INVALID_EXPIRATION; Print(__FUNCTION__+": Invalid expiration type"); return(false); } m_request.comment=comment; //--- action and return the result return(OrderSend(m_request,m_result)); } //+------------------------------------------------------------------+ //| Modify specified pending order | //+------------------------------------------------------------------+ bool CTradeCustom::OrderModify(const ulong ticket,const double price, const ENUM_ORDER_TYPE_TIME type_time,const datetime expiration,const double stoplimit) { //--- check stopped if(IsStopped(__FUNCTION__)) return(false); //--- clean ClearStructures(); //--- setting request m_request.action =TRADE_ACTION_MODIFY; m_request.magic =m_magic; m_request.order =ticket; m_request.price =price; m_request.stoplimit =stoplimit; m_request.sl =0.0; m_request.tp =0.0; m_request.type_time =type_time; m_request.expiration =expiration; //--- action and return the result return(OrderSend(m_request,m_result)); } //+------------------------------------------------------------------+ //| Delete specified pending order | //+------------------------------------------------------------------+ bool CTradeCustom::OrderDelete(const ulong ticket) { //--- check stopped if(IsStopped(__FUNCTION__)) return(false); //--- clean ClearStructures(); //--- setting request m_request.action =TRADE_ACTION_REMOVE; m_request.magic =m_magic; m_request.order =ticket; //--- action and return the result return(OrderSend(m_request,m_result)); } //+------------------------------------------------------------------+ //| Output full information of request to log | //+------------------------------------------------------------------+ void CTradeCustom::PrintRequest(void) const { if(m_log_levelLOG_LEVEL_CUST_ERRORS) printf(__FUNCTION__+": %s [%s]",FormatRequest(action,request),FormatRequestResult(fmt,request,result)); } else { if(m_log_level>LOG_LEVEL_CUST_NO) printf(__FUNCTION__+": %s [%s]",FormatRequest(action,request),FormatRequestResult(fmt,request,result)); } //--- return the result return(res); } //+------------------------------------------------------------------+