Virtual_by_fxsaber/Include/fxsaber/Virtual/Virtual.mqh

174 KiB

// #define VIRTUAL_TESTER // Запуск в виртуальном торговом окружении
// #define VIRTUAL_LIMITS_TP_SLIPPAGE // Лимитники и TP исполняются по первой цене акцепта - положительные проскальзывания
// #define VIRTUAL_CLOSEALL_BYEND // Закрывает принудительно все ордера в конце тестирования
// #define VIRTUAL_ONTESTER_FORMULA ::AccountBalance() // Что возвращать в OnTester в режиме VIRTUAL_TESTER
// #define VIRTUAL_SNAPSHOT_REFRESHTIME 1000 // Время жизни снепшота для обновления. В MT5 требует подключенной MT4Orders.mqh
// #define VIRTUAL_SNAPSHOT_WITHOUT_HISTORY // Отказ от снепшота истории для повышения производительности
// #define VIRTUAL_TESTER_FAST // Возможно, это ускорит советник в Тестере.
// #define VIRTUAL_COUNTER // Счетчик.
// #define VIRTUAL_ALTERNATIVE // Альтернативная скорость расчетов
// #define VIRTUAL_NOCHECK_NULL // Не проверять (VIRTUAL::SelectOrders != NULL) - ускорение.
// #define VIRTUAL_SELECTORDERS_OBJECT // Работа с выбраным окружением без указателей, отключает мультивалютку.
// #define VIRTUAL_ORDERSELECT_WITHOUT_COPY // OrderSelect без копирования ORDER-структуры, отключает мультивалютку.
 
#ifndef __MQL5__
#ifdef VIRTUAL_SELECTORDERS_OBJECT
#undef VIRTUAL_SELECTORDERS_OBJECT
#endif // #ifdef VIRTUAL_SELECTORDERS_OBJECT
#endif // #ifndef __MQL5__
 
#ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
#include "Snapshot.mqh"
#endif // #ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
 
#ifdef VIRTUAL_SELECTORDERS_OBJECT
#include "Orders.mqh"
 
#define VIRTUAL_SELECTORDERS_MACROS(A, B) B
#else // #ifdef VIRTUAL_SELECTORDERS_OBJECT
#ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY
#include "Orders.mqh"
#else // #ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY
#include "Multi_Orders.mqh"
#endif // #ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY #else
 
#define VIRTUAL_SELECTORDERS_MACROS(A, B) A
#endif // #ifdef VIRTUAL_SELECTORDERS_OBJECT #else
 
#define __VIRTUAL__
 
#ifdef __MQL5__
#ifndef OP_BUY
#define OP_BUY ORDER_TYPE_BUY
#endif // OP_BUY
 
#ifndef OP_SELL
#define OP_SELL ORDER_TYPE_SELL
#endif // OP_SELL
 
#ifndef OP_BUYLIMIT
#define OP_BUYLIMIT ORDER_TYPE_BUY_LIMIT
#endif // OP_BUYLIMIT
 
#ifndef OP_SELLLIMIT
#define OP_SELLLIMIT ORDER_TYPE_SELL_LIMIT
#endif // OP_SELLLIMIT
 
#ifndef OP_BUYSTOP
#define OP_BUYSTOP ORDER_TYPE_BUY_STOP
#endif // OP_BUYSTOP
 
#ifndef OP_SELLSTOP
#define OP_SELLSTOP ORDER_TYPE_SELL_STOP
#endif // OP_SELLSTOP
 
#define VIRTUAL_POINTER VIRTUAL::POINTER
#else // __MQL5__
#ifndef Bid
#define Bid SymbolInfoDouble(_Symbol, SYMBOL_BID)
#endif // Bid
 
#ifndef Ask
#define Ask SymbolInfoDouble(_Symbol, SYMBOL_ASK)
#endif // Ask
 
// https://www.mql5.com/ru/forum/438060/page5#comment_44601847
#define private public
#define protected public
 
#define VIRTUAL_POINTER POINTER
#endif // __MQL5__
 
class VIRTUAL_DELETE
{
private:
/*
static int VirtualMemory( const int NewValue = 0, const int Value = 0, const bool SetFlag = false )
{
static int PrevValue[100];
static int Pos = 0;
 
if (SetFlag)
PrevValue[Pos++] = Value;
 
return(SetFlag ? NewValue : PrevValue[--Pos]);
}
*/
public:
~VIRTUAL_DELETE( void )
{
VIRTUAL::DeleteAll();
 
#ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
VIRTUAL::SnapshotDelete();
#endif // #ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
}
/*
template <typename T>
static T VirtualMacrosFunc( const T Value )
{
VIRTUAL::SelectByHandle(VIRTUAL_DELETE::VirtualMemory());
 
return(Value);
}
*/
template <typename T>
static const T VirtualMacrosFunc2( const bool, const T /*&*/ Value, const bool )
{
return(Value);
}
};
 
// #define _V(A, B) VIRTUAL_DELETE::VirtualMacrosFunc(VIRTUAL::SelectByHandle(VIRTUAL_DELETE::VirtualMemory((A), VIRTUAL::GetHandle(), true)) ? (B) : NULL)
 
// Выбирает A(Handle)-окружение, выполняет B и возвращает предыдущее окружение.
#define _V(A, B) VIRTUAL_DELETE::VirtualMacrosFunc2(VIRTUAL::SelectFromStack(), (B), VIRTUAL::ToStack() && VIRTUAL::SelectByHandle(A))
 
// Выбирает A(POINTER)-окружение, выполняет B и возвращает предыдущее окружение.
#define _VP(A, B) VIRTUAL_DELETE::VirtualMacrosFunc2(VIRTUAL::SelectFromStack(), (B), VIRTUAL::ToStack() && (A).Select())
 
// Выбирает A(Index)-окружение, выполняет B и возвращает предыдущее окружение.
#define _VI(A, B) VIRTUAL_DELETE::VirtualMacrosFunc2(VIRTUAL::SelectFromStack(), (B), VIRTUAL::ToStack() && VIRTUAL::SelectByIndex(A))
 
// Выбирает A(HANDLE_INDEX)-окружение, выполняет B и возвращает предыдущее окружение.
#define _VI2(A, B) VIRTUAL_DELETE::VirtualMacrosFunc2(VIRTUAL::SelectFromStack(), (B), VIRTUAL::ToStack() && VIRTUAL::SelectByIndex((A).Index, (A).Handle))
 
#define TEMPNAME(A) TmpVS##A
 
#define _VS(A) \
class VS \
{ \
public: \
VS( const int Handle ) \
{ \
VIRTUAL::ToStack(); \
\
VIRTUAL::SelectByHandle(Handle); \
} \
\
~VS( void ) \
{ \
VIRTUAL::SelectFromStack(); \
} \
} TEMPNAME(__LINE__)(A);
 
#define _VSP(A) \
class VSP \
{ \
public: \
VSP( const VIRTUAL_POINTER &Pointer ) \
{ \
VIRTUAL::ToStack(); \
\
Pointer.Select(); \
} \
\
~VSP( void ) \
{ \
VIRTUAL::SelectFromStack(); \
} \
} TEMPNAME(__LINE__)(A);
 
#define _VSI(A) \
class VSI \
{ \
public: \
VSI( const int Index ) \
{ \
VIRTUAL::ToStack(); \
\
VIRTUAL::SelectByIndex(Index); \
} \
\
~VSI( void ) \
{ \
VIRTUAL::SelectFromStack(); \
} \
} TEMPNAME(__LINE__)(A);
 
#define _VSI2(A) \
class VSI2 \
{ \
public: \
VSI2( VIRTUAL::HANDLE_INDEX &HandleIndex ) \
{ \
VIRTUAL::ToStack(); \
\
VIRTUAL::SelectByIndex(HandleIndex.Index, \
HandleIndex.Handle); \
} \
\
~VSI2( void ) \
{ \
VIRTUAL::SelectFromStack(); \
} \
} TEMPNAME(__LINE__)(A);
 
// #undef TEMPNAME // Нельзя расскомментировать!!!
 
class TICKS_ARRAY
{
public:
MqlTick Ticks[];
};
 
class VIRTUAL
{
private:
static const VIRTUAL_DELETE VirtualDelete;
static ORDERS* Orders[];
static int CountHandles;
 
static ORDERS* StackOrders[100];
static int StackPos;
 
#ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
static SNAPSHOT* SnapshotPtr;
static SNAPSHOT* SnapshotPtrBackup;
#endif // #ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
 
static ORDERS* GetMyPointer( ORDERS* Ptr )
{
return(VIRTUAL_SELECTORDERS_MACROS(Ptr, (VIRTUAL::SelectOrders.MyPointer == Ptr)
? &VIRTUAL::SelectOrders
: Ptr));
}
 
protected:
static ORDERS VIRTUAL_SELECTORDERS_MACROS(*SelectOrders, SelectOrders);
 
static bool IsVirtual( void )
{
#ifdef __MQL5__
#ifdef __MT4ORDERS__
return((bool)(VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)));
#else // #ifdef __MT4ORDERS__
#ifdef VIRTUAL_NOCHECK_NULL
return(true);
#else // #ifdef VIRTUAL_NOCHECK_NULL
return((bool)(VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)));
#endif // #ifdef VIRTUAL_NOCHECK_NULL #else
#endif // #ifdef __MT4ORDERS__ #else
#else // #ifdef __MQL5__
return((bool)(VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)));
#endif // #ifdef __MQL5__ #else
}
 
public:
// DBL_MIN - тип Неттинга и размер баланса берутся с текущего счета.
static int Create( const double dBalance = DBL_MIN, datetime StartTime = 0, const bool bNetting = false )
{
// static int CountHandles = 1; // Нельзя, т.к. будет инициализация после всех глобальных переменных.
 
const int Pos = ::ArrayResize(VIRTUAL::Orders, VIRTUAL::Total() + 1) - 1;
 
if (!StartTime)
StartTime = ::MQLInfoInteger(MQL_TESTER) ? ::TimeCurrent() : (datetime)::SymbolInfoInteger(_Symbol, SYMBOL_TIME);
 
const bool FlagRes = ((VIRTUAL::Orders[Pos] = new ORDERS(VIRTUAL::CountHandles, StartTime)) != NULL);
 
if (FlagRes)
{
#ifdef VIRTUAL_TESTER
MqlTick Tick = {};
 
::SymbolInfoTick(_Symbol, Tick); // Даже если получить тик не удалось.
 
if (::MQLInfoInteger(MQL_TESTER)) // Без этого на реале можно нарваться на ситуацию, когда виртуальный тик имеет время больше реального.
{
Tick.time = ::TimeCurrent(); // Для получения корректного TimeCurrent() в OnInit и ранее.
Tick.time_msc = (long)Tick.time * 1000;
}
 
VIRTUAL::Orders[Pos].NewTick(Tick); // Для полноценной работы в виртуале в OnInit и ранее.
#endif // VIRTUAL_TESTER
 
VIRTUAL::Orders[Pos].Set(dBalance, bNetting);
 
VIRTUAL::CountHandles++;
}
else
::ArrayResize(VIRTUAL::Orders, Pos);
 
return(FlagRes ? VIRTUAL::Orders[Pos].Handle : -1);
}
 
