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

666 lines
35 KiB
MQL5
Raw Permalink Normal View History

2025-05-30 14:50:44 +02:00
<EFBFBD><EFBFBD>// https://www.mql5.com/ru/forum/1111/page2336#comment_9660624
// C6=> 4;O BestInterval
// #define TICKS_CORRECT_TIME // 2@5<O B8:>2, :@0B=>5 A5:C=45, 1C45B A:>@@5:B8@>20=> =0 <8;;8A5:C=4C 2?5@54
#ifndef ORDERS_MQH
#define ORDERS_MQH
#include "Order.mqh"
#ifdef __MQL5__
#ifndef SELECT_BY_POS
#define SELECT_BY_POS 0
#endif // SELECT_BY_POS
#ifndef SELECT_BY_TICKET
#define SELECT_BY_TICKET 1
#endif // SELECT_BY_TICKET
#ifndef MODE_TRADES
#define MODE_TRADES 0
#endif // MODE_TRADES
#ifndef MODE_HISTORY
#define MODE_HISTORY 1
#endif // MODE_HISTORY
#ifndef OP_SELL
#define OP_SELL ORDER_TYPE_SELL
#endif // OP_SELL
#endif // __MQL5__
#define MAX_ORDERS 100
#define RESERVE_HISTORY_ORDERS 2000
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
//class OrderCallBack
//{
//public:
// virtual void OnSuccess(TICKET_TYPE result, ORDER *order)
// {
// Print("Order success. Result: ",(string)result,order.ToString(TimeCurrent()));
// }
// virtual void OnFailure(TICKET_TYPE result, ORDER *order)
// {
// Print("Order failure. Result: ", (string)result, order.ToString(TimeCurrent()));
// }
// virtual void OnTimeout(TICKET_TYPE result, ORDER *order)
// {
// Print("Order timeout. Result: ", (string)result, order.ToString(TimeCurrent()));
// }
//};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class ORDERS
{
private:
bool Netting;
double Balance;
double Equity;
ORDER Orders[MAX_ORDERS];
int AmountOrders;
MqlTick CurrentTick;
int SelectIndex;
ORDER SelectOrder;
ORDER HistoryOrders[];
int AmountHistoryOrders;
int ReserveHistoryOrders;
//OrderCallBack *pCallBack;
static string GetTickFlag( uint tickflag )
{
string flag = " " + (string)tickflag;
#define TICKFLAG_MACRO(A) flag += ((bool)(tickflag & TICK_FLAG_##A)) ? " TICK_FLAG_" + #A : ""; \
tickflag -= tickflag & TICK_FLAG_##A;
TICKFLAG_MACRO(BID)
TICKFLAG_MACRO(ASK)
TICKFLAG_MACRO(LAST)
TICKFLAG_MACRO(VOLUME)
TICKFLAG_MACRO(BUY)
TICKFLAG_MACRO(SELL)
#undef TICKFLAG_MACRO
if (tickflag)
flag += " FLAG_UNKNOWN (" + (string)tickflag + ")";
return(flag);
}
#define TOSTR(A) " " + #A + " = " + (string)Tick.A
#define TOSTR2(A) " " + #A + " = " + ::DoubleToString(Tick.A, _Digits)
static string TickToString( const MqlTick &Tick )
{
return(TOSTR(time) + "." + ::IntegerToString(Tick.time_msc % 1000, 3, '0') +
TOSTR2(bid) + TOSTR2(ask) + TOSTR2(last)+ TOSTR(volume) + ORDERS::GetTickFlag(Tick.flags));
}
#undef TOSTR2
#undef TOSTR
void AddHistoryOrder( const ORDER &Order )
{
if (this.AmountHistoryOrders == this.ReserveHistoryOrders)
this.ReserveHistoryOrders = ::ArrayResize(this.HistoryOrders, this.ReserveHistoryOrders + RESERVE_HISTORY_ORDERS);
if (Order.IsNotNull())
this.HistoryOrders[this.AmountHistoryOrders++] = Order;
return;
}
bool IsChange( void )
{
bool Res = false;
double Profit = 0;
int j = 0;
for (int i = 0; i < this.AmountOrders; i++)
{
Res |= this.Orders[i].IsChange(this.CurrentTick) && !this.Orders[i].IsClosed();
if (this.Orders[i].IsClosed())
{
this.Balance += this.Orders[i].OrderProfit();
this.AddHistoryOrder(this.Orders[i]);
}
else
{
Profit += this.Orders[i].OrderProfit();
if (i != j)
this.Orders[j] = this.Orders[i];
j++;
}
}
this.AmountOrders = j;
this.Equity = this.Balance + Profit;
return(Res);
}
void CloseBy( void )
{
int j = -1;
for (int i = 0; i < this.AmountOrders; i++)
if (!this.Orders[i].IsClosed() && this.Orders[i].IsPosition())
{
if (j < 0)
j = i;
else if (this.Orders[j].CloseBy(this.Orders[i], this.CurrentTick, this.Netting) && this.Orders[j].IsClosed())
{
i = j;
j = -1;
}
}
return;
}
void Check( void )
{
while (this.IsChange())
if (this.Netting)
this.CloseBy();
return;
}
int GetIndex( const long &Ticket ) const
{
int Res = -1;
if (this.SelectIndex < this.AmountOrders)
{
if (this.Orders[this.SelectIndex].OrderTicket() == Ticket)
Res = this.SelectIndex;
else
for (int i = 0; i < this.AmountOrders; i++)
if (this.Orders[i].OrderTicket() == Ticket)
{
Res = i;
break;
}
}
return(Res);
}
bool SelectByPos( const int Index )
{
const bool Res = (Index >= 0) && (Index < this.AmountOrders);
if (Res)
{
this.SelectIndex = Index;
this.SelectOrder = this.Orders[this.SelectIndex];
}
return(Res);
}
bool SelectByTicket( const long Ticket, const long Pool )
{
const int Index = this.GetIndex(Ticket);
const bool Res = (Index >= 0);
if (Res)
{
this.SelectIndex = Index;
this.SelectOrder = this.Orders[this.SelectIndex];
}
return(Res);
}
bool SelectByPosHistory( const int Index )
{
const bool Res = (Index >= 0) && (Index < this.AmountHistoryOrders);
if (Res)
this.SelectOrder = this.HistoryOrders[Index];
return(Res);
}
public:
const int Handle;
ORDERS( const int iHandle, const datetime StartTime = 0 ) : Handle(iHandle), AmountOrders(0), SelectIndex(0), AmountHistoryOrders(0), ReserveHistoryOrders(0)
{
MqlTick Tick = {0};
Tick.time_msc = (long)StartTime * 1000;
this.NewTick(Tick);
//pCallBack = NULL;
}
void Set( const double dBalance = 0, const bool bNetting = false )
{
this.Balance = (dBalance > 0) ? dBalance : ::AccountInfoDouble(ACCOUNT_BALANCE);
this.Equity = this.Balance;
this.Netting = (dBalance > 0) ? bNetting :
#ifdef __MQL5__
!((ENUM_ACCOUNT_MARGIN_MODE)::AccountInfoInteger(ACCOUNT_MARGIN_MODE) == ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
#else // __MQL5__
bNetting
#endif //__MQL5__
;
this.AmountOrders = 0;
this.SelectIndex = 0;
this.AmountHistoryOrders = 0;
this.ReserveHistoryOrders = 0; // ::ArrayResize(this.HistoryOrders, RESERVE_HISTORY_ORDERS);
this.OrderDeposit(this.Balance);
return;
}
bool IsNetting( void ) const
{
return(this.Netting);
}
void NewTick( const MqlTick &Tick )
{
this.CurrentTick = Tick;
#ifdef TICKS_CORRECT_TIME
if (!(this.CurrentTick.time_msc % 1000))
this.CurrentTick.time_msc++;
#endif // TICKS_CORRECT_TIME
this.Check();
return;
}
void OrderDeposit( const double Deposit )
{
if (Deposit)
{
ORDER Order;
Order.Deposit(Deposit, this.CurrentTick);
this.AddHistoryOrder(Order);
}
return;
}
TICKET_TYPE OrderSend( const string&, const int &Type, const double dVolume, const double Price, const int &SlipPage, const double SL, const double TP,
const string &comment, const MAGIC_TYPE &magic, const datetime &dExpiration )
{
TICKET_TYPE Res = -1;
if (this.Orders[this.AmountOrders].Create((ENUM_ORDER_TYPE)Type, dVolume, Price, SL, TP, magic, comment, this.CurrentTick))
{
Res = this.Orders[this.AmountOrders].OrderTicket();
if (this.Orders[this.AmountOrders++].IsPosition())
{
if (Netting)
this.CloseBy();
this.IsChange();
}
else
this.Check();
}
//if (pCallBack != INVALID_HANDLE)
// if (Res > 0)
// pCallBack.OnSuccess(Res, this.Orders[this.AmountOrders]);
//if (Res < 0)
// pCallBack.OnFailure(Res,
return(Res);
}
void Stop( void )
{
for (int i = 0; i < this.AmountOrders; i++)
this.Orders[i].Stop(this.CurrentTick);
this.IsChange();
// 45AL <>6=> C60BL <0AA82 8AB>@88 B>@3>2 4;O M:>=><88 ?0<OB8.
return;
}
bool OrderSelect( const long Index, const int Select, const int Pool = MODE_TRADES )
{
return((Select == SELECT_BY_POS) ?
((Pool == MODE_TRADES) ? this.SelectByPos((int)Index) : this.SelectByPosHistory((int)Index)) :
this.SelectByTicket(Index, Pool));
}
bool OrderClose( const long Ticket, const double dLots, const double Price )
{
bool Res = false;
const int Pos = this.GetIndex(Ticket);
if (Pos >= 0)
{
const double LeftLots = this.Orders[Pos].Close(dLots, this.CurrentTick);
Res = this.Orders[Pos].IsClosed();
if (Res)
{
if (LeftLots)
{
this.Orders[this.AmountOrders] = this.Orders[Pos];
this.Orders[this.AmountOrders++].SetLots(LeftLots);
}
this.IsChange();
}
}
//if (pCallBack != INVALID_HANDLE)
// if (Res > 0)
// pCallBack.OnSuccess(Res, this.Orders[this.AmountOrders]);
return(Res);
}
bool OrderModify( const long Ticket, const double Price, const double SL, const double TP, const datetime Expiration = 0 )
{
const int Pos = this.GetIndex(Ticket);
const bool Res = (Pos >= 0) ? this.Orders[Pos].Modify(Price, SL, TP, this.CurrentTick) : false;
if (Res)
this.Check();
return(Res);
}
bool OrderCloseBy( const long Ticket, const long Opposite )
{
bool Res = false;
const int Pos1 = (Ticket != Opposite) ? this.GetIndex(Ticket) : -1;
if (Pos1 >= 0)
{
const int Pos2 = this.GetIndex(Opposite);
if (Pos2 >= 0)
{
Res = this.Orders[Pos1].CloseBy(this.Orders[Pos2], this.CurrentTick);
if (Res)
this.IsChange();
}
}
return(Res);
}
bool OrderDelete( const long Ticket )
{
bool Res = false;
const int Pos = this.GetIndex(Ticket);
if (Pos >= 0)
{
Res = this.Orders[Pos].Delete(this.CurrentTick);
if (Res)
this.IsChange();
}
return(Res);
}
int OrdersTotal2( void ) const // 2 - MT4Orders
{
return(this.AmountOrders);
}
int OrdersHistoryTotal( void ) const
{
return(this.AmountHistoryOrders);
}
#define ORDERFUNCTION(NAME,T) \
T Order##NAME( void ) const \
{ \
return(this.SelectOrder.Order##NAME()); \
}
ORDERFUNCTION(Ticket, TICKET_TYPE)
ORDERFUNCTION(Type, int)
ORDERFUNCTION(Lots, double)
ORDERFUNCTION(OpenPrice, double)
ORDERFUNCTION(OpenTimeMsc, long)
ORDERFUNCTION(OpenTime, datetime)
ORDERFUNCTION(StopLoss, double)
ORDERFUNCTION(TakeProfit, double)
ORDERFUNCTION(ClosePrice, double)
ORDERFUNCTION(CloseTimeMsc, long)
ORDERFUNCTION(CloseTime, datetime)
ORDERFUNCTION(Expiration, datetime)
ORDERFUNCTION(MagicNumber, MAGIC_TYPE)
ORDERFUNCTION(Profit, double)
ORDERFUNCTION(Commission, double)
ORDERFUNCTION(Swap, double)
ORDERFUNCTION(Symbol, string)
ORDERFUNCTION(Comment, string)
ORDERFUNCTION(OpenPriceRequest, double)
ORDERFUNCTION(ClosePriceRequest, double)
ORDERFUNCTION(OpenReason, ENUM_DEAL_REASON)
ORDERFUNCTION(CloseReason, ENUM_DEAL_REASON)
#undef ORDERFUNCTION
void OrderPrint( void ) const
{
this.SelectOrder.OrderPrint(this.CurrentTick.time);
return;
}
double AccountBalance( void ) const
{
return(::NormalizeDouble(this.Balance, 2));
}
double AccountEquity( void ) const
{
return(::NormalizeDouble(this.Equity, 2));
}
double AccountProfit( void ) const
{
return(::NormalizeDouble(this.Equity - this.Balance, 2));
}
datetime TimeCurrent( void ) const
{
return(this.CurrentTick.time);
}
datetime TimeCurrent( MqlDateTime &StructTime ) const
{
::TimeToStruct(this.CurrentTick.time, StructTime);
return(this.CurrentTick.time);
}
bool SymbolInfoTick( const string, MqlTick &Tick ) const
{
Tick = this.CurrentTick;
return(true);
}
double SymbolInfoDouble( const string Symb, const ENUM_SYMBOL_INFO_DOUBLE Property ) const
{
return((Property == SYMBOL_BID) ? this.CurrentTick.bid
: ((Property == SYMBOL_ASK) ? this.CurrentTick.ask : ::SymbolInfoDouble(Symb, Property)));
}
bool SymbolInfoDouble( const string Symb, const ENUM_SYMBOL_INFO_DOUBLE Property, double &Value ) const
{
const bool Res = (Property == SYMBOL_BID) || (Property == SYMBOL_ASK);
if (Res)
{
if (Property == SYMBOL_BID)
Value = this.CurrentTick.bid;
else
Value = this.CurrentTick.ask;
}
return(Res ? true : ::SymbolInfoDouble(Symb, Property, Value));
}
long SymbolInfoInteger( const string Symb, const ENUM_SYMBOL_INFO_INTEGER Property ) const
{
return((Property == SYMBOL_TIME) ? this.CurrentTick.time
: ((Property == SYMBOL_SPREAD) ? (long)((this.CurrentTick.ask - this.CurrentTick.bid) / _Point + 0.1) : ::SymbolInfoInteger(Symb, Property)));
}
long SymbolInfoInteger( const string Symb, const ENUM_SYMBOL_INFO_INTEGER Property, long &Value ) const
{
const bool Res = (Property == SYMBOL_TIME) || (Property == SYMBOL_SPREAD);
if (Res)
{
if (Property == SYMBOL_TIME)
Value = this.CurrentTick.time;
else
Value = (long)((this.CurrentTick.ask - this.CurrentTick.bid) / _Point + 0.1);
}
return(Res ? true : ::SymbolInfoInteger(Symb, Property, Value));
}
long AccountInfoInteger( const ENUM_ACCOUNT_INFO_INTEGER Property ) const
{
#ifdef __MQL5__
return((Property == ACCOUNT_MARGIN_MODE) ? (this.Netting ? ACCOUNT_MARGIN_MODE_RETAIL_NETTING : ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) : ::AccountInfoInteger(Property));
#else // __MQL5__
return(::AccountInfoInteger(Property));
#endif // __MQL5__
}
double AccountInfoDouble( const ENUM_ACCOUNT_INFO_DOUBLE Property ) const
{
double Res;
switch (Property)
{
case ACCOUNT_BALANCE:
Res = this.AccountBalance();
break;
case ACCOUNT_PROFIT:
Res = this.AccountProfit();
break;
case ACCOUNT_EQUITY:
Res = this.AccountEquity();
break;
case ACCOUNT_MARGIN:
Res = 0;
break;
default:
Res = ::AccountInfoDouble(Property);
}
return(Res);
}
string ToString( int LastHistoryOrders = 0, const bool Pending = true ) const
{
string Str = ORDERS::TickToString(this.CurrentTick) + "\n\n";
for (int i = 0; i < this.AmountOrders; i++)
Str += this.Orders[i].ToString(this.CurrentTick.time) + "\n";
Str += "\nBalance = " + ::DoubleToString(this.Balance, 2) +
", Equity = " + ::DoubleToString(this.Equity, 2) +
(this.AmountHistoryOrders && (this.HistoryOrders[0].OrderType() == OP_BALANCE)
? ", Profit = " + ::DoubleToString(this.Equity - this.HistoryOrders[0].OrderProfit(), 2) : NULL) +
(this.AmountHistoryOrders ? ", " + ::TimeToString(this.HistoryOrders[0].OrderOpenTime(), TIME_DATE) + " - " + (string)this.TimeCurrent() : NULL);
if (LastHistoryOrders > 0)
for (int i = this.AmountHistoryOrders - 1; (i >= 0) && LastHistoryOrders; i--)
if (Pending || (this.HistoryOrders[i].OrderType() <= OP_SELL) || (this.HistoryOrders[i].OrderType() == OP_BALANCE))
{
Str += "\n" + this.HistoryOrders[i].ToString(this.CurrentTick.time);
LastHistoryOrders--;
}
return(Str);
}
int ToFileCSV(const string fName = "orders\\orders.csv")
{
int fHandle = FileOpen(fName,FILE_WRITE|FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_CSV,'\t',CP_UTF8);
if(fHandle == INVALID_HANDLE){
PrintFormat("?5@0F8O FileOpen =5C40G=0, >H81:0 %s",GetLastError());
return -1;
}
for( int i = 0; i < this.AmountOrders; i++)
{
this.Orders[i].ToFileCSV(fHandle);
}
FileClose(fHandle);
return this.AmountOrders;
}
int FromFileCSV(const string fName = "orders\\orders.csv")
{
int fHandle = FileOpen(fName, FILE_SHARE_READ|FILE_READ|FILE_CSV,'\t',CP_UTF8);
if(fHandle == INVALID_HANDLE){
PrintFormat("?5@0F8O FileOpen =5C40G=0, >H81:0 %s",GetLastError());
return -1;
}
this.AmountOrders = 0;
while( FileIsEnding(fHandle) )
{
if (this.Orders[this.AmountOrders].FromFileCSV(fHandle))
{
PrintFormat("?5@0F8O From FileCSV =5C40G=0, >H81:0 %s",GetLastError());
break;
}
this.AmountOrders++;
}
FileClose(fHandle);
return this.AmountOrders;
}
};
#undef RESERVE_HISTORY_ORDERS
#undef MAX_ORDERS
#endif //ORDERS_MQH