MobinMQL/Include/Trade/Trade.mqh
2025-07-22 14:47:41 +03:00

1708 lines
66 KiB
MQL5

//+------------------------------------------------------------------+
//| Trade.mqh |
//| Copyright 2000-2025, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#include <Object.mqh>
#include "OrderInfo.mqh"
#include "HistoryOrderInfo.mqh"
#include "PositionInfo.mqh"
#include "DealInfo.mqh"
//+------------------------------------------------------------------+
//| enumerations |
//+------------------------------------------------------------------+
enum ENUM_LOG_LEVELS
{
LOG_LEVEL_NO =0,
LOG_LEVEL_ERRORS=1,
LOG_LEVEL_ALL =2
};
//+------------------------------------------------------------------+
//| Class CTrade. |
//| Appointment: Class trade operations. |
//| Derives from class CObject. |
//+------------------------------------------------------------------+
class CTrade : 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_ACCOUNT_MARGIN_MODE m_margin_mode;
//---
ENUM_LOG_LEVELS m_log_level;
public:
CTrade(void);
~CTrade(void);
//--- methods of access to protected data
void LogLevel(const ENUM_LOG_LEVELS 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); }
ulong RequestPosition(void) const { return(m_request.position); }
ulong RequestPositionBy(void) const { return(m_request.position_by); }
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;
int ResultRetcodeExternal(void) const { return(m_result.retcode_external); }
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; }
bool SetTypeFillingBySymbol(const string symbol);
void SetMarginMode(void) { m_margin_mode=(ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE); }
//--- methods for working with positions
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 PositionModify(const string symbol,const double sl,const double tp);
bool PositionModify(const ulong ticket,const double sl,const double tp);
bool PositionClose(const string symbol,const ulong deviation=ULONG_MAX);
bool PositionClose(const ulong ticket,const ulong deviation=ULONG_MAX);
bool PositionCloseBy(const ulong ticket,const ulong ticket_by);
bool PositionClosePartial(const string symbol,const double volume,const ulong deviation=ULONG_MAX);
bool PositionClosePartial(const ulong ticket,const double volume,const ulong deviation=ULONG_MAX);
//--- 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,const double sl,const double tp,
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 double sl,const double tp,
const ENUM_ORDER_TYPE_TIME type_time,const datetime expiration,const double stoplimit=0.0);
bool OrderDelete(const ulong ticket);
//--- additions methods
bool Buy(const double volume,const string symbol=NULL,double price=0.0,const double sl=0.0,const double tp=0.0,const string comment="");
bool Sell(const double volume,const string symbol=NULL,double price=0.0,const double sl=0.0,const double tp=0.0,const string comment="");
bool BuyLimit(const double volume,const double price,const string symbol=NULL,const double sl=0.0,const double tp=0.0,
const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC,const datetime expiration=0,const string comment="");
bool BuyStop(const double volume,const double price,const string symbol=NULL,const double sl=0.0,const double tp=0.0,
const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC,const datetime expiration=0,const string comment="");
bool SellLimit(const double volume,const double price,const string symbol=NULL,const double sl=0.0,const double tp=0.0,
const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC,const datetime expiration=0,const string comment="");
bool SellStop(const double volume,const double price,const string symbol=NULL,const double sl=0.0,const double tp=0.0,
const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC,const datetime expiration=0,const string comment="");
//--- method check
virtual double CheckVolume(const string symbol,double volume,double price,ENUM_ORDER_TYPE order_type);
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);
bool IsHedging(void) const { return(m_margin_mode==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING); }
//--- position select depending on netting or hedging
bool SelectPosition(const string symbol);
};
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CTrade::CTrade(void) : m_async_mode(false),
m_magic(0),
m_deviation(10),
m_type_filling(ORDER_FILLING_FOK),
m_log_level(LOG_LEVEL_ERRORS)
{
SetMarginMode();
//--- initialize protected data
ClearStructures();
//--- check programm mode
if(MQLInfoInteger(ENUM_MQL_INFO_INTEGER::MQL_TESTER))
m_log_level=LOG_LEVEL_ALL;
if(MQLInfoInteger(ENUM_MQL_INFO_INTEGER::MQL_OPTIMIZATION))
m_log_level=LOG_LEVEL_NO;
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTrade::~CTrade(void)
{
}
//+------------------------------------------------------------------+
//| Get the request structure |
//+------------------------------------------------------------------+
void CTrade::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;
request.position =m_request.position;
request.position_by =m_request.position_by;
}
//+------------------------------------------------------------------+
//| Get the trade action as string |
//+------------------------------------------------------------------+
string CTrade::RequestActionDescription(void) const
{
string str;
//---
FormatRequest(str,m_request);
//---
return(str);
}
//+------------------------------------------------------------------+
//| Get the order type as string |
//+------------------------------------------------------------------+
string CTrade::RequestTypeDescription(void) const
{
string str;
//---
FormatOrderType(str,(uint)m_request.order);
//---
return(str);
}
//+------------------------------------------------------------------+
//| Get the order type filling as string |
//+------------------------------------------------------------------+
string CTrade::RequestTypeFillingDescription(void) const
{
string str;
//---
FormatOrderTypeFilling(str,m_request.type_filling);
//---
return(str);
}
//+------------------------------------------------------------------+
//| Get the order type time as string |
//+------------------------------------------------------------------+
string CTrade::RequestTypeTimeDescription(void) const
{
string str;
//---
FormatOrderTypeTime(str,m_request.type_time);
//---
return(str);
}
//+------------------------------------------------------------------+
//| Get the result structure |
//+------------------------------------------------------------------+
void CTrade::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;
result.retcode_external=m_result.retcode_external;
}
//+------------------------------------------------------------------+
//| Get the retcode value as string |
//+------------------------------------------------------------------+
string CTrade::ResultRetcodeDescription(void) const
{
string str;
//---
FormatRequestResult(str,m_request,m_result);
//---
return(str);
}
//+------------------------------------------------------------------+
//| Get the check result structure |
//+------------------------------------------------------------------+
void CTrade::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 CTrade::CheckResultRetcodeDescription(void) const
{
string str;
MqlTradeResult result;
//---
result.retcode=m_check_result.retcode;
FormatRequestResult(str,m_request,result);
//---
return(str);
}
//+------------------------------------------------------------------+
//| Open position |
//+------------------------------------------------------------------+
bool CTrade::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));
}
//+------------------------------------------------------------------+
//| Modify specified opened position |
//+------------------------------------------------------------------+
bool CTrade::PositionModify(const string symbol,const double sl,const double tp)
{
//--- check stopped
if(IsStopped(__FUNCTION__))
return(false);
//--- check position existence
if(!SelectPosition(symbol))
return(false);
//--- clean
ClearStructures();
//--- setting request
m_request.action =TRADE_ACTION_SLTP;
m_request.symbol =symbol;
m_request.magic =m_magic;
m_request.sl =sl;
m_request.tp =tp;
m_request.position=PositionGetInteger(POSITION_TICKET);
//--- action and return the result
return(OrderSend(m_request,m_result));
}
//+------------------------------------------------------------------+
//| Modify specified opened position |
//+------------------------------------------------------------------+
bool CTrade::PositionModify(const ulong ticket,const double sl,const double tp)
{
//--- check stopped
if(IsStopped(__FUNCTION__))
return(false);
//--- check position existence
if(!PositionSelectByTicket(ticket))
return(false);
//--- clean
ClearStructures();
//--- setting request
m_request.action =TRADE_ACTION_SLTP;
m_request.position=ticket;
m_request.symbol =PositionGetString(POSITION_SYMBOL);
m_request.magic =m_magic;
m_request.sl =sl;
m_request.tp =tp;
//--- action and return the result
return(OrderSend(m_request,m_result));
}
//+------------------------------------------------------------------+
//| Close specified opened position |
//+------------------------------------------------------------------+
bool CTrade::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();
//--- check filling
if(!FillingCheck(symbol))
return(false);
do
{
//--- check
if(SelectPosition(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.volume =PositionGetDouble(POSITION_VOLUME);
m_request.magic =m_magic;
m_request.deviation=(deviation==ULONG_MAX) ? m_deviation : deviation;
m_request.position =PositionGetInteger(POSITION_TICKET);
//--- 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;
//--- hedging? just send order
if(IsHedging())
return(OrderSend(m_request,m_result));
//--- 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);
}
//+------------------------------------------------------------------+
//| Close specified opened position |
//+------------------------------------------------------------------+
bool CTrade::PositionClose(const ulong ticket,const ulong deviation)
{
//--- check stopped
if(IsStopped(__FUNCTION__))
return(false);
//--- check position existence
if(!PositionSelectByTicket(ticket))
return(false);
string symbol=PositionGetString(POSITION_SYMBOL);
//--- clean
ClearStructures();
//--- check filling
if(!FillingCheck(symbol))
return(false);
//--- check
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);
}
//--- setting request
m_request.action =TRADE_ACTION_DEAL;
m_request.position =ticket;
m_request.symbol =symbol;
m_request.volume =PositionGetDouble(POSITION_VOLUME);
m_request.magic =m_magic;
m_request.deviation=(deviation==ULONG_MAX) ? m_deviation : deviation;
//--- close position
return(OrderSend(m_request,m_result));
}
//+------------------------------------------------------------------+
//| Close one position by other |
//+------------------------------------------------------------------+
bool CTrade::PositionCloseBy(const ulong ticket,const ulong ticket_by)
{
//--- check stopped
if(IsStopped(__FUNCTION__))
return(false);
//--- check hedging mode
if(!IsHedging())
return(false);
//--- check position existence
if(!PositionSelectByTicket(ticket))
return(false);
string symbol=PositionGetString(POSITION_SYMBOL);
ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if(!PositionSelectByTicket(ticket_by))
return(false);
string symbol_by=PositionGetString(POSITION_SYMBOL);
ENUM_POSITION_TYPE type_by=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
//--- check positions
if(type==type_by)
return(false);
if(symbol!=symbol_by)
return(false);
//--- clean
ClearStructures();
//--- check filling
if(!FillingCheck(symbol))
return(false);
//--- setting request
m_request.action =TRADE_ACTION_CLOSE_BY;
m_request.position =ticket;
m_request.position_by=ticket_by;
m_request.magic =m_magic;
//--- close position
return(OrderSend(m_request,m_result));
}
//+------------------------------------------------------------------+
//| Partial close specified opened position (for hedging mode only) |
//+------------------------------------------------------------------+
bool CTrade::PositionClosePartial(const string symbol,const double volume,const ulong deviation)
{
uint retcode=TRADE_RETCODE_REJECT;
//--- check stopped
if(IsStopped(__FUNCTION__))
return(false);
//--- for hedging mode only
if(!IsHedging())
return(false);
//--- clean
ClearStructures();
//--- check filling
if(!FillingCheck(symbol))
return(false);
//--- check
if(SelectPosition(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);
}
//--- check volume
double position_volume=PositionGetDouble(POSITION_VOLUME);
if(position_volume>volume)
position_volume=volume;
//--- setting request
m_request.action =TRADE_ACTION_DEAL;
m_request.symbol =symbol;
m_request.volume =position_volume;
m_request.magic =m_magic;
m_request.deviation=(deviation==ULONG_MAX) ? m_deviation : deviation;
m_request.position =PositionGetInteger(POSITION_TICKET);
//--- hedging? just send order
return(OrderSend(m_request,m_result));
}
//+------------------------------------------------------------------+
//| Partial close specified opened position (for hedging mode only) |
//+------------------------------------------------------------------+
bool CTrade::PositionClosePartial(const ulong ticket,const double volume,const ulong deviation)
{
//--- check stopped
if(IsStopped(__FUNCTION__))
return(false);
//--- for hedging mode only
if(!IsHedging())
return(false);
//--- check position existence
if(!PositionSelectByTicket(ticket))
return(false);
string symbol=PositionGetString(POSITION_SYMBOL);
//--- clean
ClearStructures();
//--- check filling
if(!FillingCheck(symbol))
return(false);
//--- check
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);
}
//--- check volume
double position_volume=PositionGetDouble(POSITION_VOLUME);
if(position_volume>volume)
position_volume=volume;
//--- setting request
m_request.action =TRADE_ACTION_DEAL;
m_request.position =ticket;
m_request.symbol =symbol;
m_request.volume =position_volume;
m_request.magic =m_magic;
m_request.deviation=(deviation==ULONG_MAX) ? m_deviation : deviation;
//--- close position
return(OrderSend(m_request,m_result));
}
//+------------------------------------------------------------------+
//| Installation pending order |
//+------------------------------------------------------------------+
bool CTrade::OrderOpen(const string symbol,const ENUM_ORDER_TYPE order_type,const double volume,const double limit_price,
const double price,const double sl,const double tp,
ENUM_ORDER_TYPE_TIME type_time,const datetime expiration,const string comment)
{
//--- check stopped
if(IsStopped(__FUNCTION__))
return(false);
//--- clean
ClearStructures();
//--- check filling
if(!FillingCheck(symbol))
return(false);
//--- 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 =sl;
m_request.tp =tp;
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 CTrade::OrderModify(const ulong ticket,const double price,const double sl,const double tp,
const ENUM_ORDER_TYPE_TIME type_time,const datetime expiration,const double stoplimit)
{
//--- check stopped
if(IsStopped(__FUNCTION__))
return(false);
//--- check order existence
if(!OrderSelect(ticket))
return(false);
//--- clean
ClearStructures();
//--- setting request
m_request.symbol =OrderGetString(ORDER_SYMBOL);
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 =sl;
m_request.tp =tp;
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 CTrade::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 CTrade::PrintRequest(void) const
{
if(m_log_level<LOG_LEVEL_ALL)
return;
//---
string str;
PrintFormat("%s",FormatRequest(str,m_request));
}
//+------------------------------------------------------------------+
//| Output full information of result to log |
//+------------------------------------------------------------------+
void CTrade::PrintResult(void) const
{
if(m_log_level<LOG_LEVEL_ALL)
return;
//---
string str;
PrintFormat("%s",FormatRequestResult(str,m_request,m_result));
}
//+------------------------------------------------------------------+
//| Clear structures m_request,m_result and m_check_result |
//+------------------------------------------------------------------+
void CTrade::ClearStructures(void)
{
ZeroMemory(m_request);
ZeroMemory(m_result);
ZeroMemory(m_check_result);
}
//+------------------------------------------------------------------+
//| Checks forced shutdown of MQL5-program |
//+------------------------------------------------------------------+
bool CTrade::IsStopped(const string function)
{
if(!::IsStopped())
return(false);
//--- MQL5 program is stopped
PrintFormat("%s: MQL5 program is stopped. Trading is disabled",function);
m_result.retcode=TRADE_RETCODE_CLIENT_DISABLES_AT;
return(true);
}
//+------------------------------------------------------------------+
//| Buy operation |
//+------------------------------------------------------------------+
bool CTrade::Buy(const double volume,const string symbol=NULL,double price=0.0,const double sl=0.0,const double tp=0.0,const string comment="")
{
//--- check volume
if(volume<=0.0)
{
m_result.retcode=TRADE_RETCODE_INVALID_VOLUME;
return(false);
}
//--- check symbol
string symbol_name=(symbol==NULL) ? _Symbol : symbol;
//--- check price
if(price==0.0)
price=SymbolInfoDouble(symbol_name,SYMBOL_ASK);
//---
return(PositionOpen(symbol_name,ORDER_TYPE_BUY,volume,price,sl,tp,comment));
}
//+------------------------------------------------------------------+
//| Sell operation |
//+------------------------------------------------------------------+
bool CTrade::Sell(const double volume,const string symbol=NULL,double price=0.0,const double sl=0.0,const double tp=0.0,const string comment="")
{
//--- check volume
if(volume<=0.0)
{
m_result.retcode=TRADE_RETCODE_INVALID_VOLUME;
return(false);
}
//--- check symbol
string symbol_name=(symbol==NULL) ? _Symbol : symbol;
//--- check price
if(price==0.0)
price=SymbolInfoDouble(symbol_name,SYMBOL_BID);
//---
return(PositionOpen(symbol_name,ORDER_TYPE_SELL,volume,price,sl,tp,comment));
}
//+------------------------------------------------------------------+
//| Send BUY_LIMIT order |
//+------------------------------------------------------------------+
bool CTrade::BuyLimit(const double volume,const double price,const string symbol=NULL,const double sl=0.0,const double tp=0.0,
const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC,const datetime expiration=0,const string comment="")
{
string symbol_name;
//--- check volume
if(volume<=0.0)
{
m_result.retcode=TRADE_RETCODE_INVALID_VOLUME;
return(false);
}
//--- check symbol
symbol_name=(symbol==NULL)?Symbol():symbol;
//--- send "BUY_LIMIT" order
return(OrderOpen(symbol_name,ORDER_TYPE_BUY_LIMIT,volume,0.0,price,sl,tp,type_time,expiration,comment));
}
//+------------------------------------------------------------------+
//| Send BUY_STOP order |
//+------------------------------------------------------------------+
bool CTrade::BuyStop(const double volume,const double price,const string symbol=NULL,const double sl=0.0,const double tp=0.0,
const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC,const datetime expiration=0,const string comment="")
{
string symbol_name;
//--- check volume
if(volume<=0.0)
{
m_result.retcode=TRADE_RETCODE_INVALID_VOLUME;
return(false);
}
//--- check symbol
symbol_name=(symbol==NULL)?Symbol():symbol;
//--- send "BUY_STOP" order
return(OrderOpen(symbol_name,ORDER_TYPE_BUY_STOP,volume,0.0,price,sl,tp,type_time,expiration,comment));
}
//+------------------------------------------------------------------+
//| Send SELL_LIMIT order |
//+------------------------------------------------------------------+
bool CTrade::SellLimit(const double volume,const double price,const string symbol=NULL,const double sl=0.0,const double tp=0.0,
const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC,const datetime expiration=0,const string comment="")
{
string symbol_name;
//--- check volume
if(volume<=0.0)
{
m_result.retcode=TRADE_RETCODE_INVALID_VOLUME;
return(false);
}
//--- check symbol
symbol_name=(symbol==NULL)?Symbol():symbol;
//--- send "SELL_LIMIT" order
return(OrderOpen(symbol_name,ORDER_TYPE_SELL_LIMIT,volume,0.0,price,sl,tp,type_time,expiration,comment));
}
//+------------------------------------------------------------------+
//| Send SELL_STOP order |
//+------------------------------------------------------------------+
bool CTrade::SellStop(const double volume,const double price,const string symbol=NULL,const double sl=0.0,const double tp=0.0,
const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC,const datetime expiration=0,const string comment="")
{
string symbol_name;
//--- check volume
if(volume<=0.0)
{
m_result.retcode=TRADE_RETCODE_INVALID_VOLUME;
return(false);
}
//--- check symbol
symbol_name=(symbol==NULL)?Symbol():symbol;
//--- send "SELL_STOP" order
return(OrderOpen(symbol_name,ORDER_TYPE_SELL_STOP,volume,0.0,price,sl,tp,type_time,expiration,comment));
}
//+------------------------------------------------------------------+
//| Converts the position type to text |
//+------------------------------------------------------------------+
string CTrade::FormatPositionType(string &str,const uint type) const
{
//--- see the type
switch(type)
{
case POSITION_TYPE_BUY:
str="buy";
break;
case POSITION_TYPE_SELL:
str="sell";
break;
default:
str="unknown position type "+(string)type;
}
//--- return the result
return(str);
}
//+------------------------------------------------------------------+
//| Converts the order type to text |
//+------------------------------------------------------------------+
string CTrade::FormatOrderType(string &str,const uint type) const
{
//--- see the type
switch(type)
{
case ORDER_TYPE_BUY:
str="buy";
break;
case ORDER_TYPE_SELL:
str="sell";
break;
case ORDER_TYPE_BUY_LIMIT:
str="buy limit";
break;
case ORDER_TYPE_SELL_LIMIT:
str="sell limit";
break;
case ORDER_TYPE_BUY_STOP:
str="buy stop";
break;
case ORDER_TYPE_SELL_STOP:
str="sell stop";
break;
case ORDER_TYPE_BUY_STOP_LIMIT:
str="buy stop limit";
break;
case ORDER_TYPE_SELL_STOP_LIMIT:
str="sell stop limit";
break;
default:
str="unknown order type "+(string)type;
}
//--- return the result
return(str);
}
//+------------------------------------------------------------------+
//| Converts the order filling type to text |
//+------------------------------------------------------------------+
string CTrade::FormatOrderTypeFilling(string &str,const uint type) const
{
//--- see the type
switch(type)
{
case ORDER_FILLING_RETURN:
str="return remainder";
break;
case ORDER_FILLING_IOC:
str="cancel remainder";
break;
case ORDER_FILLING_FOK:
str="fill or kill";
break;
default:
str="unknown type filling "+(string)type;
break;
}
//--- return the result
return(str);
}
//+------------------------------------------------------------------+
//| Converts the type of order by expiration to text |
//+------------------------------------------------------------------+
string CTrade::FormatOrderTypeTime(string &str,const uint type) const
{
//--- see the type
switch(type)
{
case ORDER_TIME_GTC:
str="gtc";
break;
case ORDER_TIME_DAY:
str="day";
break;
case ORDER_TIME_SPECIFIED:
str="specified";
break;
case ORDER_TIME_SPECIFIED_DAY:
str="specified day";
break;
default:
str="unknown type time "+(string)type;
}
//--- return the result
return(str);
}
//+------------------------------------------------------------------+
//| Converts the order prices to text |
//+------------------------------------------------------------------+
string CTrade::FormatOrderPrice(string &str,const double price_order,const double price_trigger,const uint digits) const
{
string price,trigger;
//--- Is there its trigger price?
if(price_trigger)
{
price =DoubleToString(price_order,digits);
trigger=DoubleToString(price_trigger,digits);
str =StringFormat("%s (%s)",price,trigger);
}
else
str=DoubleToString(price_order,digits);
//--- return the result
return(str);
}
//+------------------------------------------------------------------+
//| Converts the parameters of a trade request to text |
//+------------------------------------------------------------------+
string CTrade::FormatRequest(string &str,const MqlTradeRequest &request) const
{
string type,price,price_new;
string tmp_string;
long tmp_long;
//--- clean
str="";
//--- set up
string symbol_name=(request.symbol==NULL) ? _Symbol : request.symbol;
int digits=_Digits;
ENUM_SYMBOL_TRADE_EXECUTION trade_execution=0;
if(SymbolInfoInteger(symbol_name,SYMBOL_DIGITS,tmp_long))
digits=(int)tmp_long;
if(SymbolInfoInteger(symbol_name,SYMBOL_TRADE_EXEMODE,tmp_long))
trade_execution=(ENUM_SYMBOL_TRADE_EXECUTION)tmp_long;
//--- see what is wanted
switch(request.action)
{
//--- instant execution of a deal
case TRADE_ACTION_DEAL:
switch(trade_execution)
{
//--- request execution
case SYMBOL_TRADE_EXECUTION_REQUEST:
if(IsHedging() && request.position!=0)
str=StringFormat("request %s %s position #%I64u %s at %s",
FormatOrderType(type,request.type),
DoubleToString(request.volume,2),
request.position,
request.symbol,
DoubleToString(request.price,digits));
else
str=StringFormat("request %s %s %s at %s",
FormatOrderType(type,request.type),
DoubleToString(request.volume,2),
request.symbol,
DoubleToString(request.price,digits));
//--- Is there SL or TP?
if(request.sl!=0.0)
{
tmp_string=StringFormat(" sl: %s",DoubleToString(request.sl,digits));
str+=tmp_string;
}
if(request.tp!=0.0)
{
tmp_string=StringFormat(" tp: %s",DoubleToString(request.tp,digits));
str+=tmp_string;
}
break;
//--- instant execution
case SYMBOL_TRADE_EXECUTION_INSTANT:
if(IsHedging() && request.position!=0)
str=StringFormat("instant %s %s position #%I64u %s at %s",
FormatOrderType(type,request.type),
DoubleToString(request.volume,2),
request.position,
request.symbol,
DoubleToString(request.price,digits));
else
str=StringFormat("instant %s %s %s at %s",
FormatOrderType(type,request.type),
DoubleToString(request.volume,2),
request.symbol,
DoubleToString(request.price,digits));
//--- Is there SL or TP?
if(request.sl!=0.0)
{
tmp_string=StringFormat(" sl: %s",DoubleToString(request.sl,digits));
str+=tmp_string;
}
if(request.tp!=0.0)
{
tmp_string=StringFormat(" tp: %s",DoubleToString(request.tp,digits));
str+=tmp_string;
}
break;
//--- market execution
case SYMBOL_TRADE_EXECUTION_MARKET:
if(IsHedging() && request.position!=0)
str=StringFormat("market %s %s position #%I64u %s",
FormatOrderType(type,request.type),
DoubleToString(request.volume,2),
request.position,
request.symbol);
else
str=StringFormat("market %s %s %s",
FormatOrderType(type,request.type),
DoubleToString(request.volume,2),
request.symbol);
//--- Is there SL or TP?
if(request.sl!=0.0)
{
tmp_string=StringFormat(" sl: %s",DoubleToString(request.sl,digits));
str+=tmp_string;
}
if(request.tp!=0.0)
{
tmp_string=StringFormat(" tp: %s",DoubleToString(request.tp,digits));
str+=tmp_string;
}
break;
//--- exchange execution
case SYMBOL_TRADE_EXECUTION_EXCHANGE:
if(IsHedging() && request.position!=0)
str=StringFormat("exchange %s %s position #%I64u %s",
FormatOrderType(type,request.type),
DoubleToString(request.volume,2),
request.position,
request.symbol);
else
str=StringFormat("exchange %s %s %s",
FormatOrderType(type,request.type),
DoubleToString(request.volume,2),
request.symbol);
//--- Is there SL or TP?
if(request.sl!=0.0)
{
tmp_string=StringFormat(" sl: %s",DoubleToString(request.sl,digits));
str+=tmp_string;
}
if(request.tp!=0.0)
{
tmp_string=StringFormat(" tp: %s",DoubleToString(request.tp,digits));
str+=tmp_string;
}
break;
}
//--- end of TRADE_ACTION_DEAL processing
break;
//--- setting a pending order
case TRADE_ACTION_PENDING:
str=StringFormat("%s %s %s at %s",
FormatOrderType(type,request.type),
DoubleToString(request.volume,2),
request.symbol,
FormatOrderPrice(price,request.price,request.stoplimit,digits));
//--- Is there SL or TP?
if(request.sl!=0.0)
{
tmp_string=StringFormat(" sl: %s",DoubleToString(request.sl,digits));
str+=tmp_string;
}
if(request.tp!=0.0)
{
tmp_string=StringFormat(" tp: %s",DoubleToString(request.tp,digits));
str+=tmp_string;
}
break;
//--- Setting SL/TP
case TRADE_ACTION_SLTP:
if(IsHedging() && request.position!=0)
str=StringFormat("modify position #%I64u %s (sl: %s, tp: %s)",
request.position,
request.symbol,
DoubleToString(request.sl,digits),
DoubleToString(request.tp,digits));
else
str=StringFormat("modify %s (sl: %s, tp: %s)",
request.symbol,
DoubleToString(request.sl,digits),
DoubleToString(request.tp,digits));
break;
//--- modifying a pending order
case TRADE_ACTION_MODIFY:
str=StringFormat("modify #%I64u at %s (sl: %s tp: %s)",
request.order,
FormatOrderPrice(price_new,request.price,request.stoplimit,digits),
DoubleToString(request.sl,digits),
DoubleToString(request.tp,digits));
break;
//--- deleting a pending order
case TRADE_ACTION_REMOVE:
str=StringFormat("cancel #%I64u",request.order);
break;
//--- close by
case TRADE_ACTION_CLOSE_BY:
if(IsHedging() && request.position!=0)
str=StringFormat("close position #%I64u by #%I64u",request.position,request.position_by);
else
str=StringFormat("wrong action close by (#%I64u by #%I64u)",request.position,request.position_by);
break;
default:
str="unknown action "+(string)request.action;
break;
}
//--- return the result
return(str);
}
//+------------------------------------------------------------------+
//| Converts the result of a request to text |
//+------------------------------------------------------------------+
string CTrade::FormatRequestResult(string &str,const MqlTradeRequest &request,const MqlTradeResult &result) const
{
//--- set up
string symbol_name=(request.symbol==NULL) ? _Symbol : request.symbol;
int digits=_Digits;
long tmp_long;
ENUM_SYMBOL_TRADE_EXECUTION trade_execution=0;
if(SymbolInfoInteger(symbol_name,SYMBOL_DIGITS,tmp_long))
digits=(int)tmp_long;
if(SymbolInfoInteger(symbol_name,SYMBOL_TRADE_EXEMODE,tmp_long))
trade_execution=(ENUM_SYMBOL_TRADE_EXECUTION)tmp_long;
//--- see the response code
switch(result.retcode)
{
case TRADE_RETCODE_REQUOTE:
str=StringFormat("requote (%s/%s)",
DoubleToString(result.bid,digits),
DoubleToString(result.ask,digits));
break;
case TRADE_RETCODE_DONE:
if(request.action==TRADE_ACTION_DEAL &&
(trade_execution==SYMBOL_TRADE_EXECUTION_REQUEST ||
trade_execution==SYMBOL_TRADE_EXECUTION_INSTANT ||
trade_execution==SYMBOL_TRADE_EXECUTION_MARKET))
str=StringFormat("done at %s",DoubleToString(result.price,digits));
else
str="done";
break;
case TRADE_RETCODE_DONE_PARTIAL:
if(request.action==TRADE_ACTION_DEAL &&
(trade_execution==SYMBOL_TRADE_EXECUTION_REQUEST ||
trade_execution==SYMBOL_TRADE_EXECUTION_INSTANT ||
trade_execution==SYMBOL_TRADE_EXECUTION_MARKET))
str=StringFormat("done partially %s at %s",
DoubleToString(result.volume,2),
DoubleToString(result.price,digits));
else
str=StringFormat("done partially %s",
DoubleToString(result.volume,2));
break;
case TRADE_RETCODE_REJECT:
str="rejected";
break;
case TRADE_RETCODE_CANCEL:
str="canceled";
break;
case TRADE_RETCODE_PLACED:
str="placed";
break;
case TRADE_RETCODE_ERROR:
str="common error";
break;
case TRADE_RETCODE_TIMEOUT:
str="timeout";
break;
case TRADE_RETCODE_INVALID:
str="invalid request";
break;
case TRADE_RETCODE_INVALID_VOLUME:
str="invalid volume";
break;
case TRADE_RETCODE_INVALID_PRICE:
str="invalid price";
break;
case TRADE_RETCODE_INVALID_STOPS:
str="invalid stops";
break;
case TRADE_RETCODE_TRADE_DISABLED:
str="trade disabled";
break;
case TRADE_RETCODE_MARKET_CLOSED:
str="market closed";
break;
case TRADE_RETCODE_NO_MONEY:
str="not enough money";
break;
case TRADE_RETCODE_PRICE_CHANGED:
str="price changed";
break;
case TRADE_RETCODE_PRICE_OFF:
str="off quotes";
break;
case TRADE_RETCODE_INVALID_EXPIRATION:
str="invalid expiration";
break;
case TRADE_RETCODE_ORDER_CHANGED:
str="order changed";
break;
case TRADE_RETCODE_TOO_MANY_REQUESTS:
str="too many requests";
break;
case TRADE_RETCODE_NO_CHANGES:
str="no changes";
break;
case TRADE_RETCODE_SERVER_DISABLES_AT:
str="auto trading disabled by server";
break;
case TRADE_RETCODE_CLIENT_DISABLES_AT:
str="auto trading disabled by client";
break;
case TRADE_RETCODE_LOCKED:
str="locked";
break;
case TRADE_RETCODE_FROZEN:
str="frozen";
break;
case TRADE_RETCODE_INVALID_FILL:
str="invalid fill";
break;
case TRADE_RETCODE_CONNECTION:
str="no connection";
break;
case TRADE_RETCODE_ONLY_REAL:
str="only real";
break;
case TRADE_RETCODE_LIMIT_ORDERS:
str="limit orders";
break;
case TRADE_RETCODE_LIMIT_VOLUME:
str="limit volume";
break;
case TRADE_RETCODE_POSITION_CLOSED:
str="position closed";
break;
case TRADE_RETCODE_INVALID_ORDER:
str="invalid order";
break;
case TRADE_RETCODE_CLOSE_ORDER_EXIST:
str="close order already exists";
break;
case TRADE_RETCODE_LIMIT_POSITIONS:
str="limit positions";
break;
default:
str="unknown retcode "+(string)result.retcode;
}
//--- return the result
return(str);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double CTrade::CheckVolume(const string symbol,double volume,double price,ENUM_ORDER_TYPE order_type)
{
//--- check
if(order_type!=ORDER_TYPE_BUY && order_type!=ORDER_TYPE_SELL)
return(0.0);
double free_margin=AccountInfoDouble(ENUM_ACCOUNT_INFO_DOUBLE::ACCOUNT_MARGIN_FREE);
if(free_margin<=0.0)
return(0.0);
//--- clean
ClearStructures();
//--- setting request
m_request.action=TRADE_ACTION_DEAL;
m_request.symbol=symbol;
m_request.volume=volume;
m_request.type =order_type;
m_request.price =price;
//--- action and return the result
if(!::OrderCheck(m_request,m_check_result) && m_check_result.margin_free<0.0)
{
double coeff=free_margin/(free_margin-m_check_result.margin_free);
double lots=NormalizeDouble(volume*coeff,2);
if(lots<volume)
{
//--- normalize and check limits
double stepvol=SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP);
if(stepvol>0.0)
volume=stepvol*(MathFloor(lots/stepvol)-1);
//---
double minvol=SymbolInfoDouble(symbol,SYMBOL_VOLUME_MIN);
if(volume<minvol)
volume=0.0;
}
}
return(volume);
}
//+------------------------------------------------------------------+
//| Checks if the m_request structure is filled correctly |
//+------------------------------------------------------------------+
bool CTrade::OrderCheck(const MqlTradeRequest &request,MqlTradeCheckResult &check_result)
{
//--- action and return the result
return(::OrderCheck(request,check_result));
}
//+------------------------------------------------------------------+
//| Set order filling type according to symbol filling mode |
//+------------------------------------------------------------------+
bool CTrade::SetTypeFillingBySymbol(const string symbol)
{
//--- get possible filling policy types by symbol
uint filling=(uint)SymbolInfoInteger(symbol,SYMBOL_FILLING_MODE);
if((filling&SYMBOL_FILLING_FOK)==SYMBOL_FILLING_FOK)
{
m_type_filling=ORDER_FILLING_FOK;
return(true);
}
if((filling&SYMBOL_FILLING_IOC)==SYMBOL_FILLING_IOC)
{
m_type_filling=ORDER_FILLING_IOC;
return(true);
}
//---
return(false);
}
//+------------------------------------------------------------------+
//| Checks and corrects type of filling policy |
//+------------------------------------------------------------------+
bool CTrade::FillingCheck(const string symbol)
{
//--- get execution mode of orders by symbol
ENUM_SYMBOL_TRADE_EXECUTION exec=(ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(symbol,SYMBOL_TRADE_EXEMODE);
//--- check execution mode
if(exec==SYMBOL_TRADE_EXECUTION_REQUEST || exec==SYMBOL_TRADE_EXECUTION_INSTANT)
{
//--- neccessary filling type will be placed automatically
return(true);
}
//--- get possible filling policy types by symbol
uint filling=(uint)SymbolInfoInteger(symbol,SYMBOL_FILLING_MODE);
//--- check execution mode again
if(exec==SYMBOL_TRADE_EXECUTION_MARKET)
{
//--- for the MARKET execution mode
//--- analyze order
if(m_request.action!=TRADE_ACTION_PENDING)
{
//--- in case of instant execution order
//--- if the required filling policy is supported, add it to the request
if((filling&SYMBOL_FILLING_FOK)==SYMBOL_FILLING_FOK)
{
m_type_filling=ORDER_FILLING_FOK;
m_request.type_filling=m_type_filling;
return(true);
}
if((filling&SYMBOL_FILLING_IOC)==SYMBOL_FILLING_IOC)
{
m_type_filling=ORDER_FILLING_IOC;
m_request.type_filling=m_type_filling;
return(true);
}
//--- wrong filling policy, set error code
m_result.retcode=TRADE_RETCODE_INVALID_FILL;
return(false);
}
return(true);
}
//--- EXCHANGE execution mode
switch(m_type_filling)
{
case ORDER_FILLING_FOK:
//--- analyze order
if(m_request.action==TRADE_ACTION_PENDING)
{
//--- in case of pending order
//--- add the expiration mode to the request
if(!ExpirationCheck(symbol))
m_request.type_time=ORDER_TIME_DAY;
//--- stop order?
if(m_request.type==ORDER_TYPE_BUY_STOP || m_request.type==ORDER_TYPE_SELL_STOP ||
m_request.type==ORDER_TYPE_BUY_LIMIT || m_request.type==ORDER_TYPE_SELL_LIMIT)
{
//--- in case of stop order
//--- add the corresponding filling policy to the request
m_request.type_filling=ORDER_FILLING_RETURN;
return(true);
}
}
//--- in case of limit order or instant execution order
//--- if the required filling policy is supported, add it to the request
if((filling&SYMBOL_FILLING_FOK)==SYMBOL_FILLING_FOK)
{
m_request.type_filling=m_type_filling;
return(true);
}
//--- wrong filling policy, set error code
m_result.retcode=TRADE_RETCODE_INVALID_FILL;
return(false);
case ORDER_FILLING_IOC:
//--- analyze order
if(m_request.action==TRADE_ACTION_PENDING)
{
//--- in case of pending order
//--- add the expiration mode to the request
if(!ExpirationCheck(symbol))
m_request.type_time=ORDER_TIME_DAY;
//--- stop order?
if(m_request.type==ORDER_TYPE_BUY_STOP || m_request.type==ORDER_TYPE_SELL_STOP ||
m_request.type==ORDER_TYPE_BUY_LIMIT || m_request.type==ORDER_TYPE_SELL_LIMIT)
{
//--- in case of stop order
//--- add the corresponding filling policy to the request
m_request.type_filling=ORDER_FILLING_RETURN;
return(true);
}
}
//--- in case of limit order or instant execution order
//--- if the required filling policy is supported, add it to the request
if((filling&SYMBOL_FILLING_IOC)==SYMBOL_FILLING_IOC)
{
m_request.type_filling=m_type_filling;
return(true);
}
//--- wrong filling policy, set error code
m_result.retcode=TRADE_RETCODE_INVALID_FILL;
return(false);
case ORDER_FILLING_RETURN:
//--- add filling policy to the request
m_request.type_filling=m_type_filling;
return(true);
}
//--- unknown execution mode, set error code
m_result.retcode=TRADE_RETCODE_ERROR;
return(false);
}
//+------------------------------------------------------------------+
//| Check expiration type of pending order |
//+------------------------------------------------------------------+
bool CTrade::ExpirationCheck(const string symbol)
{
//--- check symbol
string symbol_name=(symbol==NULL) ? _Symbol : symbol;
//--- get flags
long tmp_long;
int flags=0;
if(SymbolInfoInteger(symbol_name,SYMBOL_EXPIRATION_MODE,tmp_long))
flags=(int)tmp_long;
//--- check type
switch(m_request.type_time)
{
case ORDER_TIME_GTC:
if((flags&SYMBOL_EXPIRATION_GTC)!=0)
return(true);
break;
case ORDER_TIME_DAY:
if((flags&SYMBOL_EXPIRATION_DAY)!=0)
return(true);
break;
case ORDER_TIME_SPECIFIED:
if((flags&SYMBOL_EXPIRATION_SPECIFIED)!=0)
return(true);
break;
case ORDER_TIME_SPECIFIED_DAY:
if((flags&SYMBOL_EXPIRATION_SPECIFIED_DAY)!=0)
return(true);
break;
default:
Print(__FUNCTION__+": Unknown expiration type");
}
//--- failed
return(false);
}
//+------------------------------------------------------------------+
//| Checks order |
//+------------------------------------------------------------------+
bool CTrade::OrderTypeCheck(const string symbol)
{
bool res=false;
//--- check symbol
string symbol_name=(symbol==NULL) ? _Symbol : symbol;
//--- get flags of allowed trade orders
long tmp_long;
int flags=0;
if(SymbolInfoInteger(symbol_name,SYMBOL_ORDER_MODE,tmp_long))
flags=(int)tmp_long;
//--- depending on the type of order in request
switch(m_request.type)
{
case ORDER_TYPE_BUY:
case ORDER_TYPE_SELL:
//--- check possibility of execution
res=((flags&SYMBOL_ORDER_MARKET)!=0);
break;
case ORDER_TYPE_BUY_LIMIT:
case ORDER_TYPE_SELL_LIMIT:
//--- check possibility of execution
res=((flags&SYMBOL_ORDER_LIMIT)!=0);
break;
case ORDER_TYPE_BUY_STOP:
case ORDER_TYPE_SELL_STOP:
//--- check possibility of execution
res=((flags&SYMBOL_ORDER_STOP)!=0);
break;
case ORDER_TYPE_BUY_STOP_LIMIT:
case ORDER_TYPE_SELL_STOP_LIMIT:
//--- check possibility of execution
res=((flags&SYMBOL_ORDER_STOP_LIMIT)!=0);
break;
}
//--- check res
if(res)
{
//--- trading order is valid
//--- check if we need and able to set protective orders
if(m_request.sl!=0.0 || m_request.tp!=0.0)
{
if((flags&SYMBOL_ORDER_SL)==0)
m_request.sl=0.0;
if((flags&SYMBOL_ORDER_TP)==0)
m_request.tp=0.0;
}
}
else
{
//--- trading order is not valid
//--- set error
m_result.retcode=TRADE_RETCODE_INVALID_ORDER;
Print(__FUNCTION__+": Invalid order type");
}
//--- result
return(res);
}
//+------------------------------------------------------------------+
//| Send order |
//+------------------------------------------------------------------+
bool CTrade::OrderSend(const MqlTradeRequest &request,MqlTradeResult &result)
{
bool res;
string action="";
string fmt ="";
//--- action
if(m_async_mode)
res=::OrderSendAsync(request,result);
else
res=::OrderSend(request,result);
//--- check
if(res)
{
if(m_log_level>LOG_LEVEL_ERRORS)
PrintFormat(__FUNCTION__+": %s [%s]",FormatRequest(action,request),FormatRequestResult(fmt,request,result));
}
else
{
if(m_log_level>LOG_LEVEL_NO)
PrintFormat(__FUNCTION__+": %s [%s]",FormatRequest(action,request),FormatRequestResult(fmt,request,result));
}
//--- return the result
return(res);
}
//+------------------------------------------------------------------+
//| Position select depending on netting or hedging |
//+------------------------------------------------------------------+
bool CTrade::SelectPosition(const string symbol)
{
bool res=false;
//---
if(IsHedging())
{
uint total=PositionsTotal();
for(uint i=0; i<total; i++)
{
string position_symbol=PositionGetSymbol(i);
if(position_symbol==symbol && m_magic==PositionGetInteger(POSITION_MAGIC))
{
res=true;
break;
}
}
}
else
res=PositionSelect(symbol);
//---
return(res);
}
//+------------------------------------------------------------------+