static int Create( const string FileName, const bool FileCommon = false )
{
const int Pos = ::ArrayResize(VIRTUAL::Orders, VIRTUAL::Total() + 1) - 1;
 
const bool FlagRes = ((VIRTUAL::Orders[Pos] = new ORDERS(VIRTUAL::CountHandles)) != NULL) &&
VIRTUAL::Orders[Pos].Load(FileName, FileCommon);
 
if (!FlagRes)
{
if (VIRTUAL::Orders[Pos] != NULL)
delete VIRTUAL::Orders[Pos];
 
::ArrayResize(VIRTUAL::Orders, Pos);
}
else
VIRTUAL::CountHandles++;
 
return(FlagRes ? VIRTUAL::Orders[Pos].Handle : -1);
}
 
// DBL_MIN - тип Неттинга и размер баланса берутся с текущего счета.
static int CreateMulti( const SYMBOL_BASE &sSymbols[], const double dBalance = DBL_MIN,
datetime StartTime = 0, const bool bNetting = false )
{
// static int CountHandles = 1; // Нельзя, т.к. будет инициализация после всех глобальных переменных.
 
const int Size = ::ArraySize(sSymbols);
int Res = -1;
 
if (Size)
{
const int Pos = ::ArrayResize(VIRTUAL::Orders, VIRTUAL::Total() + 1) - 1;
 
if (!StartTime)
StartTime = ::TimeCurrent();
 
if ((VIRTUAL::Orders[Pos] = (Size > 1) ?
#ifdef VIRTUAL_SELECTORDERS_OBJECT
NULL
#else // #ifdef VIRTUAL_SELECTORDERS_OBJECT
#ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY
NULL
#else // #ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY
new MULTI_ORDERS(VIRTUAL::CountHandles, StartTime, sSymbols)
#endif // #ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY #else
#endif // #ifdef VIRTUAL_SELECTORDERS_OBJECT #else
: new ORDERS(VIRTUAL::CountHandles, StartTime, sSymbols[0])) != NULL)
{
VIRTUAL::Orders[Pos].Set(dBalance, bNetting);
 
VIRTUAL::CountHandles++;
 
Res = VIRTUAL::Orders[Pos].Handle;
}
else
::ArrayResize(VIRTUAL::Orders, Pos);
}
 
return(Res);
}
 
static int CreateMulti( const string &sSymbols[], const double dBalance = DBL_MIN,
datetime StartTime = 0, const bool bNetting = false )
{
// static int CountHandles = 1; // Нельзя, т.к. будет инициализация после всех глобальных переменных.
 
SYMBOL_BASE sSymbols2[];
 
for (uint i = ::ArrayResize(sSymbols2, ::ArraySize(sSymbols)); (bool)i--;)
sSymbols2[i] = sSymbols[i];
 
return(VIRTUAL::CreateMulti(sSymbols2, dBalance, StartTime, bNetting));
}
 
static int CreateSingle( const SYMBOL_BASE &sSymbol, const double dBalance = DBL_MIN,
datetime StartTime = 0, const bool bNetting = false )
{
// const SYMBOL_BASE sSymbols[] = {sSymbol}; // https://www.mql5.com/ru/forum/462835/page27#comment_52788664
SYMBOL_BASE sSymbols[1];
sSymbols[0] = sSymbol;
 
return(VIRTUAL::CreateMulti(sSymbols, dBalance, StartTime, bNetting));
}
 
static int CreateSingle( const string sSymbol = NULL, const double dBalance = DBL_MIN,
datetime StartTime = 0, const bool bNetting = false )
{
// const SYMBOL_BASE sSymbols[] = {sSymbol}; // https://www.mql5.com/ru/forum/462835/page27#comment_52788664
// const SYMBOL_BASE sSymbol2 = sSymbol; // https://www.mql5.com/ru/forum/492248/page3#comment_57718727
SYMBOL_BASE sSymbol2; sSymbol2 = sSymbol;
 
return(VIRTUAL::CreateSingle(sSymbol2, dBalance, StartTime, bNetting));
}
 
#ifdef __TYPETOBYTES__
template <typename T>
static int Create( const T &Array[] )
{
const int Pos = ::ArrayResize(VIRTUAL::Orders, VIRTUAL::Total() + 1) - 1;
 
const bool FlagRes = ((VIRTUAL::Orders[Pos] = new ORDERS(VIRTUAL::CountHandles)) != NULL) &&
VIRTUAL::Orders[Pos].Load(Array);
 
if (!FlagRes)
{
if (VIRTUAL::Orders[Pos] != NULL)
delete VIRTUAL::Orders[Pos];
 
::ArrayResize(VIRTUAL::Orders, Pos);
}
else
VIRTUAL::CountHandles++;
 
return(FlagRes ? VIRTUAL::Orders[Pos].Handle : -1);
}
#endif // #ifdef __TYPETOBYTES__
 
static bool CopyTo( const int Handle )
{
bool Res = false;
ORDERS* const PrevOrders = VIRTUAL_SELECTORDERS_MACROS(VIRTUAL::SelectOrders, VIRTUAL::SelectOrders.MyPointer);
 
if ((PrevOrders != NULL) && !(Res = (Handle == PrevOrders.Handle)) && VIRTUAL::SelectByHandle(Handle))
{
Res = VIRTUAL::SelectOrders.CopyFrom(PrevOrders);
 
VIRTUAL::SelectOrders = PrevOrders;
}
 
return(Res);
}
 
static bool AddTo( const int Handle )
{
ORDERS* const PrevOrders = VIRTUAL_SELECTORDERS_MACROS(VIRTUAL::SelectOrders, (VIRTUAL::SelectOrders.Handle == Handle)
? &VIRTUAL::SelectOrders
: VIRTUAL::SelectOrders.MyPointer);
const bool Res = (PrevOrders != NULL) && Handle && VIRTUAL::SelectByHandle(Handle); // https://www.mql5.com/ru/forum/438066/page27#comment_45059555
 
if (Res)
{
VIRTUAL::SelectOrders += PrevOrders;
 
VIRTUAL::SelectOrders = PrevOrders;
}
 
return(Res);
}
 
static int Summary( const string ID = NULL )
{
const int Size = VIRTUAL::Total();
const int Handle = Size ? VIRTUAL::Create() : -1;
 
if ((Handle != -1) && VIRTUAL::Orders[Size].CopyFrom(VIRTUAL_SELECTORDERS_MACROS(VIRTUAL::Orders[0], VIRTUAL::GetMyPointer(VIRTUAL::Orders[0]))) && (Size > 1))
{
for (int i = 1; i < Size; i++)
VIRTUAL::Orders[Size] += VIRTUAL_SELECTORDERS_MACROS(VIRTUAL::Orders[i], VIRTUAL::GetMyPointer(VIRTUAL::Orders[i]));
 
VIRTUAL::Orders[Size].SetID(ID);
}
 
return(Handle);
}
 
static bool Delete( void )
{
bool Res = (VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle));
 
if (Res)
{
const int Size = VIRTUAL::Total();
 
for (int i = 0; i < Size; i++)
if (Res = (VIRTUAL_SELECTORDERS_MACROS(VIRTUAL::Orders[i] == VIRTUAL::SelectOrders,
VIRTUAL::Orders[i].Handle == VIRTUAL::SelectOrders.Handle)))
{
delete VIRTUAL::Orders[i];
 
for (int j = i; j < Size - 1; j++)
VIRTUAL::Orders[j] = VIRTUAL::Orders[j + 1]; // ArrayCopy, ArrayRemove?
 
::ArrayResize(VIRTUAL::Orders, Size - 1);
 
VIRTUAL_SELECTORDERS_MACROS(VIRTUAL::SelectOrders = NULL, VIRTUAL::SelectOrders.ToNull());
 
break;
}
}
 
return(Res);
}
 
static void DeleteAll( void )
{
for (int i = VIRTUAL::Total() - 1; i >= 0; i--)
delete VIRTUAL::Orders[i];
 
::ArrayFree(VIRTUAL::Orders);
VIRTUAL_SELECTORDERS_MACROS(VIRTUAL::SelectOrders = NULL, VIRTUAL::SelectOrders.ToNull());
 
VIRTUAL::CountHandles = 1;
 
return;
}
 
#ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
// По какой-то причине нельзя вызывать эту функцию на глобальном уровне (вне On-функций) - не срабатывает.
// Symb = "" - все символы.
static ulong Snapshot( const uint RefreshTime = VIRTUAL_SNAPSHOT_REFRESHTIME, const MAGIC_TYPE Magic = -1,
const bool HistoryInit = false, const string Symb = NULL, const bool CloseBy = true )
{
if (VIRTUAL::SnapshotPtr == NULL)
{
if (VIRTUAL::SnapshotPtrBackup == NULL)
VIRTUAL::SnapshotPtrBackup = VIRTUAL::SnapshotPtr = new SNAPSHOT;
else
VIRTUAL::SnapshotPtr = VIRTUAL::SnapshotPtrBackup;
}
 
return(VIRTUAL::SnapshotPtr.Snapshot(RefreshTime, Magic, HistoryInit, Symb, CloseBy));
}
 
static bool SnapshotIsActive( void )
{
return(VIRTUAL::SnapshotPtr != NULL);
}
 
static ulong SnapshotLifeTime( void )
{
return((VIRTUAL::SnapshotPtr != NULL) ? VIRTUAL::SnapshotPtr.SnapshotLifeTime() : ULONG_MAX);
}
 
static void SnapshotDelete( const bool Full = true )
{
if (VIRTUAL::SnapshotPtr != NULL)
{
if (Full)
{
delete VIRTUAL::SnapshotPtr;
 
VIRTUAL::SnapshotPtrBackup = NULL;
}
 
VIRTUAL::SnapshotPtr = NULL;
}
else if (Full && (VIRTUAL::SnapshotPtrBackup != NULL))
{
delete VIRTUAL::SnapshotPtrBackup;
 
VIRTUAL::SnapshotPtrBackup = NULL;
}
 
return;
}
 
static bool SnapshotHistory( const MAGIC_TYPE Magic = -1,
const bool HistoryInit = false, const string Symb = NULL,
const bool CloseBy = true, const datetime From = 0 )
{
return((VIRTUAL::SnapshotPtr != NULL) && VIRTUAL::SnapshotPtr.SnapshotHistory(Magic, HistoryInit, Symb, CloseBy, From));
}
#else // #ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
static ulong Snapshot( const uint RefreshTime = 0, const MAGIC_TYPE Magic = -1,
const bool HistoryInit = false, const string Symb = NULL, const bool CloseBy = true )
{
return(1); // 1 - для использования в bool-выражении.
}
 
static bool SnapshotIsActive( void )
{
return(false);
}
 
static ulong SnapshotLifeTime( void )
{
return(ULONG_MAX);
}
 
static void SnapshotDelete( const bool Full = true )
{
return;
}
 
static bool SnapshotHistory( const MAGIC_TYPE Magic = -1,
const bool HistoryInit = false, const string Symb = NULL,
const bool CloseBy = true, const datetime From = 0 )
{
return(false);
}
#endif // #ifdef VIRTUAL_SNAPSHOT_REFRESHTIME #else
 
static void NewTick( const MqlTick &Tick, const STRATEGY Strategy )
{
if (VIRTUAL::IsVirtual())
{
VIRTUAL::SelectOrders.NewTick(Tick);
 
if (Strategy != NULL)
Strategy();
}
 
return;
}
 
static void NewTick( const MqlTick &Tick)
{
if (VIRTUAL::IsVirtual())
VIRTUAL::SelectOrders.NewTick(Tick);
 
return;
}
 
