forked from rosh/MQL5Book
472 lines
14 KiB
MQL5
472 lines
14 KiB
MQL5
//+------------------------------------------------------------------+
| |||
//| TradeBaseMonitor.mqh |
| |||
//| Copyright 2022, MetaQuotes Ltd. |
| |||
//| https://www.mql5.com |
| |||
//+------------------------------------------------------------------+
| |||
#include "EnumToArray.mqh"
| |||
#include "TradeUtils.mqh"
| |||
#include "Defines.mqh"
| |||
#include "TypeName.mqh"
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Common names for any of triple ENUMs used for properties types |
| |||
//+------------------------------------------------------------------+
| |||
enum PROP_TYPE
| |||
{
| |||
PROP_TYPE_INTEGER,
| |||
PROP_TYPE_DOUBLE,
| |||
PROP_TYPE_STRING,
| |||
};
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Base class for orders/deals/positions monitors (props viewer) |
| |||
//| Used by concrete monitors: |
| |||
//| #include "PositionMonitor.mqh" |
| |||
//| #include "DealMonitor.mqh" |
| |||
//| #include "OrderMonitor.mqh" |
| |||
//| Types I, D, S stand for 3 ENUM-types of properties |
| |||
//| I - integers, D - doubles, S - strings |
| |||
//+------------------------------------------------------------------+
| |||
template<typename I,typename D,typename S>
| |||
class MonitorInterface
| |||
{
| |||
protected:
| |||
bool ready;
| |||
| |||
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>
| |||
static int boundary(const E dummy = (E)NULL)
| |||
{
| |||
int values[];
| |||
const int n = EnumToArray(dummy, values, 0, 1000);
| |||
ArraySort(values);
| |||
return values[n - 1];
| |||
}
| |||
| |||
public:
| |||
| |||
class TradeState
| |||
{
| |||
static int indices[][2];
| |||
static int j, d, s;
| |||
public:
| |||
const static int limit;
| |||
| |||
static PROP_TYPE type(const int i)
| |||
{
| |||
return (PROP_TYPE)indices[i][0];
| |||
}
| |||
| |||
static int offset(const int i)
| |||
{
| |||
return indices[i][1];
| |||
}
| |||
| |||
static int size(const PROP_TYPE p)
| |||
{
| |||
switch(p)
| |||
{
| |||
case PROP_TYPE_INTEGER: return j;
| |||
case PROP_TYPE_DOUBLE: return d;
| |||
case PROP_TYPE_STRING: return s;
| |||
}
| |||
return 0;
| |||
}
| |||
| |||
static string enumname(const PROP_TYPE p)
| |||
{
| |||
switch(p)
| |||
{
| |||
case PROP_TYPE_INTEGER: return TYPENAME(I);
| |||
case PROP_TYPE_DOUBLE: return TYPENAME(D);
| |||
case PROP_TYPE_STRING: return TYPENAME(S);
| |||
}
| |||
return NULL;
| |||
}
| |||
| |||
static int calcIndices()
| |||
{
| |||
const int size = fmax(boundary<I>(),
| |||
fmax(boundary<D>(), boundary<S>())) + 1;
| |||
ArrayResize(indices, size);
| |||
j = d = s = 0;
| |||
for(int i = 0; i < size; ++i)
| |||
{
| |||
if(detect<I>(i))
| |||
{
| |||
indices[i][0] = PROP_TYPE_INTEGER;
| |||
indices[i][1] = j++;
| |||
}
| |||
else if(detect<D>(i))
| |||
{
| |||
indices[i][0] = PROP_TYPE_DOUBLE;
| |||
indices[i][1] = d++;
| |||
}
| |||
else if(detect<S>(i))
| |||
{
| |||
indices[i][0] = PROP_TYPE_STRING;
| |||
indices[i][1] = s++;
| |||
}
| |||
else
| |||
{
| |||
Print("Unresolved int value as enum: ", i, " for ", TYPENAME(TradeState));
| |||
indices[i][0] = -1;
| |||
indices[i][1] = -1;
| |||
}
| |||
}
| |||
| |||
return size;
| |||
}
| |||
| |||
long ulongs[];
| |||
double doubles[];
| |||
string strings[];
| |||
const MonitorInterface *owner;
| |||
| |||
TradeState(const MonitorInterface *ptr = NULL) : owner(ptr)
| |||
{
| |||
reset();
| |||
cache();
| |||
}
| |||
| |||
template<typename T>
| |||
void _get(const int e, T &value) const // overload with a reference of output value
| |||
{
| |||
if(owner)
| |||
{
| |||
value = owner.get(e, value);
| |||
}
| |||
else
| |||
{
| |||
switch(indices[e][0])
| |||
{
| |||
case PROP_TYPE_INTEGER: value = (T)ulongs[indices[e][1]]; break;
| |||
case PROP_TYPE_DOUBLE: value = (T)doubles[indices[e][1]]; break;
| |||
case PROP_TYPE_STRING: value = (T)strings[indices[e][1]]; break;
| |||
}
| |||
}
| |||
}
| |||
| |||
template<typename T>
| |||
T _get(const int e) const // read cache directly
| |||
{
| |||
T value = (T)NULL;
| |||
switch(indices[e][0])
| |||
{
| |||
case PROP_TYPE_INTEGER: value = (T)ulongs[indices[e][1]]; break;
| |||
case PROP_TYPE_DOUBLE: value = (T)doubles[indices[e][1]]; break;
| |||
case PROP_TYPE_STRING: value = (T)strings[indices[e][1]]; break;
| |||
}
| |||
return value;
| |||
}
| |||
| |||
template<typename T>
| |||
void _set(const int e, T value) const // write cache directly
| |||
{
| |||
switch(indices[e][0])
| |||
{
| |||
case PROP_TYPE_INTEGER: ulongs[indices[e][1]] = (long)value; break;
| |||
case PROP_TYPE_DOUBLE: doubles[indices[e][1]] = (double)value; break;
| |||
case PROP_TYPE_STRING: strings[indices[e][1]] = (string)value; break;
| |||
}
| |||
}
| |||
| |||
void cache()
| |||
{
| |||
for(int i = 0; i < limit; ++i)
| |||
{
| |||
switch(indices[i][0])
| |||
{
| |||
case PROP_TYPE_INTEGER: _get(i, ulongs[indices[i][1]]); break;
| |||
case PROP_TYPE_DOUBLE: _get(i, doubles[indices[i][1]]); break;
| |||
case PROP_TYPE_STRING: _get(i, strings[indices[i][1]]); break;
| |||
}
| |||
}
| |||
}
| |||
| |||
void reset()
| |||
{
| |||
ArrayResize(ulongs, j);
| |||
ArrayResize(doubles, d);
| |||
ArrayResize(strings, s);
| |||
| |||
ArrayInitialize(ulongs, 0);
| |||
ArrayInitialize(doubles, 0.0);
| |||
ArrayResize(strings, 0);
| |||
ArrayResize(strings, s);
| |||
}
| |||
};
| |||
| |||
| |||
MonitorInterface(): ready(false) { }
| |||
| |||
bool isReady() const
| |||
{
| |||
return ready;
| |||
}
| |||
| |||
template<typename E>
| |||
static string enumstr(const long v)
| |||
{
| |||
return EnumToString((E)v);
| |||
}
| |||
| |||
// all properties of type enum E (slow straightforward version)
| |||
template<typename E>
| |||
void list2log() const
| |||
{
| |||
E e = (E)0; // disable warning 'possible use of uninitialized variable'
| |||
int array[];
| |||
const int n = EnumToArray(e, array, 0, USHORT_MAX);
| |||
ResetLastError();
| |||
Print(TYPENAME(E), " Count=", n);
| |||
for(int i = 0; i < n; ++i)
| |||
{
| |||
e = (E)array[i];
| |||
PrintFormat("% 3d %s=%s", i, EnumToString(e), stringify(e));
| |||
}
| |||
}
| |||
| |||
// all properties of type enum I/D/S (fast indexed version)
| |||
void list2log(const PROP_TYPE super) const
| |||
{
| |||
Print(TradeState::enumname(super), " Count=", TradeState::size(super));
| |||
| |||
for(int k = 0, i = 0; k < TradeState::limit; ++k)
| |||
{
| |||
if(TradeState::type(k) == super)
| |||
{
| |||
switch(super)
| |||
{
| |||
case PROP_TYPE_INTEGER:
| |||
PrintFormat("% 3d %s=%s", i++, EnumToString((I)k), stringify((I)k));
| |||
break;
| |||
case PROP_TYPE_DOUBLE:
| |||
PrintFormat("% 3d %s=%s", i++, EnumToString((D)k), stringify((D)k));
| |||
break;
| |||
case PROP_TYPE_STRING:
| |||
PrintFormat("% 3d %s=%s", i++, EnumToString((S)k), stringify((S)k));
| |||
break;
| |||
}
| |||
}
| |||
}
| |||
}
| |||
| |||
// on false check _LastError for one of the following errors:
| |||
// - 4753 ERR_TRADE_POSITION_NOT_FOUND
| |||
// - 4754 ERR_TRADE_ORDER_NOT_FOUND
| |||
// - 4755 ERR_TRADE_DEAL_NOT_FOUND
| |||
// also 'ready' should be dropped to false
| |||
virtual bool refresh() = 0;
| |||
| |||
virtual void print() const
| |||
{
| |||
Print(TYPENAME(this), (ready ? "" : " not ready!"));
| |||
if(!ready) return;
| |||
list2log(PROP_TYPE_INTEGER);
| |||
list2log(PROP_TYPE_DOUBLE);
| |||
list2log(PROP_TYPE_STRING);
| |||
/*
| |||
list2log<I>();
| |||
list2log<D>();
| |||
list2log<S>();
| |||
*/
| |||
}
| |||
| |||
virtual long get(const I property) const = 0;
| |||
virtual double get(const D property) const = 0;
| |||
virtual string get(const S property) const = 0;
| |||
virtual long get(const int property, const long) const = 0;
| |||
virtual double get(const int property, const double) const = 0;
| |||
virtual string get(const int property, const string) const = 0;
| |||
| |||
virtual string stringify(const long v, const I property) const = 0;
| |||
| |||
virtual string stringify(const I property) const
| |||
{
| |||
return stringify(get(property), property);
| |||
}
| |||
| |||
virtual string stringify(const D property, const string format = NULL) const
| |||
{
| |||
if(format == NULL) return (string)get(property);
| |||
return StringFormat(format, get(property));
| |||
}
| |||
| |||
virtual string stringify(const S property) const
| |||
{
| |||
return get(property);
| |||
}
| |||
| |||
virtual string stringify(const int i) const
| |||
{
| |||
switch(TradeState::type(i))
| |||
{
| |||
case PROP_TYPE_INTEGER: return stringify((I)i);
| |||
case PROP_TYPE_DOUBLE: return stringify((D)i);
| |||
case PROP_TYPE_STRING: return stringify((S)i);
| |||
}
| |||
return NULL;
| |||
}
| |||
| |||
static string label(const int i)
| |||
{
| |||
switch(TradeState::type(i))
| |||
{
| |||
case PROP_TYPE_INTEGER: return EnumToString((I)i);
| |||
case PROP_TYPE_DOUBLE: return EnumToString((D)i);
| |||
case PROP_TYPE_STRING: return EnumToString((S)i);
| |||
}
| |||
return NULL;
| |||
}
| |||
| |||
bool operator==(const MonitorInterface &that) const
| |||
{
| |||
for(int i = 0; i < TradeState::limit; ++i)
| |||
{
| |||
switch(TradeState::type(i))
| |||
{
| |||
case PROP_TYPE_INTEGER:
| |||
if(this.get((I)i) != that.get((I)i)) return false;
| |||
break;
| |||
case PROP_TYPE_DOUBLE:
| |||
if(!TU::Equal(this.get((D)i), that.get((D)i))) return false;
| |||
break;
| |||
case PROP_TYPE_STRING:
| |||
if(this.get((S)i) != that.get((S)i)) return false;
| |||
break;
| |||
}
| |||
}
| |||
return true;
| |||
}
| |||
| |||
bool operator!=(const MonitorInterface &that) const
| |||
{
| |||
return !(this == that);
| |||
}
| |||
| |||
bool diff(const MonitorInterface &that, int &changes[])
| |||
{
| |||
int required[], r = 0;
| |||
if(ArraySize(changes) > 0) // optionally can request only part of properties
| |||
{
| |||
ArraySwap(required, changes);
| |||
ArraySort(required);
| |||
ResetLastError();
| |||
}
| |||
const int rtotal = ArraySize(required);
| |||
| |||
ArrayResize(changes, 0);
| |||
for(int i = 0; i < TradeState::limit; ++i)
| |||
{
| |||
if(rtotal > 0)
| |||
{
| |||
if(required[r] == i)
| |||
{
| |||
++r; // found, fall down to comparison and mark next property to find
| |||
}
| |||
else
| |||
{
| |||
continue; // skip if not required
| |||
}
| |||
}
| |||
| |||
switch(TradeState::type(i))
| |||
{
| |||
case PROP_TYPE_INTEGER:
| |||
if(this.get((I)i) != that.get((I)i))
| |||
{
| |||
PUSH(changes, i);
| |||
}
| |||
break;
| |||
case PROP_TYPE_DOUBLE:
| |||
if(!TU::Equal(this.get((D)i), that.get((D)i)))
| |||
{
| |||
PUSH(changes, i);
| |||
}
| |||
break;
| |||
case PROP_TYPE_STRING:
| |||
if(this.get((S)i) != that.get((S)i))
| |||
{
| |||
PUSH(changes, i);
| |||
}
| |||
break;
| |||
}
| |||
}
| |||
return ArraySize(changes) > 0;
| |||
}
| |||
| |||
bool operator==(const TradeState &that) const
| |||
{
| |||
for(int i = 0; i < TradeState::limit; ++i)
| |||
{
| |||
switch(TradeState::type(i))
| |||
{
| |||
case PROP_TYPE_INTEGER:
| |||
if(this.get((I)i) != that.ulongs[TradeState::offset(i)]) return false;
| |||
break;
| |||
case PROP_TYPE_DOUBLE:
| |||
if(!TU::Equal(this.get((D)i), that.doubles[TradeState::offset(i)])) return false;
| |||
break;
| |||
case PROP_TYPE_STRING:
| |||
if(this.get((S)i) != that.strings[TradeState::offset(i)]) return false;
| |||
break;
| |||
}
| |||
}
| |||
return true;
| |||
}
| |||
| |||
bool operator!=(const TradeState &that) const
| |||
{
| |||
return !(this == that);
| |||
}
| |||
| |||
bool diff(const TradeState &that, int &changes[])
| |||
{
| |||
ArrayResize(changes, 0);
| |||
for(int i = 0; i < TradeState::limit; ++i)
| |||
{
| |||
switch(TradeState::type(i))
| |||
{
| |||
case PROP_TYPE_INTEGER:
| |||
if(this.get((I)i) != that.ulongs[TradeState::offset(i)])
| |||
{
| |||
PUSH(changes, i);
| |||
}
| |||
break;
| |||
case PROP_TYPE_DOUBLE:
| |||
if(!TU::Equal(this.get((D)i), that.doubles[TradeState::offset(i)]))
| |||
{
| |||
PUSH(changes, i);
| |||
}
| |||
break;
| |||
case PROP_TYPE_STRING:
| |||
if(this.get((S)i) != that.strings[TradeState::offset(i)])
| |||
{
| |||
PUSH(changes, i);
| |||
}
| |||
break;
| |||
}
| |||
}
| |||
return ArraySize(changes) > 0;
| |||
}
| |||
| |||
};
| |||
//+------------------------------------------------------------------+
| |||
template<typename I,typename D,typename S>
| |||
static int MonitorInterface::TradeState::indices[][2];
| |||
template<typename I,typename D,typename S>
| |||
static int MonitorInterface::TradeState::j, MonitorInterface::TradeState::d, MonitorInterface::TradeState::s;
| |||
template<typename I,typename D,typename S>
| |||
const static int MonitorInterface::TradeState::limit = MonitorInterface::TradeState::calcIndices();
|