// #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 static T VirtualMacrosFunc( const T Value ) { VIRTUAL::SelectByHandle(VIRTUAL_DELETE::VirtualMemory()); return(Value); } */ template 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 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 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 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(VirtualHandle, true); } ::OldOnTick2(); if (ReverseHandle) SYNC::Positions(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(); */ // 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