static void NewTick( const MqlTick &Ticks[], const STRATEGY Strategy = NULL )
{
// https://www.mql5.com/ru/forum/462835/page24#comment_52770433
if (VIRTUAL::IsVirtual())
{
/*
const int Size = ::ArraySize(Ticks);
 
if (Strategy != NULL)
for (int i = 0; (i < Size) && (!::IsStopped()); i++)
{
VIRTUAL::SelectOrders.NewTick(Ticks[i]);
 
Strategy();
}
else
for (int i = 0; i < Size; i++)
VIRTUAL::SelectOrders.NewTick(Ticks[i]);
*/
//
 
VIRTUAL::SelectOrders.NewTick(Ticks, Strategy);
}
 
return;
}
 
static void NewTick_NoCheck( void )
{
VIRTUAL::SelectOrders.MultiTick();
 
return;
}
 
static void NewTick_NoCheck( const MqlTick &Tick )
{
VIRTUAL::SelectOrders.NewTick(Tick);
 
return;
}
 
static void NewTick( const STRATEGY Strategy = NULL )
{
_VC
static MqlTick Tick = {};
 
if ((VIRTUAL::IsVirtual()) && ::SymbolInfoTick(_Symbol, Tick))
VIRTUAL::NewTick(Tick, Strategy);
 
return;
}
 
static void MultiTick( const MqlTick &Ticks[] )
{
if (VIRTUAL::IsVirtual())
VIRTUAL::SelectOrders.MultiTick(Ticks);
 
return;
}
 
static void MultiTick( void )
{
if (VIRTUAL::IsVirtual())
VIRTUAL::SelectOrders.MultiTick();
 
return;
}
 
static void MultiTick( const uint &Index, const MqlTick &Tick )
{
if (VIRTUAL::IsVirtual())
VIRTUAL::SelectOrders.MultiTick(Index, Tick);
 
return;
}
 
static void MultiTick( const uint &Index )
{
if (VIRTUAL::IsVirtual())
VIRTUAL::SelectOrders.MultiTick(Index);
 
return;
}
 
static void MultiTick_NoCheck( const uint &Index )
{
VIRTUAL::SelectOrders.MultiTick(Index);
 
return;
}
 
static void NewTickMulti( const MqlTick &Ticks[], const STRATEGY_MULTI StrategyMulti = NULL )
{
if (VIRTUAL::IsVirtual())
VIRTUAL::SelectOrders.NewTickMulti(Ticks, StrategyMulti);
 
return;
}
 
static void NewTickMulti( const TICKS_ARRAY &Ticks, const STRATEGY_MULTI StrategyMulti = NULL )
{
VIRTUAL::NewTickMulti(Ticks.Ticks, StrategyMulti);
 
return;
}
 
static void NewTickMulti( const TICKS_ARRAY &TicksArray[], const STRATEGY_MULTI StrategyMulti = NULL )
{
if (VIRTUAL::IsVirtual())
{
if (!VIRTUAL::SelectOrders.IsSingle())
{
string Symbols[];
const int Amount = ::MathMin(::ArraySize(TicksArray), VIRTUAL::SelectOrders.GetSymbols(Symbols));
 
int Count = 0;
 
int Size[];
bool Res[];
int Pos[];
 
::ArrayResize(Pos, Amount);
::ArrayInitialize(Pos, 0);
 
for (uint i = ::ArrayResize(Res, ::ArrayResize(Size, Amount)); (bool)i--;)
{
Size[i] = ::ArraySize(TicksArray[i].Ticks);
 
if (Res[i] = Size[i])
Count++;
}
 
if (Count == 1)
{
const uint Index = ::ArrayMaximum(Size);
const int SizeSingle = Size[Index];
 
if (StrategyMulti != NULL)
{
const string Symb = Symbols[Index];
 
for (int i = 0; i < SizeSingle; i++)
{
VIRTUAL::SelectOrders.MultiTick(Index, TicksArray[Index].Ticks[i]);
 
StrategyMulti(Symb, Index);
}
}
else
for (int i = 0; i < SizeSingle; i++)
VIRTUAL::SelectOrders.MultiTick(Index, TicksArray[Index].Ticks[i]);
}
else if (StrategyMulti != NULL)
while (Count)
{
MqlTick MinTick = {0, 0, 0, 0, 0, LONG_MAX};
uint Index = -1;
 
// https://www.mql5.com/ru/forum/170952/page247#comment_52774236
for (uint i = Amount; (bool)i--;)
if (Res[i])
{
// Количество вызовов в ~(1+Amount)/2 раз больше общего числа элементов.
const MqlTick Tick = TicksArray[i].Ticks[Pos[i]];
 
if (Tick.time_msc < MinTick.time_msc)
{
MinTick = Tick;
 
Index = i;
}
}
 
if (!(Res[Index] = (++Pos[Index] != Size[Index])))
Count--;
 
VIRTUAL::SelectOrders.MultiTick(Index, MinTick);
StrategyMulti(Symbols[Index], Index);
}
else
while (Count)
{
MqlTick MinTick = {0, 0, 0, 0, 0, LONG_MAX};
uint Index = -1;
 
// https://www.mql5.com/ru/forum/170952/page247#comment_52774236
for (uint i = Amount; (bool)i--;)
if (Res[i])
{
// Количество вызовов в ~(1+Amount)/2 раз больше общего числа элементов.
const MqlTick Tick = TicksArray[i].Ticks[Pos[i]];
 
if (Tick.time_msc < MinTick.time_msc)
{
MinTick = Tick;
 
Index = i;
}
}
 
if (!(Res[Index] = (++Pos[Index] != Size[Index])))
Count--;
 
VIRTUAL::SelectOrders.MultiTick(Index, MinTick);
}
}
else if (::ArraySize(TicksArray))
VIRTUAL::SelectOrders.NewTickMulti(TicksArray[0].Ticks, StrategyMulti);
}
 
return;
}
 
static int GetAmountSymbols( void )
{
return((VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)) ? VIRTUAL::SelectOrders.GetAmountSymbols() : 1);
}
 
static int GetSymbols( string &Symbols[] )
{
int Res;
 
if (VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle))
Res = VIRTUAL::SelectOrders.GetSymbols(Symbols);
else
{
Res = ::ArrayResize(Symbols, 1);
 
Symbols[0] = _Symbol;
}
 
return(Res);
}
 
static string GetSymbolByIndex( const int Index )
{
return((VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)) ? VIRTUAL::SelectOrders.GetSymbolByIndex(Index)
: (!Index ? _Symbol : NULL));
}
 
static int Total( void )
{
return(::ArraySize(VIRTUAL::Orders));
}
 
static int GetHandle( void )
{
_VC
return(VIRTUAL_SELECTORDERS_MACROS((VIRTUAL::SelectOrders != NULL) ? VIRTUAL::SelectOrders.Handle : 0,
VIRTUAL::SelectOrders.Handle));
}
 
static int GetHandleByIndex( const int Index )
{
return((Index >= 0) && (Index <= VIRTUAL::Total()) ? (Index ? VIRTUAL::Orders[Index - 1].Handle : 0) : -1);
}
 
static bool IsExist( const int Handle )
{
return(VIRTUAL::GetNumber(Handle) > 0);
}
 
static bool GetCurrentTick( MqlTick &Tick )
{
_VC
return((VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)) ? VIRTUAL::SelectOrders.SymbolInfoTick(_Symbol, Tick) : ::SymbolInfoTick(_Symbol, Tick));
}
 
static bool IsNetting( void )
{
static const bool RealNetting =
#ifdef __MQL5__
!((ENUM_ACCOUNT_MARGIN_MODE)::AccountInfoInteger(ACCOUNT_MARGIN_MODE) == ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
#else // __MQL5__
false
#endif //__MQL5__
;
 
return((VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)) ? VIRTUAL::SelectOrders.IsNetting() : RealNetting);
}
 
static string ToString( const int LastHistoryOrders = 0, const bool Pending = true )
{
return((VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)) ? VIRTUAL::SelectOrders.ToString(LastHistoryOrders, Pending) : NULL);
}
 
// Вывод статистики.
static string StatToString( const int Amount = 1 )
{
return((VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)) ? VIRTUAL::SelectOrders.StatToString(Amount) : NULL);
}
 
static int GetNumber( const int Handle )
{
int Res = Handle ? -1 : 0;
 
if (Handle)
for (int i = VIRTUAL::Total() - 1; i >= 0; i--)
if (VIRTUAL::Orders[i].Handle == Handle)
{
Res = i + 1;
 
break;
}
 
return(Res);
}
 
static bool SelectByIndex( const int Index = 0 )
{
const bool Res = (Index >= 0) && (Index <= VIRTUAL::Total());
 
if (Res)
VIRTUAL::SelectOrders = Index ? VIRTUAL::Orders[Index - 1] : NULL;
 
return(Res);
}
 
static bool SelectByIndex( int &Index, const int Handle )
{
bool Res = (VIRTUAL::GetHandleByIndex(Index) == Handle) && VIRTUAL::SelectByIndex(Index);
 
if (!Res)
Index = (Res = VIRTUAL::SelectByHandle(Handle)) ? VIRTUAL::GetNumber(Handle) : -1;
 
return(Res);
}
 
static bool SelectByHandle( const int Handle = 0 )
{
_VC
bool Res = (Handle >= 0);
 
if (Res && (Handle != VIRTUAL::GetHandle()))
{
if (Handle)
{
Res = false;
 
const int Size = VIRTUAL::Total();
 
for (int i = 0; i < Size; i++)
if (Res = (VIRTUAL::Orders[i].Handle == Handle))
{
VIRTUAL::SelectOrders = VIRTUAL::Orders[i];
 
break;
}
}
else
VIRTUAL::SelectOrders = NULL;
}
 
return(Res);
}
 
static bool Stop( void )
{
return((VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)) && VIRTUAL::SelectOrders.Stop());
}
 
static bool IsHistoryChanged( void )
{
return((VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)) && VIRTUAL::SelectOrders.IsHistoryChanged());
}
 
// https://www.mql5.com/ru/forum/282062/page72#comment_56065971
static bool IsChanged( void )
{
return((VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(== NULL, .Handle == 0)) || VIRTUAL::SelectOrders.GetFlagChange());
}
 
// С какого времени считать TesterStatistics.
static bool InitStat( const long StartTime )
{
return((VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)) ? VIRTUAL::SelectOrders.InitStat(StartTime) : false);
}
 
static double VirtualTesterStatistics( const ENUM_STATISTICS StatID )
{
return((VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)) ? VIRTUAL::SelectOrders.TesterStatistics(StatID) : ::TesterStatistics(StatID));
}
 
static bool VIRTUAL::InitStat( const datetime StartTime )
{
return(VIRTUAL::InitStat((long)StartTime * 1000));
}
 
static datetime VirtualTimeCurrent( void )
{
_VC
return(VIRTUAL::IsVirtual() ? VIRTUAL::SelectOrders.TimeCurrent() : ::TimeCurrent());
}
 
static datetime VirtualTimeCurrent( MqlDateTime &StructTime )
{
return(VIRTUAL::IsVirtual() ? VIRTUAL::SelectOrders.TimeCurrent(StructTime) : ::TimeCurrent(StructTime));
}
 
static datetime VirtualTimeTradeServer( void )
{
_VC
 
#ifdef __MQL5__
return(VIRTUAL::IsVirtual() ? VIRTUAL::SelectOrders.TimeCurrent() : ::TimeTradeServer());
#else // #ifdef __MQL5__
return(VIRTUAL::IsVirtual() ? VIRTUAL::SelectOrders.TimeCurrent() : ::TimeCurrent());
#endif // #ifdef __MQL5__ #else
}
 
