forked from rosh/MQL5Book
205 lines
6.1 KiB
MQL5
205 lines
6.1 KiB
MQL5
//+------------------------------------------------------------------+
| |
//| CustomSymbolMonitor.mqh |
| |
//| Copyright 2022, MetaQuotes Ltd. |
| |
//| https://www.mql5.com |
| |
//+------------------------------------------------------------------+
| |
#include "EnumToArray.mqh"
| |
#include "SymbolMonitor.mqh"
| |
#include "AutoPtr.mqh"
| |
#include "MqlError.mqh"
| |
| |
//+------------------------------------------------------------------+
| |
//| Main class for reading and writing custom symbol properties |
| |
//+------------------------------------------------------------------+
| |
class CustomSymbolMonitor: public SymbolMonitor
| |
{
| |
protected:
| |
AutoPtr<const SymbolMonitor> origin;
| |
bool warningsOff;
| |
int errorCount;
| |
int mismatchCount;
| |
int fixes;
| |
| |
template<typename E>
| |
static bool detect(const int v)
| |
{
| |
ResetLastError();
| |
const string s = EnumToString((E)v); // resulting string is not used
| |
if(_LastError == 0) // only error code is important
| |
{
| |
return true;
| |
}
| |
return false;
| |
}
| |
| |
template<typename E>
| |
bool verify(const int i)
| |
{
| |
if(detect<E>(i))
| |
{
| |
if(get((E)i) != origin[].get((E)i))
| |
{
| |
if(!warningsOff)
| |
{
| |
Print("Fixing ", EnumToString((E)i), ": ", get((E)i), " <<< ", origin[].get((E)i));
| |
}
| |
set((E)i);
| |
fixes++;
| |
}
| |
return true;
| |
}
| |
return false;
| |
}
| |
| |
public:
| |
CustomSymbolMonitor(): SymbolMonitor(), origin(NULL), warningsOff(false) { }
| |
CustomSymbolMonitor(const string s, const SymbolMonitor *m = NULL): SymbolMonitor(s), origin(m), warningsOff(false) { }
| |
CustomSymbolMonitor(const string s, const string other): SymbolMonitor(s), origin(new SymbolMonitor(other)), warningsOff(false) { }
| |
| |
void inherit(const SymbolMonitor &m)
| |
{
| |
origin = &m;
| |
}
| |
| |
bool setAll(const bool reverseOrder = true, const int limit = UCHAR_MAX)
| |
{
| |
int properties[];
| |
ArrayResize(properties, limit);
| |
for(int i = 0; i < limit; ++i)
| |
{
| |
properties[i] = reverseOrder ? limit - i - 1 : i; // NB: number of errors differs in direct and reverse order
| |
}
| |
warningsOff = true;
| |
errorCount = 0;
| |
mismatchCount = 0;
| |
const bool success = set(properties);
| |
warningsOff = false;
| |
Print("Errors: ", errorCount);
| |
Print("Mismatches: ", mismatchCount);
| |
return success;
| |
}
| |
| |
int verifyAll(const int limit = UCHAR_MAX)
| |
{
| |
int properties[];
| |
ArrayResize(properties, limit);
| |
for(int i = 0; i < limit; ++i)
| |
{
| |
properties[i] = i;
| |
}
| |
return verify(properties);
| |
}
| |
| |
int verify(const int &properties[])
| |
{
| |
if(origin[] == NULL) return 0;
| |
fixes = 0;
| |
for(int i = 0; i < ArraySize(properties); ++i)
| |
{
| |
if(verify<ENUM_SYMBOL_INFO_INTEGER>(properties[i])
| |
|| verify<ENUM_SYMBOL_INFO_DOUBLE>(properties[i])
| |
|| verify<ENUM_SYMBOL_INFO_STRING>(properties[i]))
| |
{
| |
// ok
| |
}
| |
else
| |
{
| |
break; // unknown property: should not happen
| |
}
| |
}
| |
return fixes;
| |
}
| |
| |
bool set(const int &properties[])
| |
{
| |
bool success = true;
| |
for(int i = 0; i < ArraySize(properties); ++i)
| |
{
| |
if(detect<ENUM_SYMBOL_INFO_INTEGER>(properties[i]))
| |
{
| |
success = set((ENUM_SYMBOL_INFO_INTEGER)properties[i]) && success;
| |
}
| |
else if(detect<ENUM_SYMBOL_INFO_DOUBLE>(properties[i]))
| |
{
| |
success = set((ENUM_SYMBOL_INFO_DOUBLE)properties[i]) && success;
| |
}
| |
else if(detect<ENUM_SYMBOL_INFO_STRING>(properties[i]))
| |
{
| |
success = set((ENUM_SYMBOL_INFO_STRING)properties[i]) && success;
| |
}
| |
else if(!warningsOff)
| |
{
| |
Print("Unresolved int value as enum: ", i, " for ", name);
| |
}
| |
}
| |
return success;
| |
}
| |
| |
template<typename E>
| |
bool set(const E e)
| |
{
| |
if(origin[])
| |
{
| |
ResetLastError();
| |
const bool result = set(e, origin[].get(e));
| |
PrintFormat("%s %s %s -> %s (%d)", (result ? "true " : "false"), EnumToString(e), (string)origin[].get(e), E2S(_LastError), _LastError);
| |
if(result)
| |
{
| |
if(this.get(e) != origin[].get(e))
| |
{
| |
mismatchCount++;
| |
Print("!!!Mismatch!!! ", this.get(e), " <-> ", origin[].get(e));
| |
}
| |
}
| |
else
| |
{
| |
errorCount++;
| |
}
| |
return result;
| |
}
| |
return false;
| |
}
| |
| |
bool set(const ENUM_SYMBOL_INFO_INTEGER property, const long value) const
| |
{
| |
return CustomSymbolSetInteger(name, property, value);
| |
}
| |
| |
bool set(const ENUM_SYMBOL_INFO_DOUBLE property, const double value) const
| |
{
| |
return CustomSymbolSetDouble(name, property, value);
| |
}
| |
| |
bool set(const ENUM_SYMBOL_INFO_STRING property, const string value) const
| |
{
| |
return CustomSymbolSetString(name, property, value);
| |
}
| |
};
| |
| |
//+------------------------------------------------------------------+
| |
//| Example |
| |
//+------------------------------------------------------------------+
| |
/*
| |
#include "CustomSymbolMonitor.mqh"
| |
| |
void OnStart()
| |
{
| |
const string custom = "ABCXYZ";
| |
if(CustomSymbolCreate(custom, "", _Symbol))
| |
{
| |
CustomSymbolMonitor cs(custom, _Symbol);
| |
int p[] = {SYMBOL_CURRENCY_BASE, SYMBOL_CURRENCY_PROFIT};
| |
Print("Try 1 (may fail if _Symbol has SYMBOL_CALC_MODE_FOREX)");
| |
cs.verify(p);
| |
Print("Try 2 (will succeed)");
| |
cs.set(SYMBOL_TRADE_CALC_MODE, SYMBOL_CALC_MODE_CFD);
| |
cs.set(p);
| |
cs.verify(p);
| |
}
| |
else
| |
{
| |
PrintFormat("Can't create symbol '%s': %s", custom, E2S(_LastError));
| |
}
| |
}
| |
*/
| |
//+------------------------------------------------------------------+
|