EA-Setka-2/Libs/Virtual/Order.mqh

801 lines
42 KiB
MQL5
Raw Permalink Normal View History

2025-05-30 14:50:44 +02:00
<EFBFBD><EFBFBD>#ifndef ORDER_MQH
#define ORDER_MQH
#property strict
#include "String.mqh"
#define ORDER_COMMISSION (0)
//#define ORDER_COMMISSION (-5)
#ifdef __MQL5__
#ifndef TICKET_TYPE
#define TICKET_TYPE long
#endif // TICKET_TYPE
#ifndef MAGIC_TYPE
#define MAGIC_TYPE long
#endif // MAGIC_TYPE
#ifndef OP_SELL
#define OP_SELL ORDER_TYPE_SELL
#endif // OP_SELL
#else // __MQL5__
#ifndef TICKET_TYPE
#define TICKET_TYPE int
#endif // TICKET_TYPE
#ifndef MAGIC_TYPE
#define MAGIC_TYPE int
#endif // MAGIC_TYPE
// https://www.mql5.com/ru/docs/constants/tradingconstants/dealproperties#enum_deal_reason
enum ENUM_DEAL_REASON
{
DEAL_REASON_CLIENT,
DEAL_REASON_MOBILE,
DEAL_REASON_WEB,
DEAL_REASON_EXPERT,
DEAL_REASON_SL,
DEAL_REASON_TP,
DEAL_REASON_SO,
DEAL_REASON_ROLLOVER,
DEAL_REASON_VMARGIN,
DEAL_REASON_SPLIT
};
#endif // __MQL5__
#ifndef OP_BALANCE
#define OP_BALANCE 6
#endif // OP_BALANCE
struct ORDER
{
private:
static TICKET_TYPE TicketCount;
TICKET_TYPE Ticket;
ENUM_ORDER_TYPE Type;
double Lots;
string oSymbol;
string comment;
double OpenPrice;
long OpenTimeMsc;
double StopLoss;
double TakeProfit;
double ClosePrice;
long CloseTimeMsc;
// datetime Expiration;
long MagicNumber;
double Profit;
// double Commission;
// double Swap;
ENUM_DEAL_REASON OpenReason;
ENUM_DEAL_REASON CloseReason;
double OpenPriceRequest;
double ClosePriceRequest;
static int PriceToPips( const double Price, const double point )
{
return(point ? ((int)(Price / point + 0.1)) : 0);
}
static int ProfitToPips( const double PriceClose, const double PriceOpen, const double point )
{
return(ORDER::PriceToPips(PriceClose, point) - ORDER::PriceToPips(PriceOpen , point));
}
static string LengthToString( const datetime Length )
{
const int Days = (int)(Length / (24 * 3600));
return(((Days) ? (string)Days + "d ": "") + ::TimeToString(Length, TIME_SECONDS));
}
template <typename T>
string NumToString( T Num, const bool CheckType = true, const int digits = 2 ) const // Report.mqh
{
if (CheckType && (this.OrderType() == OP_SELL))
Num = -Num;
return(((Num > 0) ? "+" : "") + ((typename(T) == "double") ? ::DoubleToString(Num, digits) : (string)Num));
}
TICKET_TYPE GetNewTicket( void ) const
{
return(++ORDER::TicketCount);
}
bool Reset( void )
{
this.StopLoss = -1; // 'B>1K ?@8 A>740=88 >@45@0 A@01>B0;> CA;>285 (this.StopLoss != SL) 2 Check.
return(true);
}
bool Check( double &Price, double &SL, double &TP, const MqlTick &Tick ) const
{
bool Res = false;
if (SL)
SL = ::NormalizeDouble(SL, _Digits);
if (TP)
TP = ::NormalizeDouble(TP, _Digits);
switch (this.Type)
{
case ORDER_TYPE_BUY:
Res = ((this.StopLoss != SL) || (this.TakeProfit != TP)) &&
(SL >= 0) && (SL <= Tick.bid) &&
((!TP) || (TP >= Tick.bid));
break;
case ORDER_TYPE_SELL:
Res = ((this.StopLoss != SL) || (this.TakeProfit != TP)) &&
(TP >= 0) && (TP <= Tick.ask) &&
(!SL || (SL >= Tick.ask));
break;
case ORDER_TYPE_BUY_LIMIT:
/*
if (TimeCurrent() >= D'2019.01.11 11:50:58')
DebugBreak();
*/
Price = ::NormalizeDouble(Price, _Digits);
/*
#ifdef BESTINTERVAL_ONTESTER
if (Price == Tick.ask) // ;O ?@8<5=5=8O BestInterval ?>:0 =5;L7O 2KAB02;OBL ;8<8B=8: ?> B5:CI59 F5=5.
Price = Tick.ask - _Point;
#endif // BESTINTERVAL_ONTESTER
*/
Res = ((this.OpenPrice != Price) || (this.StopLoss != SL) || (this.TakeProfit != TP)) &&
(Price > 0) && (Price <= Tick.ask) &&
(SL >= 0) && (SL <= Price) &&
(!TP || (TP >= Price));
break;
case ORDER_TYPE_SELL_LIMIT:
Price = ::NormalizeDouble(Price, _Digits);
/*
#ifdef BESTINTERVAL_ONTESTER
if (Price == Tick.bid) // ;O ?@8<5=5=8O BestInterval ?>:0 =5;L7O 2KAB02;OBL ;8<8B=8: ?> B5:CI59 F5=5.
Price = Tick.bid + _Point;
#endif // BESTINTERVAL_ONTESTER
*/
Res = ((this.OpenPrice != Price) || (this.StopLoss != SL) || (this.TakeProfit != TP)) &&
(Price >= Tick.bid) &&
(TP >= 0) && (TP <= Price) &&
(!SL || (SL >= Price));
break;
case ORDER_TYPE_BUY_STOP:
Price = ::NormalizeDouble(Price, _Digits);
Res = ((this.OpenPrice != Price) || (this.StopLoss != SL) || (this.TakeProfit != TP)) &&
(Price >= Tick.ask) &&
(SL >= 0) && (SL <= Price) &&
(!TP || (TP >= Price));
break;
case ORDER_TYPE_SELL_STOP:
Price = ::NormalizeDouble(Price, _Digits);
Res = ((this.OpenPrice != Price) || (this.StopLoss != SL) || (this.TakeProfit != TP)) &&
(Price > 0) && (Price <= Tick.bid) &&
(TP >= 0) && (TP <= Price) &&
(!SL || (SL >= Price));
;
}
return(Res);
}
void ToClose( const MqlTick &Tick )
{
if (!this.IsClosed())
{
this.Ticket = -this.Ticket;
this.CloseTimeMsc = Tick.time_msc;
if (this.IsPosition())
this.Profit = this.GetProfit();
}
return;
}
void ToNull( void )
{
this.Ticket = 0;
this.Profit = 0;
return;
}
double GetClosePrice( const MqlTick &Tick ) const
{
double Price = this.ClosePrice;
if (!this.IsClosed())
switch (this.Type)
{
case ORDER_TYPE_BUY:
case ORDER_TYPE_SELL_LIMIT:
case ORDER_TYPE_SELL_STOP:
Price = Tick.bid;
break;
case ORDER_TYPE_SELL:
case ORDER_TYPE_BUY_LIMIT:
case ORDER_TYPE_BUY_STOP:
Price = Tick.ask;
}
return(Price);
}
double GetProfit( void ) const
{
return((this.Type == ORDER_TYPE_BUY) ? this.Lots * (this.ClosePrice - this.OpenPrice) / _Point
: ((this.Type == ORDER_TYPE_SELL) ? this.Lots * (this.OpenPrice - this.ClosePrice) / _Point : 0));
}
static void CloseBy( ORDER &Order1, ORDER &Order2, const MqlTick &Tick )
{
Order2.ClosePrice = Order1.OpenPrice;
Order2.CloseReason = Order2.OpenReason;
Order2.ClosePriceRequest = Order1.OpenPriceRequest;
Order2.ToClose(Tick);
Order1.Lots = ::NormalizeDouble(Order1.Lots - Order2.Lots, 2);
Order1.Profit = Order1.GetProfit();
return;
}
public:
bool IsPosition( void ) const
{
return(this.Type <= ORDER_TYPE_SELL);
}
bool Modify( double Price, double SL, double TP, const MqlTick &Tick )
{
const bool Res = this.Check(Price, SL, TP, Tick);
if (Res)
{
this.TakeProfit = TP;
this.StopLoss = SL;
if (!this.IsPosition())
{
this.OpenPrice = Price;
this.OpenPriceRequest = this.OpenPrice;
}
}
return(Res);
}
bool IsClosed( void ) const
{
return(this.Ticket <= 0);
}
bool IsNotNull( void ) const
{
return((bool)this.Ticket);
}
bool IsChange( const MqlTick &Tick )
{
bool Res = false;
const ENUM_ORDER_TYPE PrevType = this.Type;
if (!this.IsClosed())
{
switch (this.Type)
{
case ORDER_TYPE_BUY:
if (Tick.bid <= this.StopLoss)
{
this.CloseReason = DEAL_REASON_SL;
this.ClosePriceRequest = this.StopLoss;
this.ClosePrice = Tick.bid;
Res = true;
}
else if (TakeProfit && (Tick.bid >= this.TakeProfit))
{
this.CloseReason = DEAL_REASON_TP;
this.ClosePriceRequest = this.TakeProfit;
#ifdef VIRTUAL_LIMITS_TP_SLIPPAGE
this.ClosePrice = Tick.bid;
#else // VIRTUAL_LIMITS_TP_SLIPPAGE
this.ClosePrice = this.TakeProfit;
#endif // VIRTUAL_LIMITS_TP_SLIPPAGE
Res = true;
}
break;
case ORDER_TYPE_SELL:
if (this.StopLoss && Tick.ask >= this.StopLoss)
{
this.CloseReason = DEAL_REASON_SL;
this.ClosePriceRequest = this.StopLoss;
this.ClosePrice = Tick.ask;
Res = true;
}
else if (Tick.ask <= this.TakeProfit)
{
this.CloseReason = DEAL_REASON_TP;
this.ClosePriceRequest = this.TakeProfit;
#ifdef VIRTUAL_LIMITS_TP_SLIPPAGE
this.ClosePrice = Tick.ask;
#else // VIRTUAL_LIMITS_TP_SLIPPAGE
this.ClosePrice = this.TakeProfit;
#endif // VIRTUAL_LIMITS_TP_SLIPPAGE
Res = true;
}
break;
case ORDER_TYPE_BUY_LIMIT:
if (Res = (Tick.ask <= this.OpenPrice))
{
this.Type = ORDER_TYPE_BUY;
#ifdef VIRTUAL_LIMITS_TP_SLIPPAGE
this.OpenPrice = Tick.ask;
#endif // VIRTUAL_LIMITS_TP_SLIPPAGE
}
break;
case ORDER_TYPE_SELL_LIMIT:
if (Res = (Tick.bid >= this.OpenPrice))
{
this.Type = ORDER_TYPE_SELL;
#ifdef VIRTUAL_LIMITS_TP_SLIPPAGE
this.OpenPrice = Tick.bid;
#endif // VIRTUAL_LIMITS_TP_SLIPPAGE
}
break;
case ORDER_TYPE_BUY_STOP:
if (Res = (Tick.ask >= this.OpenPrice))
{
this.Type = ORDER_TYPE_BUY;
this.OpenPrice = Tick.ask;
}
break;
case ORDER_TYPE_SELL_STOP:
if (Res = (Tick.bid <= this.OpenPrice))
{
this.Type = ORDER_TYPE_SELL;
this.OpenPrice = Tick.bid;
}
}
if (Res)
{
if (this.Type == PrevType)
this.ToClose(Tick);
else
{
this.OpenTimeMsc = Tick.time_msc;
this.ClosePrice = this.GetClosePrice(Tick);
this.Profit = this.GetProfit();
}
}
else
{
this.ClosePrice = this.GetClosePrice(Tick);
this.Profit = this.GetProfit();
}
}
return(Res);
}
bool Delete( const MqlTick &Tick )
{
const bool Res = !this.IsPosition();
if (Res)
{
this.ClosePrice = this.GetClosePrice(Tick);
this.CloseReason = this.OpenReason;
this.ToClose(Tick);
}
return(Res);
}
bool Create( const ENUM_ORDER_TYPE inType, double dLots, double inPrice, double inSL, double inTP,
const MAGIC_TYPE &iMagicNumber, const string &sComment, const MqlTick &Tick )
{
this.Lots = ::NormalizeDouble(dLots, 2);
this.Type = inType;
const bool Res = (this.Lots > 0) && this.Reset() && this.Check(inPrice, inSL, inTP, Tick);
if (Res)
{
this.Ticket = this.GetNewTicket();
this.OpenPrice = (this.Type == ORDER_TYPE_BUY) ? Tick.ask : ((this.Type == ORDER_TYPE_SELL) ? Tick.bid : inPrice);
this.OpenTimeMsc = Tick.time_msc;
this.StopLoss = inSL;
this.TakeProfit = inTP;
this.MagicNumber = iMagicNumber;
this.ClosePrice = this.GetClosePrice(Tick);
this.Profit = this.GetProfit();
this.CloseTimeMsc = 0;
this.comment = sComment;
this.OpenReason = DEAL_REASON_EXPERT;
this.CloseReason = DEAL_REASON_CLIENT;
this.OpenPriceRequest = this.OpenPrice;
// this.ClosePriceRequest = this.ClosePrice;
}
return(Res);
}
void Deposit( const double dDeposit, const MqlTick &Tick )
{
this.Ticket = this.GetNewTicket();
this.Type = OP_BALANCE;
this.Lots = 0;
// this.Symbol = NULL;
const string Str = NULL;
this.comment = Str;
this.OpenPrice = 0;
this.OpenTimeMsc = Tick.time_msc;
this.StopLoss = 0;
this.TakeProfit = 0;
this.ClosePrice = this.OpenPrice;
// this.Expiration = 0;
this.MagicNumber = 0;
this.Profit = dDeposit;
// this.Commission = 0;
// this.Swap = 0;
this.OpenReason = DEAL_REASON_CLIENT;
this.CloseReason = this.OpenReason;
this.OpenPriceRequest = this.OpenPrice;
this.ClosePriceRequest = this.OpenPriceRequest;
this.ToClose(Tick);
return;
}
static void CloseByNetting( ORDER &Order1, ORDER &Order2, const MqlTick &Tick )
{
Order1.ClosePrice = Order2.OpenPrice;
Order1.CloseReason = Order2.OpenReason;
Order1.ClosePriceRequest = Order2.OpenPriceRequest;
Order1.ToClose(Tick);
Order2.ToNull();
return;
}
bool CloseBy( ORDER &Order, const MqlTick &Tick, const bool Netting = false )
{
const bool Res = (this.Ticket != Order.Ticket) && (this.Type == ORDER_TYPE_SELL - Order.Type) && !this.IsClosed() && !Order.IsClosed();
if (Res)
{
if (this.Lots == Order.Lots)
{
if (!Netting)
{
this.ClosePrice = Order.OpenPrice;
this.CloseReason = this.OpenReason;
this.ClosePriceRequest = Order.OpenPriceRequest;
this.ToClose(Tick);
Order.ClosePrice = Order.OpenPrice;
Order.CloseReason = Order.OpenReason;
Order.OpenPriceRequest = Order.OpenPrice;
Order.ClosePriceRequest = Order.ClosePrice;
Order.ToClose(Tick);
}
else if (this.OpenTimeMsc <= Order.OpenTimeMsc)
ORDER::CloseByNetting(this, Order, Tick);
else
ORDER::CloseByNetting(Order, this, Tick);
}
else if (this.Lots > Order.Lots)
ORDER::CloseBy(this, Order, Tick);
else
ORDER::CloseBy(Order, this, Tick);
}
return(Res);
}
double Close( double dLots, const MqlTick &Tick )
{
double Res = 0;
if (this.IsPosition() && !this.IsClosed())
{
dLots = ::NormalizeDouble(dLots, 2);
if ((dLots > 0) && (dLots <= this.Lots))
{
Res = this.Lots - dLots;
this.Lots = dLots;
this.CloseReason = this.OpenReason;
this.ClosePriceRequest = this.ClosePrice;
this.ToClose(Tick);
}
}
return(Res);
}
void SetLots( const double dLots )
{
this.Ticket = this.GetNewTicket();
this.Lots = ::NormalizeDouble(dLots, _Digits);
this.CloseTimeMsc = 0;
this.Profit = this.GetProfit();
return;
}
#define ORDERFUNCTION(NAME,T) \
T Order##NAME( void ) const \
{ \
return((T)this.NAME); \
}
ORDERFUNCTION(Ticket, TICKET_TYPE)
ORDERFUNCTION(Type, int)
ORDERFUNCTION(Lots, double)
ORDERFUNCTION(OpenPrice, double)
ORDERFUNCTION(OpenTimeMsc, long)
ORDERFUNCTION(StopLoss, double)
ORDERFUNCTION(TakeProfit, double)
ORDERFUNCTION(ClosePrice, double)
ORDERFUNCTION(CloseTimeMsc, long)
ORDERFUNCTION(MagicNumber, MAGIC_TYPE)
ORDERFUNCTION(Profit, double)
ORDERFUNCTION(OpenReason, ENUM_DEAL_REASON)
ORDERFUNCTION(CloseReason, ENUM_DEAL_REASON)
ORDERFUNCTION(OpenPriceRequest, double)
#undef ORDERFUNCTION
datetime OrderOpenTime( void ) const
{
return((datetime)(this.OpenTimeMsc / 1000));
}
datetime OrderCloseTime( void ) const
{
return((datetime)(this.CloseTimeMsc / 1000));
}
datetime OrderExpiration( void ) const
{
return(0);
}
double OrderCommission( void ) const
{
return(this.Lots * ORDER_COMMISSION);
}
double OrderSwap( void ) const
{
return(0);
}
string OrderSymbol( void ) const
{
return(_Symbol);
}
string OrderComment( void ) const
{
return(this.comment);
}
void OrderPrint( const datetime timeCurrent ) const
{
::Print(this.ToString(timeCurrent));
}
double OrderClosePriceRequest( void ) const
{
return(this.IsClosed() ? this.ClosePriceRequest : this.OrderClosePrice());
}
string ToString( const datetime tTimeCurrent ) const
{
static const string Types[] = {"buy", "sell", "buy limit", "sell limit", "buy stop", "sell stop", "balance"};
return(("#" + (string)this.OrderTicket() + " " +
(string)this.OrderOpenTime() + "." + ::IntegerToString(this.OrderOpenTimeMsc() % 1000, 3, '0') + " " +
((this.Type < ::ArraySize(Types)) ? Types[this.Type] : "unknown") + " " +
::DoubleToString(this.OrderLots(), 2) + " " +
this.OrderSymbol() + " " +
::DoubleToString(this.OrderOpenPrice(), _Digits) + " " +
::DoubleToString(this.OrderStopLoss(), _Digits) + " " +
::DoubleToString(this.OrderTakeProfit(), _Digits) + " " +
((this.OrderCloseTime() > 0) ? ((string)this.OrderCloseTime() + "." + ::IntegerToString(this.OrderCloseTimeMsc() % 1000, 3, '0') + " ") : NULL) +
::DoubleToString(this.OrderClosePrice(), _Digits) + " " +
::DoubleToString(this.OrderCommission(), 2) + " " +
::DoubleToString(this.OrderSwap(), 2) + " " +
::DoubleToString(this.OrderProfit(), 2) + " " +
((this.OrderComment() == "") ? NULL : (this.OrderComment() + " ")) +
(string)this.OrderMagicNumber() +
(((this.OrderExpiration() > 0) ? (" expiration " + (string)this.OrderExpiration()): NULL))) +
((this.OrderType() <= OP_SELL) ? ": " + this.NumToString(ORDER::ProfitToPips(this.OrderClosePrice(), this.OrderOpenPrice(), _Point)) +
" (" + this.NumToString(ORDER::ProfitToPips(this.OrderClosePriceRequest(), this.OrderOpenPriceRequest(), _Point)) + ")" : NULL) +
" - " + ORDER::LengthToString((this.OrderCloseTime() ? this.OrderCloseTime() : tTimeCurrent) - this.OrderOpenTime()));
}
string ToStringCSV( const string delim = "\t") const
{
static const string Types[] = {"buy", "sell", "buy limit", "sell limit", "buy stop", "sell stop", "balance"};
return((string)this.OrderTicket() + delim +
(string)this.OrderOpenTime() + delim +
IntegerToString(this.Type) + delim +
::DoubleToString(this.OrderLots(), 2) + delim +
this.OrderSymbol() + delim +
::DoubleToString(this.OrderOpenPrice(), _Digits) + delim +
::DoubleToString(this.OrderStopLoss(), _Digits) + delim +
::DoubleToString(this.OrderTakeProfit(), _Digits) + delim +
((this.OrderCloseTime() > 0) ? ((string)this.OrderCloseTime() + delim) : delim) +
::DoubleToString(this.OrderClosePrice(), _Digits) + delim +
::DoubleToString(this.OrderCommission(), 2) + delim +
::DoubleToString(this.OrderSwap(), 2) + delim +
::DoubleToString(this.OrderProfit(), 2) + delim +
((this.OrderComment() == "") ? delim : (this.OrderComment() + delim)) +
(string)this.OrderMagicNumber() + delim +
((this.OrderExpiration() > 0) ? ((string)this.OrderExpiration() + delim): delim) + "\r\n");
}
int ToFileCSV(const int fHandle) const
{
return((int)FileWrite(fHandle,this.OrderTicket(),
this.OrderOpenTime(),
this.Type,
::DoubleToString(this.OrderLots(), 2),
this.OrderSymbol(),
::DoubleToString(this.OrderOpenPrice(), _Digits),
::DoubleToString(this.OrderStopLoss(), _Digits),
::DoubleToString(this.OrderTakeProfit(), _Digits),
this.OrderCloseTime(),
::DoubleToString(this.OrderClosePrice(), _Digits),
//::DoubleToString(this.OrderCommission(), 2),
//::DoubleToString(this.OrderSwap(), 2),
//::DoubleToString(this.OrderProfit(), 2),
this.OrderComment(),
this.OrderMagicNumber()));
//this.OrderExpiration()));
}
bool FromFileCSV(const int fHandle)
{
this.Ticket = (TICKET_TYPE) FileReadNumber(fHandle);
this.OpenTimeMsc = 1000 * FileReadDatetime(fHandle);
this.Type = (ENUM_ORDER_TYPE)FileReadNumber(fHandle);
this.Lots = FileReadNumber(fHandle);
this.oSymbol = FileReadString(fHandle);
this.OpenPrice = FileReadNumber(fHandle);
this.StopLoss = FileReadNumber(fHandle);
this.TakeProfit = FileReadNumber(fHandle);
this.CloseTimeMsc = FileReadDatetime(fHandle);
this.ClosePrice = FileReadNumber(fHandle);
//this.Commission = FileReadNumber(fHandle);
//this.Swap = FileReadNumber(fHandle);
//this.OrderProfit = FileReadNumber(fHandle);
this.comment = FileReadString(fHandle);
this.MagicNumber = (MAGIC_TYPE)FileReadNumber(fHandle);
//this.Expiration = FileReadDatetime(fHandle);
if(!FileIsLineEnding(fHandle))
return false;
if (this.TicketCount < this.Ticket)
this.TicketCount = this.OrderTicket();
return true;
}
void Stop( const MqlTick &Tick )
{
if (!this.Delete(Tick) && !this.Close(this.Lots, Tick))
{
const string Str = "end of test";
this.comment = Str;
}
return;
}
};
static TICKET_TYPE ORDER::TicketCount = 0; // https://www.mql5.com/ru/forum/1111/page2302#comment_8891928
#endif //ORDER_MQH