static datetime VirtualTimeTradeServer( MqlDateTime &StructTime )
{
#ifdef __MQL5__
return(VIRTUAL::IsVirtual() ? VIRTUAL::SelectOrders.TimeTradeServer(StructTime) : ::TimeTradeServer(StructTime));
#else // #ifdef __MQL5__
return(VIRTUAL::IsVirtual() ? VIRTUAL::SelectOrders.TimeTradeServer(StructTime) : ::TimeCurrent(StructTime));
#endif // #ifdef __MQL5__ #else
}
 
static bool VirtualSymbolInfoTick( const string Symb, MqlTick &Tick )
{
_VC
return(VIRTUAL::IsVirtual() ? VIRTUAL::SelectOrders.SymbolInfoTick(Symb, Tick) : ::SymbolInfoTick(Symb, Tick));
}
 
static double VirtualSymbolInfoDouble( const string Symb, const ENUM_SYMBOL_INFO_DOUBLE Property )
{
_VC
return(VIRTUAL::IsVirtual() ? VIRTUAL::SelectOrders.SymbolInfoDouble(Symb, Property) : ::SymbolInfoDouble(Symb, Property));
}
 
static bool VirtualSymbolInfoDouble( const string Symb, const ENUM_SYMBOL_INFO_DOUBLE Property, double &Value )
{
_VC
return(VIRTUAL::IsVirtual() ? VIRTUAL::SelectOrders.SymbolInfoDouble(Symb, Property, Value) : ::SymbolInfoDouble(Symb, Property, Value));
}
 
static long VirtualSymbolInfoInteger( const string Symb, const ENUM_SYMBOL_INFO_INTEGER Property )
{
_VC
return(VIRTUAL::IsVirtual() ? VIRTUAL::SelectOrders.SymbolInfoInteger(Symb, Property) : ::SymbolInfoInteger(Symb, Property));
}
 
static bool VirtualSymbolInfoInteger( const string Symb, const ENUM_SYMBOL_INFO_INTEGER Property, long &Value )
{
_VC
return(VIRTUAL::IsVirtual() ? VIRTUAL::SelectOrders.SymbolInfoInteger(Symb, Property, Value) : ::SymbolInfoInteger(Symb, Property, Value));
}
 
static long VirtualAccountInfoInteger( const ENUM_ACCOUNT_INFO_INTEGER Property )
{
_VC
// Отказ от такого варианта из-за возможного вызова в OnInit и
// возможности не вызывать на каждом тике.
// return(VIRTUAL::IsVirtual() ? VIRTUAL::SelectOrders.AccountInfoInteger(Property) : ::AccountInfoInteger(Property));
return((VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)) ? VIRTUAL::SelectOrders.AccountInfoInteger(Property) : ::AccountInfoInteger(Property));
}
 
static double VirtualAccountInfoDouble( const ENUM_ACCOUNT_INFO_DOUBLE Property )
{
_VC
return(VIRTUAL::IsVirtual() ? VIRTUAL::SelectOrders.AccountInfoDouble(Property) : ::AccountInfoDouble(Property));
}
 
static double VirtualAccountBalance( void )
{
_VC
return(VIRTUAL::IsVirtual() ? VIRTUAL::SelectOrders.AccountBalance() : ::AccountInfoDouble(ACCOUNT_BALANCE));
}
 
static double VirtualAccountEquity( void )
{
_VC
return(VIRTUAL::IsVirtual() ? VIRTUAL::SelectOrders.AccountEquity() : ::AccountInfoDouble(ACCOUNT_EQUITY));
}
 
static double VirtualAccountProfit( void )
{
_VC
return(VIRTUAL::IsVirtual() ? VIRTUAL::SelectOrders.AccountProfit() : ::AccountInfoDouble(ACCOUNT_PROFIT));
}
 
static bool VirtualTesterDeposit( const double Deposit )
{
return((VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)) ? VIRTUAL::SelectOrders.TesterDeposit(Deposit) :
#ifdef __MQL5__
::TesterDeposit(Deposit)
#else // __MQL5__
false
#endif // __MQL5__
);
}
 
static bool VirtualTesterWithdrawal( const double Withdraw )
{
return((VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)) ? VIRTUAL::SelectOrders.TesterWithdrawal(Withdraw) :
#ifdef __MQL5__
::TesterWithdrawal(Withdraw)
#else // __MQL5__
false
#endif // __MQL5__
);
}
 
// Можно сохранить выбранный ордер в static ORDER_BASE VIRTUAL::SelectOrder,
// а потом избежать оверхеда работы с указателем VIRTUAL::SelectOrders,
// забирая Order*()-значения из сохраненной структуры. Но это решение еще медленнее по замерам.
static bool VirtualOrderSelect( const TICKET_TYPE Index, const int Select, const int Pool )
{
_VC
return(VIRTUAL::IsVirtual() ? VIRTUAL::SelectOrders.OrderSelect(Index, Select, Pool) :
#ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
(VIRTUAL::SnapshotPtr != NULL) ?
#ifdef __MQL5__ // Выбор по тикету в MT5 - разнообразный набор вариантов.
(Select == SELECT_BY_TICKET) ? ::OrderSelect(Index, Select, Pool) && VIRTUAL::SnapshotPtr.CopyOrder() // Будет выбран не из снепшота!
:
#endif // #ifdef __MQL5__
((((Index == INT_MIN) || (Index == INT_MAX)) && (Pool == MODE_TRADES) &&
::OrderSelect(Index, Select, Pool) &&
#ifdef VIRTUAL_SNAPSHOT_WITHOUT_HISTORY
VIRTUAL::SnapshotPtr.CopyOrder(true))
#else // #ifdef VIRTUAL_SNAPSHOT_WITHOUT_HISTORY
VIRTUAL::SnapshotPtr.CopyOrder())
#endif // #ifdef VIRTUAL_SNAPSHOT_WITHOUT_HISTORY #else
|| VIRTUAL::SnapshotPtr.OrderSelect(Index, Select, Pool))
:
#endif // #ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderSelect(Index, Select, Pool)
#else // __MT4ORDERS__
false
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderSelect(Index, Select, Pool)
#endif // __MQL5__
);
}
 
static bool VirtualOrderSelect( const TICKET_TYPE Index, const int Select )
{
_VC
return(VIRTUAL::IsVirtual() ? VIRTUAL::SelectOrders.OrderSelect(Index, Select) :
#ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
(VIRTUAL::SnapshotPtr != NULL) ?
#ifdef __MQL5__ // Выбор по тикету в MT5 - разнообразный набор вариантов.
(Select == SELECT_BY_TICKET) ? ::OrderSelect(Index, Select) && VIRTUAL::SnapshotPtr.CopyOrder() // Будет выбран не из снепшота!
:
#endif // #ifdef __MQL5__
((((Index == INT_MIN) || (Index == INT_MAX)) &&
::OrderSelect(Index, Select) &&
#ifdef VIRTUAL_SNAPSHOT_WITHOUT_HISTORY
VIRTUAL::SnapshotPtr.CopyOrder(true))
#else // #ifdef VIRTUAL_SNAPSHOT_WITHOUT_HISTORY
VIRTUAL::SnapshotPtr.CopyOrder())
#endif // #ifdef VIRTUAL_SNAPSHOT_WITHOUT_HISTORY #else
|| VIRTUAL::SnapshotPtr.OrderSelect(Index, Select))
:
#endif // #ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderSelect(Index, Select)
#else // __MT4ORDERS__
false
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderSelect(Index, Select)
#endif // __MQL5__
);
}
 
#ifdef __MQL5__
// Такая "перегрузка" позволяет использоваться совместно и MT5-вариант OrderSelect
static bool VirtualOrderSelect( const ulong Ticket )
{
return(::OrderSelect(Ticket));
}
#endif // __MQL5__
 
static bool VirtualOrderClose( const TICKET_TYPE Ticket, const double dLots, const double Price, const int SlipPage, const color Arrow_Color = clrNONE )
{
return(VIRTUAL::IsVirtual() ? VIRTUAL::SelectOrders.OrderClose(Ticket, dLots, Price) :
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderClose(Ticket, dLots, Price, SlipPage, Arrow_Color)
#else // __MT4ORDERS__
false
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderClose(Ticket, dLots, Price, SlipPage, Arrow_Color)
#endif // __MQL5__
);
}
 
static bool VirtualOrderModify( const TICKET_TYPE Ticket, const double Price, const double SL, const double TP, const datetime Expiration, const color Arrow_Color = clrNONE )
{
_VC
return(VIRTUAL::IsVirtual() ? VIRTUAL::SelectOrders.OrderModify(Ticket, Price, SL, TP, Expiration) :
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderModify(Ticket, Price, SL, TP, Expiration, Arrow_Color)
#else // __MT4ORDERS__
false
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderModify(Ticket, Price, SL, TP, Expiration, Arrow_Color)
#endif // __MQL5__
);
}
 
static bool VirtualOrderCloseBy( const TICKET_TYPE Ticket, const TICKET_TYPE Opposite, const color Arrow_Color = clrNONE )
{
return(VIRTUAL::IsVirtual() ? VIRTUAL::SelectOrders.OrderCloseBy(Ticket, Opposite) :
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderCloseBy(Ticket, Opposite, Arrow_Color)
#else // __MT4ORDERS__
false
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderCloseBy(Ticket, Opposite, Arrow_Color)
#endif // __MQL5__
);
}
 
static bool VirtualOrderDelete( const TICKET_TYPE Ticket, const color Arrow_Color = clrNONE )
{
_VC
return(VIRTUAL::IsVirtual() ? VIRTUAL::SelectOrders.OrderDelete(Ticket) :
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderDelete(Ticket, Arrow_Color)
#else // __MT4ORDERS__
false
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderDelete(Ticket, Arrow_Color)
#endif // __MQL5__
);
}
 
static TICKET_TYPE VirtualOrderSend( const string Symb, const int Type, const double dVolume, const double Price, const int SlipPage, const double SL, const double TP,
const string comment = NULL, const MAGIC_TYPE magic = 0, const datetime dExpiration = 0, color arrow_color = clrNONE )
{
_VC
return(VIRTUAL::IsVirtual() ? VIRTUAL::SelectOrders.OrderSend(Symb, Type, dVolume, Price, SlipPage, SL, TP, comment, magic, dExpiration) :
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderSend(Symb, Type, dVolume, Price, SlipPage, SL, TP, comment, magic, dExpiration, arrow_color)
#else // __MT4ORDERS__
-1
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderSend(Symb, Type, dVolume, Price, SlipPage, SL, TP, comment, magic, dExpiration, arrow_color)
#endif // __MQL5__
);
}
 
#ifdef __MQL5__
static int VirtualOrdersTotal( const bool )
{
#ifdef __MT4ORDERS__
return(::OrdersTotal(true)); // См. static int MT4OrdersTotal( const bool ) в MT4Orders.mqh
#else // __MT4ORDERS__
return(::OrdersTotal());
#endif // __MT4ORDERS__
}
#endif // __MQL5__
 
static int VirtualOrdersTotal( void )
{
_VC
return(VIRTUAL::IsVirtual() ? VIRTUAL::SelectOrders.OrdersTotal2() :
#ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
(VIRTUAL::SnapshotPtr != NULL) ? VIRTUAL::SnapshotPtr.OrdersTotal2() :
#endif // #ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrdersTotal()
#else // __MT4ORDERS__
false
#endif // __MT4ORDERS__
#else // __MQL5__
::OrdersTotal()
#endif // __MQL5__
);
}
 
