问题
I'm trying to develop a piece of MQL4 code which sends you a notification when an position opened by an Expert Advisor fails to close/liquidate when the conditions specified by the Expert Advisor are met.
Here is what I have so far.
The ClosePosition()
returns true
when the position is closed successfully and returns false
when the EA fails to close the position. This is where the else if (ClosePosition == false)
kicks in.
//Order Close//
string sym = Symbol();
int ordersTotal = OrdersTotal();
for(int PosSel = ordersTotal-1; PosSel>=0; PosSel--)
{
if(OrderSelect(PosSel,SELECT_BY_POS,MODE_TRADES))
if(OrderTicket() > 0)
if(OrderMagicNumber() == Period())
if(OrderSymbol() == Symbol())
if(TimeCurrent() >=(OrderOpenTime() + 60 * Period()))
{
ClosePosition = OrderClose(OrderTicket(),8,MarketInfo(sym,MODE_BID) + MarketInfo(sym,MODE_SPREAD) * MarketInfo(sym,MODE_POINT),300,clrNONE);
if(ClosePosition == true)
{
Sleep(60000);
int PosSelHist = OrdersHistoryTotal()-1;
bool reshist = OrderSelect(PosSelHist,SELECT_BY_POS,MODE_HISTORY);
if(reshist == true && Digits == 5)
{
double ClosingPrice = OrderClosePrice();
double OpeningPrice = OrderOpenPrice();
double Profit = OrderProfit();
int Ticket = OrderTicket();
SendMail("Trade Notification Email (TNE)","Order# "+DoubleToStr(Ticket,0)+" has been closed on the account "+AccountName()+
"\n"+
"\nThe order exit price for this trade is "+DoubleToStr(ClosingPrice,5)+"with a profit/loss of"+DoubleToStr(Profit,2)+
"\n"+
"\nThe spread charge for this position is £"+DoubleToStr((spread*tickvalue)*LotSize,2)+
"\n"+
"\n-----------------------------------------------------------------------"+
"\n"+
SendNotification("Ticket # "+IntegerToString(Ticket,10)+"has closed with a profit/loss of "+DoubleToStr(Profit,2));
}
else if(reshist == true && Digits == 3)
{
double ClosingPrice = OrderClosePrice();
double OpeningPrice = OrderOpenPrice();
double Profit = OrderProfit();
int Ticket = OrderTicket();
SendMail("Trade Notification Email (TNE)","Order# "+DoubleToStr(Ticket,0)+" has been placed on the account "+AccountName()+
"\n"+
"\nThe order entry price for this trade is "+DoubleToStr(ClosingPrice,3)+"with a profit/loss of"+DoubleToStr(Profit,2)+
"\n"+
"\nThe spread charge for this position is £"+DoubleToStr((spread*tickvalue)*LotSize,2)+
"\n"+
"\n-----------------------------------------------------------------------"+
SendNotification("Ticket # "+IntegerToString(Ticket,10)+" has closed with a profit/loss of "+DoubleToStr(Profit,2));
}
}
else if(ClosePosition == false)
{
int failedClosePosition = OrdersTotal()-1;
bool fail = OrderSelect(failedClosePosition,SELECT_BY_POS,MODE_HISTORY);
if(fail == true)
{
SendNotification("Order Number #"+IntegerToString(OrderTicket(),10)+" has failed to close. Please refer to error code "+IntegerToString(GetLastError()));
}
}
}
}
Firstly, is this the correct way in getting the desired result, and secondly; is this the correct way of coding the alternative for the ClosePosition == true
, or would this be an if
instead of an else if
?
回答1:
It is possible that you will try to close your position but fail due to some regular issues - requote, or trade context is busy or some other. For such reason you MUST make sure the closing price is fresh
RefreshRates();
also you may try to solve TradeContext issue and others, alternative way - try to send some number of close requests:
int ATTEMPTS; // declare on a global scope
int OnInit(){ // initialise
ATTEMPTS = IsTesting() ? 1 : 100; // ternary-op =boolA?valB:valC
//---
return(INIT_SUCCEEDED);
}
void Order_Close(){
int ticket = GetTicketNumberToClose(); // put all checks here
TradeClose(ticket);
}
void TradeClose(int ticket){ // change to bool
// returning T/F if you need
while(IsTradeContextBusy()) Sleep(5); // a blocking-loop
int current_attempt = 0, err=-1;
while(current_attempt < ATTEMPTS){
RefreshRates(); // get fresh prices from Market
if (!OrderClose(ticket,OrderLots(),OrderClosePrice(),5)){
err = GetLastError();
current_attempt++;
}else return;
}
Print( __LINE__,
"failed to close ticket#", ticket,
" at price", DoubleToStr( OrderClosePrice(), Digits ),
". err#", err
); // send notification
// instead of Print() if you need
}
回答2:
No, sorry. The code is hard to get confirmed to be correct.
Sure, a few syntax errors could be fixed.
A big portion of processing is duplicated, which may work, but is hard to maintain in a longer run and is considered a poor software engineering practice/habit.
You may benefit from rather retaining a unique OrderTicket()
value once searching the db.POOL
and then straight SELECT_BY_TICKET
, which is robust against all order_state / order_relative_position changes in both of the { MODE_TRADES | MODE_HISTORY }
parts of the db.POOL
.
Next is the worse enemy for the proposed MQL4 code:
If one may believe this, never block the flow of the code. MQL4 code-execution environment is event-based, triggered by the external source of events ( The Market ) and calling Sleep( 60000 )
is worse than taking a sleep on the rails, right in the exit from the EuroTunnel. OUCH!!
Your code is envictimed not to be able to react, and worse, not to be able to let other parts of the MQL4 code eco-system breathe in sync with the flow of The Market Events. There are sure better ways, how to do "almost nothing", than Sleep()
, believe me.
Efficiency tips:
Avoid code-duplicates and rather use Digits
-specific formatting, be it via a variable set to actual number of decimal places, or via StringFormat()
patterns.
May enjoy a bit different layout of code, that will help you avoid syntax errors, missed parentheses et al.
Prefer to use globally unique, direct-pointer-alike OrderTicket()
numbers, as evaluated in OrderClose()
and rather pre-store it's value, as you wish to re-use it later in the code, but not having it readily available from the OrderClose()
-moment.
May enjoy another IDE for MQL4 code-effort, where collapsing and columnar blocks become very handy ( Geany, SciTe, JEdit, Notepad++ ).
Here, you soon physically view the inefficiency in if(){}else if(){}
code-blocks, where the natural mutual {True|False}
-dichotomy was duplicated in the else
part.
Anyway, enjoy the wild worlds of MQL4
//Order Close//
string sym = Symbol();
int ordersTotal = OrdersTotal();
for ( int PosSel = ordersTotal - 1; // loopVar .SET
PosSel >= 0; // loopVar .PRE-condition
PosSel-- // loopVar .DEC at loop-end
)
{
if ( OrderSelect( PosSel, SELECT_BY_POS, MODE_TRADES ) )
if ( OrderTicket() > 0 )
if ( OrderMagicNumber() == Period() )
if ( OrderSymbol() == Symbol() )
if ( OrderOpenTime() <= TimeCurrent() - ( 60 * Period() ) )
{
ClosePosition = OrderClose( OrderTicket(),
8,
MarketInfo( sym, MODE_BID )
+ MarketInfo( sym, MODE_SPREAD )
* MarketInfo( sym, MODE_POINT ),
300,
clrNONE
);
if ( ClosePosition == true )
{ // ===================||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||====================
Sleep( 60000 ); //|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| BLOCKS EA-EXECUTION
// ===================||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||====================
int PosSelHist = OrdersHistoryTotal() - 1;
bool reshist = OrderSelect( PosSelHist, SELECT_BY_POS, MODE_HISTORY );
if ( reshist == true
&& Digits == 5
)
{
double ClosingPrice = OrderClosePrice();
double OpeningPrice = OrderOpenPrice();
double Profit = OrderProfit();
int Ticket = OrderTicket();
SendMail( "Trade Notification Email (TNE)",
"Order# " + DoubleToStr( Ticket, 0 )
+ " has been closed on the account " + AccountName()
+ "\n"
+ "\nThe order exit price for this trade is " + DoubleToStr( ClosingPrice, 5 )
+ "with a profit/loss of" + DoubleToStr( Profit, 2 )
+ "\n"
+ "\nThe spread charge for this position is £" + DoubleToStr( ( spread
* tickvalue
)
* LotSize, 2 )
+ "\n"
+ "\n-----------------------------------------------------------------------"
+ "\n"
+ SendNotification( "Ticket # "
+ IntegerToString( Ticket, 10 )
+ "has closed with a profit/loss of "
+ DoubleToStr( Profit, 2 )
)
);
}
else if ( reshist == true
&& Digits == 3
)
{
double ClosingPrice = OrderClosePrice();
double OpeningPrice = OrderOpenPrice();
double Profit = OrderProfit();
int Ticket = OrderTicket();
SendMail( "Trade Notification Email (TNE)",
"Order# " + DoubleToStr( Ticket, 0 )
+ " has been placed on the account " + AccountName()
+ "\n"
+ "\nThe order entry price for this trade is " + DoubleToStr( ClosingPrice, 3 )
+ "with a profit/loss of" + DoubleToStr( Profit, 2 )
+ "\n"
+ "\nThe spread charge for this position is £" + DoubleToStr( ( spread
* tickvalue
)
* LotSize, 2 )
+ "\n"
+ "\n-----------------------------------------------------------------------"
+ SendNotification( "Ticket # "
+ IntegerToString( Ticket, 10 )
+ " has closed with a profit/loss of "
+ DoubleToStr( Profit, 2 )
)
);
}
}
else if ( ClosePosition == false )
{
int failedClosePosition = OrdersTotal() - 1;
bool fail = OrderSelect( failedClosePosition, SELECT_BY_POS, MODE_HISTORY );
if ( fail == true )
{
SendNotification( "Order Number #"
+ IntegerToString( OrderTicket(), 10 )
+ " has failed to close. Please refer to error code "
+ IntegerToString( GetLastError() )
);
}
}
}
}
来源:https://stackoverflow.com/questions/39685941/email-notification-when-expert-advisor-fails-to-close-a-trading-position