Virtual_by_fxsaber/Include/fxsaber/Virtual/Order_Base.mqh

582 lines
No EOL
34 KiB
MQL5

#property strict
#include "Symbol_Base.mqh"
#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
// https://www.mql5.com/ru/forum/317893/page3#comment_55114508
struct ORDER_BASE : public SYMBOL_BASE
{
#ifdef __MQL5__
using SYMBOL_BASE::operator=; // https://www.mql5.com/ru/forum/492248/page23#comment_57855997
#endif // #ifdef __MQL5__
private:
static int PriceToPips( const double Price, const double point )
{
return(point ? ((int)(Price / point + 0.1)) : 0);
}
static int ProfitToPips( const double dPriceClose, const double dPriceOpen, const double point )
{
return(ORDER_BASE::PriceToPips(dPriceClose, point) - ORDER_BASE::PriceToPips(dPriceOpen , point));
}
static string LengthToString( const datetime Length )
{
const int Days = (int)(Length / (24 * 3600));
return(((Days) ? (string)Days + "d ": NULL) + ::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.GetType() == OP_SELL))
Num = -Num;
return(((Num > 0) ? "+" : "") + ((typename(T) == "double") ? ::DoubleToString(Num, digits) : (string)Num));
}
// https://www.mql5.com/ru/forum/170952/page208#comment_24667438
#define DAY (24 * 3600)
#define WEEK (DAY * 7)
// Возвращает количество "четвергов" между двумя датами.
static int GetAmountWeekDay( const datetime &Begin, const datetime &End, const int DayWeek = -1 )
{
const datetime OffsetTime = (DayWeek - WEDNESDAY) * DAY;
return((DayWeek != -1) ? (int)((End - OffsetTime) / WEEK - (Begin - OffsetTime) / WEEK) : 0);
}
// Возвращает количество рабочих дней между двумя датами.
static int GetAmountWorkingDays( const datetime &Begin, const datetime &End )
{
const int Res = (int)(End / DAY - Begin / DAY);
return(Res ? Res - ORDER_BASE::GetAmountWeekDay(Begin, End, SATURDAY) -
ORDER_BASE::GetAmountWeekDay(Begin, End, SUNDAY) : 0);
}
// Возвращает количество ролловеров (включая тройные) между двумя датами.
static int GetRolloverAmounts( datetime TimeOpen, datetime TimeClose,
const datetime &RolloverTime, const int &Rollover3Days )
{
TimeOpen -= RolloverTime;
TimeClose -= RolloverTime;
const int Res = ORDER_BASE::GetAmountWorkingDays(TimeOpen, TimeClose);
return(Res ? Res + (ORDER_BASE::GetAmountWeekDay(TimeOpen, TimeClose, Rollover3Days) << 1) : 0);
}
#undef WEEK
#undef DAY
static string TimeToString( const long time )
{
return((string)(datetime)(time / 1000) + "." + ::IntegerToString(time % 1000, 3, '0'));
}
static int GetDigits( double Price )
{
int Res = 0;
while ((bool)(Price = ::NormalizeDouble(Price - (int)Price, 8)))
{
Price *= 10;
Res++;
}
return(Res);
}
static string DoubleToString( const double Num, const int digits )
{
return(::DoubleToString(Num, ::MathMax(digits, ORDER_BASE::GetDigits(Num))));
}
static string GetAddType( const int Type, const bool FlagOrder )
{
#ifdef __MQL5__
::ResetLastError();
string Str = FlagOrder ? ::EnumToString((ENUM_ORDER_TYPE)Type) : ::EnumToString((ENUM_DEAL_TYPE)Type);
if (!::_LastError && ::StringToLower(Str))
{
Str = FlagOrder ? ::StringSubstr(Str, 11) // "order_type_"
: (!::StringFind(Str, "deal_type_") ? ::StringSubstr(Str, 10) // "deal_type_"
: (!::StringFind(Str, "deal_") ? ::StringSubstr(Str, 5) // "deal_"
: Str));
::StringReplace(Str, "_", " ");
}
else
#else // #ifdef __MQL5__
string
#endif // #ifdef __MQL5__ #else
Str = "unknown(" + (string)Type + ")";
return(Str);
}
public:
long Ticket;
ENUM_ORDER_TYPE Type;
double Lots;
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;
/*
// Отсутствие конструктора позволяет использовать в union для VIRTUAL::Save/Load.
// Зачем было обнуление Ticket? Возможно, для SelectOrder.Ticket в Orders.mqh.
// Сделан обход (в ORDERS::Set( const double dBalance = DBL_MIN, const bool bNetting = false )) в виде this.SelectOrder.SetTicket(0);
// Обнуление Ticket требовалось для такой конструкции:
// ORDER_BASE Orders[4]; // OP_BUY, OP_SELL, OP_BUYLIMIT, OP_SELLLIMIT
// Orders[OrderType()] = VIRTUAL::GetOrder();
// Обход - https://www.mql5.com/ru/forum/170952/page209#comment_25891642
ORDER_BASE( void ) : Ticket(0)
{
}
*/
#define ORDERFUNCTION(NAME,T) \
T Get##NAME( void ) const \
{ \
_VC 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)
ORDERFUNCTION(Commission, double)
ORDERFUNCTION(Swap, double)
ORDERFUNCTION(SymbolID, int)
#undef ORDERFUNCTION
datetime GetOpenTime( void ) const
{
return((datetime)(this.OpenTimeMsc / 1000));
}
datetime GetCloseTime( void ) const
{
return((datetime)(this.CloseTimeMsc / 1000));
}
datetime GetExpiration( void ) const
{
return(0);
}
string GetComment( void ) const
{
return(this.comment.Get());
}
void GetPrint( const datetime &timeCurrent ) const
{
::Print(this.ToString(timeCurrent));
}
double GetClosePriceRequest( void ) const
{
return(this.CloseTimeMsc ? this.ClosePriceRequest : this.GetClosePrice());
}
double GetFullProfit( void ) const
{
return(this.GetProfit() + this.GetCommission() + this.GetSwap());
}
void SetComment( const string &NewComment )
{
this.comment = NewComment;
return;
}
double SetSwap( const double &SwapShort, const double &SwapLong, const datetime &RolloverTime, const int &Rollover3Days )
{
const double PrevSwap = this.Swap;
// См. ORDER::ToHistory().
this.Swap = this.IsPosition() ? ::NormalizeDouble((ORDER_BASE::GetRolloverAmounts(this.GetOpenTime(), this.GetCloseTime(), RolloverTime, Rollover3Days) *
(this.Type ? SwapShort : SwapLong) *
(this.Lots ? this.Lots : 1) * // Для CloseBy TickValue учитывает Lots.
this.TickValue * (this.Point / this.TickSize)),
SYMBOL_BASE::DigitsCurrency)
: 0;
return(this.Swap - PrevSwap);
}
string ToString( const datetime tTimeCurrent ) const
{
static const string Types[] = {"buy", "sell", "buy limit", "sell limit", "buy stop", "sell stop", "balance"};
const string Symb = (this.Type >= OP_BALANCE) ? NULL : this.GetSymbol();
const int digits = (this.Type >= OP_BALANCE) ? 0 : (this.CloseTimeMsc ? (int)::SymbolInfoInteger(Symb, SYMBOL_DIGITS)
: this.Digits);
const double point = (this.Type == OP_BALANCE) ? 0 : (this.CloseTimeMsc ? ::SymbolInfoDouble(Symb, SYMBOL_POINT)
: this.Point);
const string StrComm = this.GetComment();
return(("#" + (string)this.GetTicket() + " " +
ORDER_BASE::TimeToString(this.GetOpenTimeMsc()) + " " +
(((this.Type < ::ArraySize(Types)) &&
((this.Type <= ORDER_TYPE_SELL_STOP) || !this.OpenPrice)) ? Types[this.Type] : ORDER_BASE::GetAddType(this.Type, this.OpenPrice)) + " " +
// (string)this.GetLots() + " " +
ORDER_BASE::DoubleToString(this.GetLots(), 2) + " " +
((this.Type >= OP_BALANCE) ? NULL : Symb + " ") +
ORDER_BASE::DoubleToString(this.GetOpenPrice(), digits) + " " +
ORDER_BASE::DoubleToString(this.GetStopLoss(), digits) + " " +
ORDER_BASE::DoubleToString(this.GetTakeProfit(), digits) + " " +
((this.GetCloseTimeMsc() > 0) ? ORDER_BASE::TimeToString(this.GetCloseTimeMsc()) + " " : NULL) +
ORDER_BASE::DoubleToString(this.GetClosePrice(), digits) + " " +
ORDER_BASE::DoubleToString(this.GetCommission(), SYMBOL_BASE::DigitsCurrency) + " " +
ORDER_BASE::DoubleToString(this.GetSwap(), SYMBOL_BASE::DigitsCurrency) + " " +
ORDER_BASE::DoubleToString(this.GetProfit(), SYMBOL_BASE::DigitsCurrency) + " " +
((StrComm == "") ? NULL : (StrComm + " ")) +
(string)this.GetMagicNumber() +
(((this.GetExpiration() > 0) ? (" expiration " + (string)this.GetExpiration()): NULL))) +
((this.IsPosition()) ? ": " + this.NumToString(ORDER_BASE::ProfitToPips(this.GetClosePrice(), this.GetOpenPrice(), point)) +
" (" + this.NumToString(ORDER_BASE::ProfitToPips(this.GetClosePriceRequest(), this.GetOpenPriceRequest(), point)) + ")" : NULL) +
" - " + ORDER_BASE::LengthToString((this.GetCloseTimeMsc() ? this.GetCloseTime()
: ((this.GetOpenTime() > tTimeCurrent) ? this.GetOpenTime()
: tTimeCurrent)) - this.GetOpenTime()));
}
bool IsPosition( void ) const
{
_VC
return(this.Type <= ORDER_TYPE_SELL);
}
bool IsPending( void ) const
{
_VC
return((this.Type > OP_SELL) && (this.Type < OP_BALANCE));
}
bool operator ==( const ENUM_ORDER_TYPE tType ) const
{
return(this.Type == tType);
}
bool operator !=( const ENUM_ORDER_TYPE tType ) const
{
return(this.Type != tType);
}
bool operator ==( const ORDER_BASE &Order ) const
{
static const bool IsTester = ::MQLInfoInteger(MQL_TESTER);
return((this.GetType() == Order.GetType()) &&
(this.GetMagicNumber() == Order.GetMagicNumber()) &&
// (this.GetLots() <= Order.GetLots()) &&
(this.GetTakeProfit() == Order.GetTakeProfit()) &&
(this.GetStopLoss() == Order.GetStopLoss()) &&
(this.IsPosition() || (this.GetOpenPrice() == Order.GetOpenPrice())) &&
(IsTester || (this.GetSymbol() == Order.GetSymbol())));
}
bool operator !=( const ORDER_BASE &Order ) const
{
return(!(this == Order));
}
#define MACROS_OPERATOR(A) \
bool operator A( const long lTime ) const \
{ \
return(this.OpenTimeMsc A lTime); \
}
MACROS_OPERATOR(>=)
MACROS_OPERATOR(<=)
MACROS_OPERATOR(!=)
MACROS_OPERATOR(<)
MACROS_OPERATOR(>)
#undef MACROS_OPERATOR
double GetMargin(void) const
{
#ifdef __MQL5__
double result;
if (::OrderCalcMargin(this.Type, this.GetSymbol(), this.Lots, this.OpenPrice, result))
return result;
#endif // __MQL5__
return 0;
}
#define MACROS(A) this.##A = ::Order##A();
bool Copy( const bool WithoutHistory = false )
{
#ifdef __MQL5__
#ifdef __MT4ORDERS__
static const bool IsTester = ::MQLInfoInteger(MQL_TESTER);
if (IsTester)
{
static bool FirstRun = true;
static ORDER_BASE TmpOrder;
if (FirstRun)
{
this.Symbol = _Symbol;
// const string Str = NULL;
// this.comment = Str;
this.comment.Array[0] = 0;
FirstRun = false;
}
this = TmpOrder;
}
else
{
// MACROS(Symbol)
const string Symb = ::OrderSymbol();
this.Symbol = Symb;
}
MACROS(CloseTimeMsc)
MACROS(OpenPrice)
if (WithoutHistory && !this.CloseTimeMsc) // Для исторических ордеров оставляем все без изменений.
{
if (!IsTester)
{
// const string Str = NULL;
// this.comment = Str;
this.comment.Array[0] = 0; // = NULL;
}
this.Commission = 0;
this.OpenPriceRequest = this.OpenPrice;
}
else if (!IsTester || this.CloseTimeMsc)// В MT4Orders требуется обращение к истории.
{
const string Str = ::OrderComment();
this.comment = Str;
MACROS(Commission)
MACROS(OpenPriceRequest)
if (IsTester)
{
// MACROS(Symbol)
const string Symb = ::OrderSymbol();
this.Symbol = Symb;
}
}
MACROS(Ticket)
// MACROS(Type)
this.Type = (ENUM_ORDER_TYPE)::OrderType();
MACROS(Lots)
MACROS(OpenTimeMsc)
MACROS(StopLoss)
MACROS(TakeProfit)
MACROS(ClosePrice)
MACROS(MagicNumber)
MACROS(Profit)
MACROS(Swap)
// MACROS(Expiration)
MACROS(OpenReason)
MACROS(CloseReason)
MACROS(ClosePriceRequest)
MACROS(Profit)
#endif // #ifdef __MT4ORDERS__
#else // #ifdef __MQL5__
MACROS(Ticket)
// MACROS(Type)
this.Type = (ENUM_ORDER_TYPE)::OrderType();
// MACROS(Symbol)
const string Symb = ::OrderSymbol();
this.Symbol = Symb;
MACROS(Lots)
// MACROS(Comment)
const string Str = ::OrderComment();
this.comment = Str;
MACROS(OpenPrice)
this.OpenTimeMsc = ::OrderOpenTime() * 1000;
MACROS(StopLoss)
MACROS(TakeProfit)
MACROS(ClosePrice)
this.CloseTimeMsc = ::OrderCloseTime() * 1000;
MACROS(MagicNumber)
MACROS(Profit)
MACROS(Commission)
MACROS(Swap)
// MACROS(Expiration)
#ifdef __MQL5__
if (!WithoutHistory)
{
MACROS(OpenReason)
MACROS(CloseReason)
MACROS(OpenPriceRequest)
MACROS(ClosePriceRequest)
}
else
#endif // #ifdef __MQL5__
{
this.OpenReason = DEAL_REASON_CLIENT;
this.CloseReason = DEAL_REASON_CLIENT;
this.OpenPriceRequest = this.OpenPrice;
this.ClosePriceRequest = this.ClosePrice;
}
MACROS(Profit)
#endif // #ifdef __MQL5__ #else
return(true);
}
#undef MACROS
// Если отрицательный - см. HISTORYORDERS::AddHistoryOrder -> ORDER::ToHistory
void SetTicket( const long iTicket )
{
this.Ticket = iTicket;
return;
}
void Set( const long lTicket,
const ENUM_ORDER_TYPE eType,
const double dLots,
const string sSymbol,
const string sComment,
const double dOpenPrice,
const long lOpenTimeMsc,
const double dStopLoss = 0,
const double dTakeProfit = 0,
const double dClosePrice = 0,
const long lCloseTimeMsc = 0,
const long lMagicNumber = 0,
const double dProfit = 0,
const double dCommission = 0,
const double dSwap = 0,
const ENUM_DEAL_REASON eOpenReason = DEAL_REASON_CLIENT,
const ENUM_DEAL_REASON eCloseReason = DEAL_REASON_CLIENT,
const double dOpenPriceRequest = 0,
const double dClosePriceRequest = 0 )
{
this.Ticket = lTicket;
this.Type = eType;
this.Lots = dLots;
this.Symbol = sSymbol;
this.comment = sComment;
this.OpenPrice = dOpenPrice;
this.OpenTimeMsc = lOpenTimeMsc;
this.StopLoss = dStopLoss;
this.TakeProfit = dTakeProfit;
this.ClosePrice = dClosePrice;
this.CloseTimeMsc = lCloseTimeMsc;
this.MagicNumber = lMagicNumber;
this.Profit = dProfit;
this.Commission = dCommission;
this.Swap = dSwap;
this.OpenReason = eOpenReason;
this.CloseReason = eCloseReason;
this.OpenPriceRequest = dOpenPriceRequest ? dOpenPriceRequest : this.OpenPrice;
this.ClosePriceRequest = dClosePriceRequest ? dClosePriceRequest : this.ClosePrice;
return;
}
};