static void VirtualOrderPrint( void )
{
if (VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle))
VIRTUAL::SelectOrders.OrderPrint();
else
#ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
if (VIRTUAL::SnapshotPtr != NULL)
VIRTUAL::SnapshotPtr.OrderPrint();
else
#endif // #ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderPrint();
#else // __MT4ORDERS__
::Print("");
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderPrint();
#endif // __MQL5__
 
return;
}
 
#ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
#define ORDERFUNCTION(NAME,T) \
static T VirtualOrder##NAME( void ) \
{ \
_VC return((VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)) \
? VIRTUAL::SelectOrders.Order##NAME() : \
(VIRTUAL::SnapshotPtr != NULL) ? VIRTUAL::SnapshotPtr.Order##NAME() :
 
#else // #ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
#define ORDERFUNCTION(NAME,T) \
static T VirtualOrder##NAME( void ) \
{ \
_VC return(VIRTUAL::IsVirtual() ? VIRTUAL::SelectOrders.Order##NAME() :
#endif // #ifdef VIRTUAL_SNAPSHOT_REFRESHTIME #else
 
ORDERFUNCTION(sHistoryTotal, int)
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrdersHistoryTotal()
#else // __MT4ORDERS__
0
#endif // __MT4ORDERS__
#else // __MQL5__
::OrdersHistoryTotal()
#endif // __MQL5__
);
}
 
ORDERFUNCTION(Ticket, TICKET_TYPE)
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderTicket()
#else // __MT4ORDERS__
0
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderTicket()
#endif // __MQL5__
);
}
 
ORDERFUNCTION(Type, int)
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderType()
#else // __MT4ORDERS__
0
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderType()
#endif // __MQL5__
);
}
 
ORDERFUNCTION(Lots, double)
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderLots()
#else // __MT4ORDERS__
0
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderLots()
#endif // __MQL5__
);
}
 
ORDERFUNCTION(OpenPrice, double)
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderOpenPrice()
#else // __MT4ORDERS__
0
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderOpenPrice()
#endif // __MQL5__
);
}
 
ORDERFUNCTION(OpenTime, datetime)
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderOpenTime()
#else // __MT4ORDERS__
0
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderOpenTime()
#endif // __MQL5__
);
}
 
ORDERFUNCTION(StopLoss, double)
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderStopLoss()
#else // __MT4ORDERS__
0
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderStopLoss()
#endif // __MQL5__
);
}
 
ORDERFUNCTION(TakeProfit, double)
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderTakeProfit()
#else // __MT4ORDERS__
0
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderTakeProfit()
#endif // __MQL5__
);
}
 
ORDERFUNCTION(ClosePrice, double)
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderClosePrice()
#else // __MT4ORDERS__
0
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderClosePrice()
#endif // __MQL5__
);
}
 
ORDERFUNCTION(CloseTime, datetime)
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderCloseTime()
#else // __MT4ORDERS__
0
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderCloseTime()
#endif // __MQL5__
);
}
 
ORDERFUNCTION(Expiration, datetime)
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderExpiration()
#else // __MT4ORDERS__
0
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderExpiration()
#endif // __MQL5__
);
}
 
ORDERFUNCTION(MagicNumber, MAGIC_TYPE)
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderMagicNumber()
#else // __MT4ORDERS__
0
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderMagicNumber()
#endif // __MQL5__
);
}
 
ORDERFUNCTION(Profit, double)
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderProfit()
#else // __MT4ORDERS__
0
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderProfit()
#endif // __MQL5__
);
}
 
ORDERFUNCTION(Commission, double)
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderCommission()
#else // __MT4ORDERS__
0
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderCommission()
#endif // __MQL5__
);
}
 
ORDERFUNCTION(Swap, double)
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderSwap()
#else // __MT4ORDERS__
0
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderSwap()
#endif // __MQL5__
);
}
 
ORDERFUNCTION(SymbolID, int)
0
);
}
 
ORDERFUNCTION(Symbol, string)
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderSymbol()
#else // __MT4ORDERS__
NULL
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderSymbol()
#endif // __MQL5__
);
}
 
ORDERFUNCTION(Comment, string)
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderComment()
#else // __MT4ORDERS__
NULL
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderComment()
#endif // __MQL5__
);
}
 
ORDERFUNCTION(OpenTimeMsc, long)
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderOpenTimeMsc()
#else // __MT4ORDERS__
0
#endif // __MT4ORDERS__
#else // __MQL5__
(long)::OrderOpenTime() * 1000
#endif // __MQL5__
);
}
 
ORDERFUNCTION(CloseTimeMsc, long)
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderCloseTimeMsc()
#else // __MT4ORDERS__
0
#endif // __MT4ORDERS__
#else // __MQL5__
(long)::OrderCloseTime() * 1000
#endif // __MQL5__
);
}
 
ORDERFUNCTION(OpenPriceRequest, double)
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderOpenPriceRequest()
#else // __MT4ORDERS__
0
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderOpenPrice()
#endif // __MQL5__
);
}
 
ORDERFUNCTION(ClosePriceRequest, double)
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderClosePriceRequest()
#else // __MT4ORDERS__
0
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderClosePrice()
#endif // __MQL5__
);
}
 
ORDERFUNCTION(OpenReason, ENUM_DEAL_REASON)
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderOpenReason()
#else // __MT4ORDERS__
DEAL_REASON_CLIENT
#endif // __MT4ORDERS__
#else // __MQL5__
DEAL_REASON_CLIENT
#endif // __MQL5__
);
}
 
ORDERFUNCTION(CloseReason, ENUM_DEAL_REASON)
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderCloseReason()
#else // __MT4ORDERS__
DEAL_REASON_CLIENT
#endif // __MT4ORDERS__
#else // __MQL5__
DEAL_REASON_CLIENT
#endif // __MQL5__
);
}
 
ORDERFUNCTION(TicketID, TICKET_TYPE)
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderTicketID()
#else // __MT4ORDERS__
0
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderTicket()
#endif // __MQL5__
);
}
 
ORDERFUNCTION(DealsAmount, int)
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderDealsAmount()
#else // __MT4ORDERS__
0
#endif // __MT4ORDERS__
#else // __MQL5__
0
#endif // __MQL5__
);
}
 
ORDERFUNCTION(LotsOpen, double)
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderLotsOpen()
#else // __MT4ORDERS__
0
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderLots()
#endif // __MQL5__
);
}
 
ORDERFUNCTION(TicketOpen, TICKET_TYPE)
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderTicketOpen()
#else // __MT4ORDERS__
0
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderTicket()
#endif // __MQL5__
);
}
#undef ORDERFUNCTION
 
static bool VirtualOrderComment( const string NewComment )
{
_VC
return((VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)) && VIRTUAL::SelectOrders.OrderComment(NewComment));
}
 
// DBL_MIN - тип Неттинга и размер баланса берутся с текущего счета.
static int Tester( const MqlTick &Ticks[], const STRATEGY Strategy, const double Balance = DBL_MIN, const bool Stop = true, const bool bNetting = false )
{
const int Handle = VIRTUAL::GetHandle();
const bool Res = ::ArraySize(Ticks) && VIRTUAL::SelectByHandle(VIRTUAL::Create(Balance, Ticks[0].time - Ticks[0].time % (24 * 3600), bNetting));
 
if (Res)
{
VIRTUAL::NewTick(Ticks, Strategy);
 
if (Stop)
VIRTUAL::Stop();
}
 
return(Res ? Handle : -1);
}
 
// ----TesterSingle-section: begin.
static int TesterSingle( const SYMBOL_BASE &sSymbol, const MqlTick &Ticks[], const STRATEGY Strategy,
const double Balance = DBL_MIN, const bool Stop = true, const bool bNetting = false )
{
const int Handle = VIRTUAL::GetHandle();
const bool Res = ::ArraySize(Ticks) &&
VIRTUAL::SelectByHandle(VIRTUAL::CreateSingle(sSymbol, Balance,
Ticks[0].time - Ticks[0].time % (24 * 3600), bNetting));
 
if (Res)
{
VIRTUAL::NewTick(Ticks, Strategy);
 
if (Stop)
VIRTUAL::Stop();
}
 
return(Res ? Handle : -1);
}
 
static int TesterSingle( const SYMBOL_BASE &sSymbol, const TICKS_ARRAY &Ticks, const STRATEGY Strategy,
const double Balance = DBL_MIN, const bool Stop = true, const bool bNetting = false )
{
return(VIRTUAL::TesterSingle(sSymbol, Ticks.Ticks, Strategy, Balance, Stop, bNetting));
}
 
static int TesterSingle( const string sSymbol, const MqlTick &Ticks[], const STRATEGY Strategy,
const double Balance = DBL_MIN, const bool Stop = true, const bool bNetting = false )
{
// const SYMBOL_BASE sSymbol2 = sSymbol; // https://www.mql5.com/ru/forum/492248/page3#comment_57718727
SYMBOL_BASE sSymbol2; sSymbol2 = sSymbol;
 
return(VIRTUAL::TesterSingle(sSymbol2, Ticks, Strategy, Balance, Stop, bNetting));
}
 
static int TesterSingle( const string sSymbol, const TICKS_ARRAY &Ticks, const STRATEGY Strategy,
const double Balance = DBL_MIN, const bool Stop = true, const bool bNetting = false )
{
return(VIRTUAL::TesterSingle(sSymbol, Ticks.Ticks, Strategy, Balance, Stop, bNetting));
}
// ----TesterSingle-section: end.
 
// ----TesterMulti-section1: begin.
static int TesterMulti( const SYMBOL_BASE &sSymbol, const MqlTick &Ticks[], const STRATEGY_MULTI StrategyMulti,
const double Balance = DBL_MIN, const bool Stop = true, const bool bNetting = false )
{
const int Handle = VIRTUAL::GetHandle();
const bool Res = ::ArraySize(Ticks) &&
VIRTUAL::SelectByHandle(VIRTUAL::CreateSingle(sSymbol, Balance,
Ticks[0].time - Ticks[0].time % (24 * 3600), bNetting));
 
if (Res)
{
VIRTUAL::NewTickMulti(Ticks, StrategyMulti);
 
if (Stop)
VIRTUAL::Stop();
}
 
return(Res ? Handle : -1);
}
 
static int TesterMulti( const SYMBOL_BASE &sSymbol, const TICKS_ARRAY &Ticks, const STRATEGY_MULTI StrategyMulti,
const double Balance = DBL_MIN, const bool Stop = true, const bool bNetting = false )
{
return(VIRTUAL::TesterMulti(sSymbol, Ticks.Ticks, StrategyMulti, Balance, Stop, bNetting));
}
 
static int TesterMulti( const string sSymbol, const MqlTick &Ticks[], const STRATEGY_MULTI StrategyMulti,
const double Balance = DBL_MIN, const bool Stop = true, const bool bNetting = false )
{
// const SYMBOL_BASE sSymbol2 = sSymbol; // https://www.mql5.com/ru/forum/492248/page3#comment_57718727
SYMBOL_BASE sSymbol2; sSymbol2 = sSymbol;
 
return(VIRTUAL::TesterMulti(sSymbol2, Ticks, StrategyMulti, Balance, Stop, bNetting));
}
 
