1270 lines
52 KiB
MQL5
1270 lines
52 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| 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 <Object.mqh>
|
|
#include <Trade\SymbolInfo.mqh>
|
|
#include <Trade\OrderInfo.mqh>
|
|
#include <Trade\HistoryOrderInfo.mqh>
|
|
#include <Trade\PositionInfo.mqh>
|
|
#include <Trade\DealInfo.mqh>
|
|
//+------------------------------------------------------------------+
|
|
//| 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_level<LOG_LEVEL_CUST_ALL)
|
|
return;
|
|
//---
|
|
string str;
|
|
printf("%s",FormatRequest(str,m_request));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Output full information of result to log |
|
|
//+------------------------------------------------------------------+
|
|
void CTradeCustom::PrintResult(void) const
|
|
{
|
|
if(m_log_level<LOG_LEVEL_CUST_ALL)
|
|
return;
|
|
//---
|
|
string str;
|
|
printf("%s",FormatRequestResult(str,m_request,m_result));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Clear structures m_request,m_result and m_check_result |
|
|
//+------------------------------------------------------------------+
|
|
void CTradeCustom::ClearStructures(void)
|
|
{
|
|
ZeroMemory(m_request);
|
|
ZeroMemory(m_result);
|
|
ZeroMemory(m_check_result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks forced shutdown of MQL5-program |
|
|
//+------------------------------------------------------------------+
|
|
bool CTradeCustom::IsStopped(const string function)
|
|
{
|
|
if(!IsStopped())
|
|
return(false);
|
|
//--- MQL5 program is stopped
|
|
printf("%s: MQL5 program is stopped. Trading is disabled",function);
|
|
m_result.retcode=TRADE_RETCODE_CLIENT_DISABLES_AT;
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Buy operation |
|
|
//+------------------------------------------------------------------+
|
|
bool CTradeCustom::Buy(const double volume,const string symbol=NULL,const string comment="")
|
|
{
|
|
CSymbolInfo sym;
|
|
//--- check volume
|
|
if(volume<=0.0)
|
|
{
|
|
m_result.retcode=TRADE_RETCODE_INVALID_VOLUME;
|
|
return(false);
|
|
}
|
|
//--- check symbol
|
|
sym.Name((symbol==NULL)?Symbol():symbol);
|
|
//--- check price
|
|
sym.RefreshRates();
|
|
double price=sym.Ask();
|
|
//---
|
|
return(PositionOpen(sym.Name(),ORDER_TYPE_BUY,volume,price,0.0,0.0,comment));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Sell operation |
|
|
//+------------------------------------------------------------------+
|
|
bool CTradeCustom::Sell(const double volume,const string symbol=NULL,const string comment="")
|
|
{
|
|
CSymbolInfo sym;
|
|
//--- check volume
|
|
if(volume<=0.0)
|
|
{
|
|
m_result.retcode=TRADE_RETCODE_INVALID_VOLUME;
|
|
return(false);
|
|
}
|
|
//--- check symbol
|
|
sym.Name((symbol==NULL)?Symbol():symbol);
|
|
//--- check price
|
|
sym.RefreshRates();
|
|
double price=sym.Bid();
|
|
//---
|
|
return(PositionOpen(sym.Name(),ORDER_TYPE_SELL,volume,price,0.0,0.0,comment));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Send BUY_LIMIT order |
|
|
//+------------------------------------------------------------------+
|
|
bool CTradeCustom::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="")
|
|
{
|
|
string sym;
|
|
//--- check volume
|
|
if(volume<=0.0)
|
|
{
|
|
m_result.retcode=TRADE_RETCODE_INVALID_VOLUME;
|
|
return(false);
|
|
}
|
|
//--- check price
|
|
if(price==0.0)
|
|
{
|
|
m_result.retcode=TRADE_RETCODE_INVALID_PRICE;
|
|
return(false);
|
|
}
|
|
//--- check symbol
|
|
sym=(symbol==NULL)?Symbol():symbol;
|
|
//--- send "BUY_LIMIT" order
|
|
return(OrderOpen(sym,ORDER_TYPE_BUY_LIMIT,volume,0.0,price,type_time,expiration,comment));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Send BUY_STOP order |
|
|
//+------------------------------------------------------------------+
|
|
bool CTradeCustom::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="")
|
|
{
|
|
string sym;
|
|
//--- check volume
|
|
if(volume<=0.0)
|
|
{
|
|
m_result.retcode=TRADE_RETCODE_INVALID_VOLUME;
|
|
return(false);
|
|
}
|
|
//--- check price
|
|
if(price==0.0)
|
|
{
|
|
m_result.retcode=TRADE_RETCODE_INVALID_PRICE;
|
|
return(false);
|
|
}
|
|
//--- check symbol
|
|
sym=(symbol==NULL)?Symbol():symbol;
|
|
//--- send "BUY_STOP" order
|
|
return(OrderOpen(sym,ORDER_TYPE_BUY_STOP,volume,0.0,price,type_time,expiration,comment));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Send SELL_LIMIT order |
|
|
//+------------------------------------------------------------------+
|
|
bool CTradeCustom::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="")
|
|
{
|
|
string sym;
|
|
//--- check volume
|
|
if(volume<=0.0)
|
|
{
|
|
m_result.retcode=TRADE_RETCODE_INVALID_VOLUME;
|
|
return(false);
|
|
}
|
|
//--- check price
|
|
if(price==0.0)
|
|
{
|
|
m_result.retcode=TRADE_RETCODE_INVALID_PRICE;
|
|
return(false);
|
|
}
|
|
//--- check symbol
|
|
sym=(symbol==NULL)?Symbol():symbol;
|
|
//--- send "SELL_LIMIT" order
|
|
return(OrderOpen(sym,ORDER_TYPE_SELL_LIMIT,volume,0.0,price,type_time,expiration,comment));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Send SELL_STOP order |
|
|
//+------------------------------------------------------------------+
|
|
bool CTradeCustom::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="")
|
|
{
|
|
string sym;
|
|
//--- check volume
|
|
if(volume<=0.0)
|
|
{
|
|
m_result.retcode=TRADE_RETCODE_INVALID_VOLUME;
|
|
return(false);
|
|
}
|
|
//--- check price
|
|
if(price==0.0)
|
|
{
|
|
m_result.retcode=TRADE_RETCODE_INVALID_PRICE;
|
|
return(false);
|
|
}
|
|
//--- check symbol
|
|
sym=(symbol==NULL)?Symbol():symbol;
|
|
//--- send "SELL_STOP" order
|
|
return(OrderOpen(sym,ORDER_TYPE_SELL_STOP,volume,0.0,price,type_time,expiration,comment));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Converts the position type to text |
|
|
//+------------------------------------------------------------------+
|
|
string CTradeCustom::FormatPositionType(string &str,const uint type) const
|
|
{
|
|
//--- clean
|
|
str="";
|
|
//--- 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;
|
|
break;
|
|
}
|
|
//--- return the result
|
|
return(str);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Converts the order type to text |
|
|
//+------------------------------------------------------------------+
|
|
string CTradeCustom::FormatOrderType(string &str,const uint type) const
|
|
{
|
|
//--- clean
|
|
str="";
|
|
//--- 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;
|
|
break;
|
|
}
|
|
//--- return the result
|
|
return(str);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Converts the order filling type to text |
|
|
//+------------------------------------------------------------------+
|
|
string CTradeCustom::FormatOrderTypeFilling(string &str,const uint type) const
|
|
{
|
|
//--- clean
|
|
str="";
|
|
//--- 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 CTradeCustom::FormatOrderTypeTime(string &str,const uint type) const
|
|
{
|
|
//--- clean
|
|
str="";
|
|
//--- 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;
|
|
break;
|
|
}
|
|
//--- return the result
|
|
return(str);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Converts the order prices to text |
|
|
//+------------------------------------------------------------------+
|
|
string CTradeCustom::FormatOrderPrice(string &str,const double price_order,const double price_trigger,const uint digits) const
|
|
{
|
|
string price,trigger;
|
|
//--- clean
|
|
str="";
|
|
//--- 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 CTradeCustom::FormatRequest(string &str,const MqlTradeRequest &request) const
|
|
{
|
|
string type,price,price_new;
|
|
string tmp;
|
|
CSymbolInfo symbol;
|
|
//--- clean
|
|
str="";
|
|
//--- set up
|
|
int digits=5;
|
|
if(request.symbol!=NULL)
|
|
{
|
|
if(symbol.Name(request.symbol))
|
|
digits=symbol.Digits();
|
|
}
|
|
//--- see what is wanted
|
|
switch(request.action)
|
|
{
|
|
//--- instant execution of a deal
|
|
case TRADE_ACTION_DEAL:
|
|
switch(symbol.TradeExecution())
|
|
{
|
|
//--- request execution
|
|
case SYMBOL_TRADE_EXECUTION_REQUEST:
|
|
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=StringFormat(" sl: %s",DoubleToString(request.sl,digits));
|
|
str+=tmp;
|
|
}
|
|
if(request.tp!=0.0)
|
|
{
|
|
tmp=StringFormat(" tp: %s",DoubleToString(request.tp,digits));
|
|
str+=tmp;
|
|
}
|
|
break;
|
|
//--- instant execution
|
|
case SYMBOL_TRADE_EXECUTION_INSTANT:
|
|
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=StringFormat(" sl: %s",DoubleToString(request.sl,digits));
|
|
str+=tmp;
|
|
}
|
|
if(request.tp!=0.0)
|
|
{
|
|
tmp=StringFormat(" tp: %s",DoubleToString(request.tp,digits));
|
|
str+=tmp;
|
|
}
|
|
break;
|
|
//--- market execution
|
|
case SYMBOL_TRADE_EXECUTION_MARKET:
|
|
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=StringFormat(" sl: %s",DoubleToString(request.sl,digits));
|
|
str+=tmp;
|
|
}
|
|
if(request.tp!=0.0)
|
|
{
|
|
tmp=StringFormat(" tp: %s",DoubleToString(request.tp,digits));
|
|
str+=tmp;
|
|
}
|
|
break;
|
|
//--- exchange execution
|
|
case SYMBOL_TRADE_EXECUTION_EXCHANGE:
|
|
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=StringFormat(" sl: %s",DoubleToString(request.sl,digits));
|
|
str+=tmp;
|
|
}
|
|
if(request.tp!=0.0)
|
|
{
|
|
tmp=StringFormat(" tp: %s",DoubleToString(request.tp,digits));
|
|
str+=tmp;
|
|
}
|
|
break;
|
|
}
|
|
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=StringFormat(" sl: %s",DoubleToString(request.sl,digits));
|
|
str+=tmp;
|
|
}
|
|
if(request.tp!=0.0)
|
|
{
|
|
tmp=StringFormat(" tp: %s",DoubleToString(request.tp,digits));
|
|
str+=tmp;
|
|
}
|
|
break;
|
|
|
|
//--- Setting SL/TP
|
|
case TRADE_ACTION_SLTP:
|
|
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;
|
|
|
|
default:
|
|
str="unknown action "+(string)request.action;
|
|
break;
|
|
}
|
|
//--- return the result
|
|
return(str);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Converts the result of a request to text |
|
|
//+------------------------------------------------------------------+
|
|
string CTradeCustom::FormatRequestResult(string &str,const MqlTradeRequest &request,const MqlTradeResult &result) const
|
|
{
|
|
CSymbolInfo symbol;
|
|
//--- clean
|
|
str="";
|
|
//--- set up
|
|
int digits=5;
|
|
if(request.symbol!=NULL)
|
|
{
|
|
if(symbol.Name(request.symbol))
|
|
digits=symbol.Digits();
|
|
}
|
|
//--- 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 &&
|
|
(symbol.TradeExecution()==SYMBOL_TRADE_EXECUTION_REQUEST ||
|
|
symbol.TradeExecution()==SYMBOL_TRADE_EXECUTION_INSTANT ||
|
|
symbol.TradeExecution()==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 &&
|
|
(symbol.TradeExecution()==SYMBOL_TRADE_EXECUTION_REQUEST ||
|
|
symbol.TradeExecution()==SYMBOL_TRADE_EXECUTION_INSTANT ||
|
|
symbol.TradeExecution()==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;
|
|
|
|
default:
|
|
str="unknown retcode "+(string)result.retcode;
|
|
break;
|
|
}
|
|
//--- return the result
|
|
return(str);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks if the m_request structure is filled correctly |
|
|
//+------------------------------------------------------------------+
|
|
bool CTradeCustom::OrderCheck(const MqlTradeRequest &request,MqlTradeCheckResult &check_result)
|
|
{
|
|
//--- action and return the result
|
|
return(::OrderCheck(request,check_result));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks and corrects type of filling policy |
|
|
//+------------------------------------------------------------------+
|
|
bool CTradeCustom::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(m_type_filling==ORDER_FILLING_FOK && (filling &SYMBOL_FILLING_FOK)!=0)
|
|
{
|
|
m_request.type_filling=m_type_filling;
|
|
return(true);
|
|
}
|
|
if(m_type_filling==ORDER_FILLING_IOC && (filling &SYMBOL_FILLING_IOC)!=0)
|
|
{
|
|
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)
|
|
{
|
|
//--- 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)!=0)
|
|
{
|
|
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)
|
|
{
|
|
//--- 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)!=0)
|
|
{
|
|
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 CTradeCustom::ExpirationCheck(const string symbol)
|
|
{
|
|
CSymbolInfo sym;
|
|
//--- check symbol
|
|
if(!sym.Name((symbol==NULL)?Symbol():symbol))
|
|
return(false);
|
|
//--- get flags
|
|
int flags=sym.TradeTimeFlags();
|
|
//--- 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");
|
|
break;
|
|
}
|
|
//--- failed
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks order |
|
|
//+------------------------------------------------------------------+
|
|
bool CTradeCustom::OrderTypeCheck(const string symbol)
|
|
{
|
|
bool res=false;
|
|
//--- check symbol
|
|
CSymbolInfo sym;
|
|
if(!sym.Name((symbol==NULL)?Symbol():symbol))
|
|
return(false);
|
|
//--- get flags of allowed trade orders
|
|
int flags=sym.OrderMode();
|
|
//--- 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;
|
|
default:
|
|
break;
|
|
}
|
|
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 CTradeCustom::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_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);
|
|
}
|
|
//+------------------------------------------------------------------+
|