static int TesterMulti( const string sSymbol, const TICKS_ARRAY &Ticks, const STRATEGY_MULTI StrategyMulti,
const double Balance = DBL_MIN, const bool Stop = true, const bool bNetting = false )
{
return(VIRTUAL::TesterMulti(sSymbol, Ticks.Ticks, StrategyMulti, Balance, Stop, bNetting));
}
// ----TesterMulti-section1: end.
 
// ----TesterMulti-section2: begin.
static int TesterMulti( const SYMBOL_BASE &sSymbols[], const TICKS_ARRAY &TicksArray[], const STRATEGY_MULTI StrategyMulti,
const double Balance = DBL_MIN, const bool Stop = true, const bool bNetting = false )
{
datetime MinTime = INT_MAX;
 
for (uint i = ::MathMin(::ArraySize(sSymbols), ::ArraySize(TicksArray)); (bool)i--;)
MinTime = ::MathMin(MinTime, ::ArraySize(TicksArray[i].Ticks) ? TicksArray[i].Ticks[0].time : INT_MAX);
 
const int Handle = VIRTUAL::GetHandle();
const bool Res = ::ArraySize(TicksArray) && (MinTime != INT_MAX) &&
VIRTUAL::SelectByHandle(VIRTUAL::CreateMulti(sSymbols, Balance, MinTime - MinTime % (24 * 3600), bNetting));
 
if (Res)
{
VIRTUAL::NewTickMulti(TicksArray, StrategyMulti);
 
if (Stop)
VIRTUAL::Stop();
}
 
return(Res ? Handle : -1);
}
 
static int TesterMulti( const string &sSymbols[], const TICKS_ARRAY &TicksArray[], const STRATEGY_MULTI StrategyMulti,
const double Balance = DBL_MIN, const bool Stop = true, const bool bNetting = false )
{
SYMBOL_BASE sSymbols2[];
 
for (uint i = ::ArrayResize(sSymbols2, ::ArraySize(sSymbols)); (bool)i--;)
sSymbols2[i] = sSymbols[i];
 
return(VIRTUAL::TesterMulti(sSymbols2, TicksArray, StrategyMulti, Balance, Stop, bNetting));
}
// ----TesterMulti-section2: end.
 
// ----TesterMultiApart-section: begin.
static int TesterMultiApart( const SYMBOL_BASE &sSymbols[], const TICKS_ARRAY &TicksArray[], const STRATEGY_MULTI StrategyMulti,
const double Balance = DBL_MIN, const bool Stop = true, const bool bNetting = false )
{
const int Handle = VIRTUAL::GetHandle();
 
ORDERS* PrevOrders = NULL;
 
for (uint i = ::MathMin(::ArraySize(sSymbols), ::ArraySize(TicksArray)); (bool)i--;)
if (VIRTUAL::TesterMulti(sSymbols[i], TicksArray[i], StrategyMulti, Balance, Stop, bNetting) != -1)
{
if (PrevOrders != NULL)
{
PrevOrders += VIRTUAL_SELECTORDERS_MACROS(VIRTUAL::SelectOrders, &VIRTUAL::SelectOrders);
 
VIRTUAL::Delete();
}
else
PrevOrders = VIRTUAL_SELECTORDERS_MACROS(VIRTUAL::SelectOrders, VIRTUAL::SelectOrders.MyPointer);
}
 
if (PrevOrders != NULL)
VIRTUAL::SelectOrders = PrevOrders;
 
return((PrevOrders != NULL) ? Handle : -1);
}
 
static int TesterMultiApart( const string &sSymbols[], const TICKS_ARRAY &TicksArray[], const STRATEGY_MULTI StrategyMulti,
const double Balance = DBL_MIN, const bool Stop = true, const bool bNetting = false )
{
SYMBOL_BASE sSymbols2[];
 
for (uint i = ::ArrayResize(sSymbols2, ::ArraySize(sSymbols)); (bool)i--;)
sSymbols2[i] = sSymbols[i];
 
return(VIRTUAL::TesterMultiApart(sSymbols2, TicksArray, StrategyMulti, Balance, Stop, bNetting));
}
// ----TesterMultiApart-section: end.
 
// Сброс номеров тикетов Истории торгов.
static bool ResetTickets( void )
{
return((VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)) && VIRTUAL::SelectOrders.ResetTickets());
}
 
static int GetMemoryUsed( void )
{
return((VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)) ? VIRTUAL::SelectOrders.GetMemoryUsed() : 0);
}
 
static int GetMemoryUsedFull( void )
{
int Memory = sizeof(VIRTUAL::SelectOrders) +
::ArraySize(VIRTUAL::Orders) * sizeof(ORDERS*) +
sizeof(VIRTUAL::CountHandles) +
sizeof(VIRTUAL::VirtualDelete);
 
for (int i = VIRTUAL::Total() - 1; i >= 0; i--)
Memory += VIRTUAL::Orders[i].GetMemoryUsed() + VIRTUAL_SELECTORDERS_MACROS(0, VIRTUAL::SelectOrders.GetMemoryUsed());
 
#ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
Memory += sizeof(VIRTUAL::SnapshotPtr) + ((VIRTUAL::SnapshotPtr != NULL) ? VIRTUAL::SnapshotPtr.GetMemoryUsed() : 0);
#endif // #ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
 
return(Memory);
}
 
static bool ToStack( void )
{
VIRTUAL::StackOrders[VIRTUAL::StackPos++] = VIRTUAL_SELECTORDERS_MACROS(VIRTUAL::SelectOrders, VIRTUAL::SelectOrders.MyPointer);
 
return(true);
}
 
static bool SelectFromStack( void )
{
const bool Res = VIRTUAL::StackPos;
 
if (Res)
VIRTUAL::SelectOrders = VIRTUAL::StackOrders[--VIRTUAL::StackPos];
 
return(Res);
}
 
static const ORDER_BASE GetOrder( void )
{
ORDER_BASE Order;
 
if (VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle))
Order = VIRTUAL::SelectOrders.GetOrder();
else
{
#ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
if (VIRTUAL::SnapshotPtr != NULL)
Order = VIRTUAL::SnapshotPtr.GetOrder();
else
#endif // #ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
Order.Copy();
}
 
return(Order);
}
 
static bool AddOrder( const ORDER_BASE &Order )
{
return((VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)) && VIRTUAL::SelectOrders.AddOrder(Order));
}
 
static bool AddOrder( const ORDER_BASE &NewOrders[] )
{
const int Size = ::ArraySize(NewOrders);
bool Res = (VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)) && Size;
 
if (Res)
for (int i = 0; i < Size; i++)
Res &= VIRTUAL::SelectOrders.AddOrder(NewOrders[i]);
 
return(Res);
}
 
static bool CalcSwaps( const datetime RolloverTime = 0 )
{
return(VIRTUAL::CalcSwaps(_Symbol, RolloverTime));
}
 
static bool CalcSwaps( const string Symb, const datetime RolloverTime = 0 )
{
return(VIRTUAL::CalcSwaps(::SymbolInfoDouble(Symb, SYMBOL_SWAP_SHORT), ::SymbolInfoDouble(Symb, SYMBOL_SWAP_LONG),
RolloverTime,(int)::SymbolInfoInteger(Symb, SYMBOL_SWAP_ROLLOVER3DAYS)));
}
 
static bool CalcSwaps( const double SwapShort, const double SwapLong,
datetime RolloverTime = 0, const int Rollover3Days = WEDNESDAY )
{
#define DAY (24 * 3600)
RolloverTime = RolloverTime % DAY;
#undef DAY
 
return((VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)) && VIRTUAL::SelectOrders.CalcSwaps(SwapShort, SwapLong, RolloverTime, Rollover3Days));
}
 
static uint Save( const string FileName, const bool FileCommon = false )
{
return((VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)) ? VIRTUAL::SelectOrders.Save(FileName, FileCommon) :
#ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
((VIRTUAL::SnapshotPtr != NULL) ? VIRTUAL::SnapshotPtr.Save(FileName, FileCommon) : 0)
#else // #ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
0
#endif // // #ifdef VIRTUAL_SNAPSHOT_REFRESHTIME #else
);
}
 
static bool Load( const string FileName, const bool FileCommon = false )
{
return((VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)) && VIRTUAL::SelectOrders.Load(FileName, FileCommon));
}
 
static uint Save( const int FileHandle )
{
return((VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)) ? VIRTUAL::SelectOrders.Save(FileHandle) :
#ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
((VIRTUAL::SnapshotPtr != NULL) ? VIRTUAL::SnapshotPtr.Save(FileHandle) : 0)
#else // #ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
0
#endif // // #ifdef VIRTUAL_SNAPSHOT_REFRESHTIME #else
);
}
 
static bool Load( const int FileHandle )
{
return((VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)) && VIRTUAL::SelectOrders.Load(FileHandle));
}
 
#ifdef __TYPETOBYTES__
template <typename T>
static uint Save( T &Array[], const int LastHistoryOrders = INT_MAX )
{
return((VIRTUAL::SelectOrders != NULL) ? VIRTUAL::SelectOrders.Save(Array, LastHistoryOrders) :
#ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
((VIRTUAL::SnapshotPtr != NULL) ? VIRTUAL::SnapshotPtr.Save(Array, LastHistoryOrders) : 0)
#else // #ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
0
#endif // // #ifdef VIRTUAL_SNAPSHOT_REFRESHTIME #else
);
}
 
template <typename T>
static bool Load( const T &Array[] )
{
return((VIRTUAL::SelectOrders != NULL) && VIRTUAL::SelectOrders.Load(Array));
}
#endif // #ifdef __TYPETOBYTES__
 
// NULL-строка превратится в ""-строку. См. String.mqh.
static bool SetID( const string NewID )
{
return((VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)) ? VIRTUAL::SelectOrders.SetID(NewID) :
#ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
((VIRTUAL::SnapshotPtr != NULL) && VIRTUAL::SnapshotPtr.SetID(NewID))
#else // #ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
false
#endif // // #ifdef VIRTUAL_SNAPSHOT_REFRESHTIME #else
);
}
 
// NULL-строка может возвращаться только для реального окружения. См. VIRTUAL::SetID.
static string GetID( void )
{
return((VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)) ? VIRTUAL::SelectOrders.GetID() :
#ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
((VIRTUAL::SnapshotPtr != NULL) ? VIRTUAL::SnapshotPtr.GetID() : NULL)
#else // #ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
NULL
#endif // // #ifdef VIRTUAL_SNAPSHOT_REFRESHTIME #else
);
}
 
static bool ReduceHistory( const int LastHistoryOrders, const bool ChangeHistory = false )
{
return((VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)) && VIRTUAL::SelectOrders.ReduceHistory(LastHistoryOrders, ChangeHistory));
}
 
static string GetMode( void )
{
#define MACROS_TOSTRING(A) #A
#define MACROS_TOSTRING2(A) MACROS_TOSTRING(A)
 
static const string Str = NULL
 
#ifdef __MT4ORDERS__
+ "__MT4ORDERS__ " + MACROS_TOSTRING2(__MT4ORDERS__) + "\n"
#endif // #ifdef __MT4ORDERS__
 
#ifdef MAX_ORDERS
+ "MAX_ORDERS " + MACROS_TOSTRING2(MAX_ORDERS) + "\n"
#endif // #ifdef MAX_ORDERS
 
#ifdef VIRTUAL_TESTER
+ "VIRTUAL_TESTER\n"
#endif // #ifdef VIRTUAL_TESTER
 
#ifdef VIRTUAL_TESTER_MULTI
+ "VIRTUAL_TESTER_MULTI\n"
#endif // #ifdef VIRTUAL_TESTER_MULTI
 
#ifdef VIRTUAL_LIMITS_TP_SLIPPAGE
+ "VIRTUAL_LIMITS_TP_SLIPPAGE\n"
#endif // #ifdef VIRTUAL_LIMITS_TP_SLIPPAGE
 
#ifdef VIRTUAL_CLOSEALL_BYEND
+ "VIRTUAL_CLOSEALL_BYEND\n"
#endif // #ifdef VIRTUAL_CLOSEALL_BYEND
 
#ifdef VIRTUAL_ONTESTER_FORMULA
+ "VIRTUAL_ONTESTER_FORMULA " + MACROS_TOSTRING2(VIRTUAL_ONTESTER_FORMULA) + "\n"
#endif // #ifdef VIRTUAL_ONTESTER_FORMULA
 
#ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
+ "VIRTUAL_SNAPSHOT_REFRESHTIME " + MACROS_TOSTRING2(VIRTUAL_SNAPSHOT_REFRESHTIME) + "\n"
#endif // #ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
 
#ifdef VIRTUAL_SNAPSHOT_WITHOUT_HISTORY
+ "VIRTUAL_SNAPSHOT_WITHOUT_HISTORY\n"
#endif // #ifdef VIRTUAL_SNAPSHOT_WITHOUT_HISTORY
 
#ifdef VIRTUAL_TESTER_FAST
+ "VIRTUAL_TESTER_FAST\n"
#endif // #ifdef VIRTUAL_TESTER_FAST
 
#ifdef VIRTUAL_COUNTER
+ "VIRTUAL_COUNTER\n"
#endif // #ifdef VIRTUAL_COUNTER
 
#ifdef VIRTUAL_NOCHECK_NULL
+ "VIRTUAL_NOCHECK_NULL\n"
#endif // #ifdef VIRTUAL_NOCHECK_NULL
 
#ifdef VIRTUAL_ALTERNATIVE
+ "VIRTUAL_ALTERNATIVE\n"
#endif // #ifdef VIRTUAL_ALTERNATIVE
 
#ifdef VIRTUAL_SELECTORDERS_OBJECT
+ "VIRTUAL_SELECTORDERS_OBJECT\n"
#endif // #ifdef VIRTUAL_SELECTORDERS_OBJECT
 
#ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY
+ "VIRTUAL_ORDERSELECT_WITHOUT_COPY\n"
#endif // #ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY
 
#ifdef TICKS_CORRECT_TIME
+ "TICKS_CORRECT_TIME\n"
#endif // #ifdef TICKS_CORRECT_TIME
 
#ifdef TICKS_FORCE_NORMALIZE
+ "TICKS_FORCE_NORMALIZE\n"
#endif // #ifdef TICKS_FORCE_NORMALIZE
;
#undef MACROS_TOSTRING2
#undef MACROS_TOSTRING
 
return(Str);
}
 
static uint VirtualOrderCloseAsync( const TICKET_TYPE Ticket, const double dLots, const double Price, const int SlipPage, const color Arrow_Color = clrNONE )
{
return((VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)) ? VIRTUAL::SelectOrders.OrderClose(Ticket, dLots, Price) :
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderCloseAsync(Ticket, dLots, Price, SlipPage, Arrow_Color)
#else // __MT4ORDERS__
false
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderClose(Ticket, dLots, Price, SlipPage, Arrow_Color)
#endif // __MQL5__
);
}
 
static uint VirtualOrderModifyAsync( const TICKET_TYPE Ticket, const double Price, const double SL, const double TP, const datetime Expiration, const color Arrow_Color = clrNONE )
{
return((VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)) ? VIRTUAL::SelectOrders.OrderModify(Ticket, Price, SL, TP, Expiration) :
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderModifyAsync(Ticket, Price, SL, TP, Expiration, Arrow_Color)
#else // __MT4ORDERS__
false
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderModify(Ticket, Price, SL, TP, Expiration, Arrow_Color)
#endif // __MQL5__
);
}
 
static uint VirtualOrderDeleteAsync( const TICKET_TYPE Ticket, const color Arrow_Color = clrNONE )
{
return((VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)) ? VIRTUAL::SelectOrders.OrderDelete(Ticket) :
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderDeleteAsync(Ticket, Arrow_Color)
#else // __MT4ORDERS__
false
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderDelete(Ticket, Arrow_Color)
#endif // __MQL5__
);
}
 
static TICKET_TYPE VirtualOrderSendAsync( const string Symb, const int Type, const double dVolume, const double Price, const int SlipPage, const double SL, const double TP,
const string comment = NULL, const MAGIC_TYPE magic = 0, const datetime dExpiration = 0, color arrow_color = clrNONE )
{
return((VIRTUAL::SelectOrders VIRTUAL_SELECTORDERS_MACROS(!= NULL, .Handle)) ? VIRTUAL::SelectOrders.OrderSend(Symb, Type, dVolume, Price, SlipPage, SL, TP, comment, magic, dExpiration) :
#ifdef __MQL5__
#ifdef __MT4ORDERS__
::OrderSendAsync(Symb, Type, dVolume, Price, SlipPage, SL, TP, comment, magic, dExpiration, arrow_color)
#else // __MT4ORDERS__
-1
#endif // __MT4ORDERS__
#else // __MQL5__
::OrderSend(Symb, Type, dVolume, Price, SlipPage, SL, TP, comment, magic, dExpiration, arrow_color)
#endif // __MQL5__
);
}
 
// _VI2, _VSI2
struct HANDLE_INDEX
{
int Handle;
int Index;
 
HANDLE_INDEX( const int NewHandle = -1) : Handle(NewHandle), Index(-1)
{
}
 
void operator =( const int NewHandle )
{
this.Handle = NewHandle;
 
return;
}
 
bool operator !=( const int AnotherHandle ) const
{
return(this.Handle != AnotherHandle);
}
};
 
// https://www.mql5.com/ru/forum/170952/page228#comment_44576693
struct POINTER
{
private:
ORDERS* Pointer;
int Handle;
 
public:
POINTER( void ) : Pointer(NULL), Handle(-1)
{
}
 
POINTER( const int iHandle )
{
this = iHandle;
}
 
void operator =( const int iHandle )
{
if (iHandle == VIRTUAL::GetHandle())
{
this.Pointer = VIRTUAL_SELECTORDERS_MACROS(VIRTUAL::SelectOrders, VIRTUAL::SelectOrders.MyPointer);
this.Handle = iHandle;
}
else
{
const int Index = VIRTUAL::GetNumber(iHandle);
 
if (Index >= 0)
{
this.Pointer = Index ? VIRTUAL::Orders[Index - 1] : NULL;
this.Handle = iHandle;
}
else
{
this.Pointer = NULL;
this.Handle = -1;
}
}
 
return;
}
 
bool operator +=( const VIRTUAL_POINTER &pPointer ) const
{
const bool Res = (this.Pointer != NULL) && (pPointer.Pointer != NULL) && !this.IsNull() && !pPointer.IsNull();
 
if (Res)
VIRTUAL_SELECTORDERS_MACROS(this.Pointer, VIRTUAL::GetMyPointer(this.Pointer)) +=
VIRTUAL_SELECTORDERS_MACROS(pPointer.Pointer, VIRTUAL::GetMyPointer(pPointer.Pointer));
 
return(Res);
}
 
bool Init( void )
{
this.Pointer = VIRTUAL_SELECTORDERS_MACROS(VIRTUAL::SelectOrders, VIRTUAL::SelectOrders.MyPointer);
this.Handle = VIRTUAL::GetHandle();
 
return(true);
}
 
void Delete( void )
{
this.Handle = -1;
 
return;
}
 
bool Select( void ) const
{
const bool Res = !this.IsNull();
 
if (Res)
VIRTUAL::SelectOrders = this.Pointer;
 
return(Res);
}
 
int GetHandle( void ) const
{
return(this.Handle);
}
 
bool IsNull() const
{
return(this.Handle == -1);
}
 
bool operator != ( const VIRTUAL_POINTER &pPointer ) const
{
return(this.Handle != pPointer.Handle);
}
 
bool operator != ( const int iHandle ) const
{
return(this.Handle != iHandle);
}
};
};
 
static ORDERS* VIRTUAL::Orders[];
 
static ORDERS VIRTUAL_SELECTORDERS_MACROS(*VIRTUAL::SelectOrders = NULL, VIRTUAL::SelectOrders(0));
 
static int VIRTUAL::CountHandles = 1;
 
static ORDERS* VIRTUAL::StackOrders[100];
static int VIRTUAL::StackPos = 0;
 
#ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
static SNAPSHOT* VIRTUAL::SnapshotPtr = NULL;
static SNAPSHOT* VIRTUAL::SnapshotPtrBackup = NULL;
#endif // #ifdef VIRTUAL_SNAPSHOT_REFRESHTIME
 
static const VIRTUAL_DELETE VIRTUAL::VirtualDelete;
 
#undef VIRTUAL_SELECTORDERS_MACROS
 
#define AccountBalance VIRTUAL::VirtualAccountBalance
#define AccountEquity VIRTUAL::VirtualAccountEquity
#define AccountProfit VIRTUAL::VirtualAccountProfit
 
#ifdef OrdersTotal
#undef OrdersTotal
#endif // OrdersTotal
 
#ifdef __MQL5__
#ifndef __MT4ORDERS__
#define VIRTUAL_FUNCTIONS
#endif // __MT4ORDERS__
#endif // __MQL5__
 
#ifdef VIRTUAL_FUNCTIONS
bool OrderSelect( const TICKET_TYPE Index, const int Select, const int Pool )
{
return(VIRTUAL::VirtualOrderSelect(Index, Select, Pool));
}
 
bool OrderSelect( const TICKET_TYPE Index, const int Select)
{
return(VIRTUAL::VirtualOrderSelect(Index, Select));
}
 
bool OrderClose( const TICKET_TYPE Ticket, const double dLots, const double Price, const int SlipPage, const color Arrow_Color = clrNONE )
{
return(VIRTUAL::VirtualOrderClose(Ticket, dLots, Price, SlipPage, Arrow_Color));
}
 
bool OrderModify( const TICKET_TYPE Ticket, const double Price, const double SL, const double TP, const datetime Expiration, const color Arrow_Color = clrNONE )
{
return(VIRTUAL::VirtualOrderModify(Ticket, Price, SL, TP, Expiration, Arrow_Color));
}
 
bool OrderCloseBy( const TICKET_TYPE Ticket, const TICKET_TYPE Opposite, const color Arrow_Color = clrNONE )
{
return(VIRTUAL::VirtualOrderCloseBy(Ticket, Opposite, Arrow_Color));
}
 
bool OrderDelete( const TICKET_TYPE Ticket, const color Arrow_Color = clrNONE )
{
return(VIRTUAL::VirtualOrderDelete(Ticket, Arrow_Color));
}
 
TICKET_TYPE OrderSend( const string Symb, const int Type, const double dVolume, const double Price, const int SlipPage, const double SL, const double TP,
const string comment = NULL, const MAGIC_TYPE magic = 0, const datetime dExpiration = 0, color arrow_color = clrNONE )
{
return(VIRTUAL::VirtualOrderSend(Symb, Type, dVolume, Price, SlipPage, SL, TP, comment, magic, dExpiration, arrow_color));
}
 
void OrderPrint( void )
{
VIRTUAL::VirtualOrderPrint();
 
return;
}
 
#define OrdersTotal VIRTUAL::VirtualOrdersTotal
 
#define ORDERFUNCTION(NAME, T) \
T NAME( void ) \
{ \
return(VIRTUAL::Virtual##NAME()); \
}
 
ORDERFUNCTION(OrdersHistoryTotal, int);
ORDERFUNCTION(OrderTicket, TICKET_TYPE);
ORDERFUNCTION(OrderType, int);
ORDERFUNCTION(OrderLots, double);
ORDERFUNCTION(OrderOpenPrice, double);
ORDERFUNCTION(OrderOpenTime, datetime);
ORDERFUNCTION(OrderStopLoss, double);
ORDERFUNCTION(OrderTakeProfit, double);
ORDERFUNCTION(OrderClosePrice, double);
ORDERFUNCTION(OrderCloseTime, datetime);
ORDERFUNCTION(OrderExpiration, datetime);
ORDERFUNCTION(OrderMagicNumber, MAGIC_TYPE);
ORDERFUNCTION(OrderProfit, double);
ORDERFUNCTION(OrderCommission, double);
ORDERFUNCTION(OrderSwap, double);
ORDERFUNCTION(OrderSymbol, string);
ORDERFUNCTION(OrderComment, string);
ORDERFUNCTION(OrderOpenTimeMsc, long);
ORDERFUNCTION(OrderCloseTimeMsc, long);
ORDERFUNCTION(OrderOpenPriceRequest, double);
ORDERFUNCTION(OrderClosePriceRequest, double);
ORDERFUNCTION(OrderOpenReason, ENUM_DEAL_REASON);
ORDERFUNCTION(OrderCloseReason, ENUM_DEAL_REASON);
ORDERFUNCTION(OrderTicketID, TICKET_TYPE);
ORDERFUNCTION(OrderDealsAmount, int);
ORDERFUNCTION(OrderTicketOpen, TICKET_TYPE);
ORDERFUNCTION(OrderLotsOpen, double);
ORDERFUNCTION(OrderSymbolID, int);
 
#undef ORDERFUNCTION
#undef VIRTUAL_FUNCTIONS
 
uint OrderCloseAsync( const TICKET_TYPE Ticket, const double dLots, const double Price, const int SlipPage, const color Arrow_Color = clrNONE )
{
return(VIRTUAL::VirtualOrderClose(Ticket, dLots, Price, SlipPage, Arrow_Color));
}
 
uint OrderModifyAsync( const TICKET_TYPE Ticket, const double Price, const double SL, const double TP, const datetime Expiration, const color Arrow_Color = clrNONE )
{
return(VIRTUAL::VirtualOrderModify(Ticket, Price, SL, TP, Expiration, Arrow_Color));
}
 
uint OrderDeleteAsync( const TICKET_TYPE Ticket, const color Arrow_Color = clrNONE )
{
return(VIRTUAL::VirtualOrderDelete(Ticket, Arrow_Color));
}
 
TICKET_TYPE OrderSendAsync( const string Symb, const int Type, const double dVolume, const double Price, const int SlipPage, const double SL, const double TP,
const string comment = NULL, const MAGIC_TYPE magic = 0, const datetime dExpiration = 0, color arrow_color = clrNONE )
{
return(VIRTUAL::VirtualOrderSendAsync(Symb, Type, dVolume, Price, SlipPage, SL, TP, comment, magic, dExpiration, arrow_color));
}
 
#else // #ifdef VIRTUAL_FUNCTIONS
#define OrderSelect VIRTUAL::VirtualOrderSelect
#define OrderClose VIRTUAL::VirtualOrderClose
#define OrderModify VIRTUAL::VirtualOrderModify
#define OrderCloseBy VIRTUAL::VirtualOrderCloseBy
#define OrderDelete VIRTUAL::VirtualOrderDelete
#define OrdersTotal VIRTUAL::VirtualOrdersTotal
#define OrderSend VIRTUAL::VirtualOrderSend
#define OrderPrint VIRTUAL::VirtualOrderPrint
 
#define OrdersHistoryTotal VIRTUAL::VirtualOrdersHistoryTotal
#define OrderTicket VIRTUAL::VirtualOrderTicket
#define OrderType VIRTUAL::VirtualOrderType
#define OrderLots VIRTUAL::VirtualOrderLots
#define OrderOpenPrice VIRTUAL::VirtualOrderOpenPrice
#define OrderOpenTime VIRTUAL::VirtualOrderOpenTime
#define OrderStopLoss VIRTUAL::VirtualOrderStopLoss
#define OrderTakeProfit VIRTUAL::VirtualOrderTakeProfit
#define OrderClosePrice VIRTUAL::VirtualOrderClosePrice
#define OrderCloseTime VIRTUAL::VirtualOrderCloseTime
#define OrderExpiration VIRTUAL::VirtualOrderExpiration
#define OrderMagicNumber VIRTUAL::VirtualOrderMagicNumber
#define OrderProfit VIRTUAL::VirtualOrderProfit
#define OrderCommission VIRTUAL::VirtualOrderCommission
#define OrderSwap VIRTUAL::VirtualOrderSwap
#define OrderSymbolID VIRTUAL::VirtualOrderSymbolID
#define OrderSymbol VIRTUAL::VirtualOrderSymbol
#define OrderComment VIRTUAL::VirtualOrderComment
#define OrderOpenTimeMsc VIRTUAL::VirtualOrderOpenTimeMsc
#define OrderCloseTimeMsc VIRTUAL::VirtualOrderCloseTimeMsc
#define OrderOpenPriceRequest VIRTUAL::VirtualOrderOpenPriceRequest
#define OrderClosePriceRequest VIRTUAL::VirtualOrderClosePriceRequest
#define OrderOpenReason VIRTUAL::VirtualOrderOpenReason
#define OrderCloseReason VIRTUAL::VirtualOrderCloseReason
#define OrderTicketID VIRTUAL::VirtualOrderTicketID
#define OrderDealsAmount VIRTUAL::VirtualOrderDealsAmount
#define OrderTicketOpen VIRTUAL::VirtualOrderTicketOpen
#define OrderLotsOpen VIRTUAL::VirtualOrderLotsOpen
 
#define OrderCloseAsync VIRTUAL::VirtualOrderCloseAsync
#define OrderModifyAsync VIRTUAL::VirtualOrderModifyAsync
#define OrderDeleteAsync VIRTUAL::VirtualOrderDeleteAsync
#define OrderSendAsync VIRTUAL::VirtualOrderSendAsync
#endif // #ifdef VIRTUAL_FUNCTIONS #else
 
#define TesterDeposit VIRTUAL::VirtualTesterDeposit
#define TesterWithdrawal VIRTUAL::VirtualTesterWithdrawal
#define TesterStatistics VIRTUAL::VirtualTesterStatistics
 
#define TimeCurrent VIRTUAL::VirtualTimeCurrent
#define TimeTradeServer VIRTUAL::VirtualTimeTradeServer
 
#define SymbolInfoTick VIRTUAL::VirtualSymbolInfoTick
#define SymbolInfoInteger VIRTUAL::VirtualSymbolInfoInteger
#define SymbolInfoDouble VIRTUAL::VirtualSymbolInfoDouble
 
#define AccountInfoInteger VIRTUAL::VirtualAccountInfoInteger
#define AccountInfoDouble VIRTUAL::VirtualAccountInfoDouble
 
#ifdef VIRTUAL_TESTER
 
#include "Sync.mqh"
 
sinput bool VirtualTester = false;
sinput bool ReverseDeals = false;
 
const int VirtualHandle = VirtualTester ? VIRTUAL::Create() : 0;
const int ReverseHandle = ReverseDeals ? VIRTUAL::Create() : 0;
 
const bool VirtualInit = VIRTUAL::SelectByHandle(ReverseDeals ? ReverseHandle : VirtualHandle);
 
void OnTick( void )
{
VIRTUAL::NewTick();
 
if (ReverseHandle)
{
VIRTUAL::SelectByHandle(VirtualHandle);
VIRTUAL::NewTick();
 
// Comment(VIRTUAL::ToString());
 
VIRTUAL::SelectByHandle(ReverseHandle);
SYNC::Positions<ISTIME>(VirtualHandle, true);
}
 
::OldOnTick2();
 
if (ReverseHandle)
SYNC::Positions<ISTIME>(VirtualHandle, true);
 
// Comment(VIRTUAL::ToString());
return;
}
#ifndef BESTINTERVAL_ONTESTER
 
double OnTester()
{
VIRTUAL::SelectByHandle(VirtualHandle);
 
#ifdef VIRTUAL_CLOSEALL_BYEND
VIRTUAL::Stop();
#endif // VIRTUAL_CLOSEALL_BYEND
 
#ifdef VIRTUAL_ONTESTER_FORMULA
return(VIRTUAL_ONTESTER_FORMULA);
#else // VIRTUAL_ONTESTER_FORMULA
return(::AccountEquity());
#endif // VIRTUAL_ONTESTER_FORMULA
}
 
#define OnTester OldOnTester2
 
#endif // BESTINTERVAL_ONTESTER
 
#define OnTick OldOnTick2 // TesterBenchmark.mqh ?
 
#else // #ifdef VIRTUAL_TESTER
 
#ifdef VIRTUAL_TESTER_FAST
 
#include "Sync.mqh"
 
struct HISTORY_UNIT
{
long Ticket;
int Type;
double Lots;
 
HISTORY_UNIT( void ) : Ticket(::OrderTicket()), Type(::OrderType()), Lots(::OrderLots())
{
}
 
bool operator !=( const HISTORY_UNIT &Unit ) const
{
return((this.Ticket != Unit.Ticket) || (this.Type != Unit.Type) || (this.Lots != Unit.Lots));
}
 
bool IsChange( void )
{
const HISTORY_UNIT Tmp;
const bool Res = (this != Tmp);
 
if (Res)
this = Tmp;
 
return(Res);
}
};
 
// Возвращает true только в случае, если с последнего вызова произошли торговые изменения
bool IsChange( void )
{
static HISTORY_UNIT History[];
 
const int Total = OrdersTotal();
bool Res = (ArraySize(History) != Total);
 
for (int i = 0, j = Res ? ArrayResize(History, 0, Total) : 0; i < Total; i++)
if (OrderSelect(i, SELECT_BY_POS))
{
if (Res || (Res = History[j].IsChange()))
ArrayResize(History, j + 1, Total);
 
j++;
}
 
return(Res);
}
 
void OnTick()
{
static const bool Init = VIRTUAL::SelectByHandle(VIRTUAL::Create());
 
// https://www.mql5.com/ru/forum/282062/page31#comment_19616482
// if (VIRTUAL::SelectByIndex(1))
{
VIRTUAL::NewTick(::OldOnTick2);
/*
if (IsChange()) // https://www.mql5.com/ru/forum/215783/page12#comment_5924356
SYNC::Positions<ISTIME>();
*/
// VIRTUAL::SelectByIndex();
}
}
 
#define OnTick OldOnTick2 // TesterBenchmark.mqh ?
 
#endif // #ifdef VIRTUAL_TESTER_FAST
 
#endif // #ifdef VIRTUAL_TESTER #else
 
#ifndef __MQL5__
// VIRTUAL::POINTER
#undef private
#undef protected
#endif // #ifndef __MQL5__
 
#undef VIRTUAL_SELECTORDERS_MACROS
 
#ifdef DELETE_MACRO_MAX_ORDERS
#undef MAX_ORDERS
#undef DELETE_MACRO_MAX_ORDERS
#endif // #ifdef DELETE_MACRO_MAX_ORDERS