forked from rosh/MQL5Book
228 lines
7.5 KiB
MQL5
228 lines
7.5 KiB
MQL5
//+------------------------------------------------------------------+
| |
//| TradeState.mqh |
| |
//| Copyright 2022, MetaQuotes Ltd. |
| |
//| https://www.mql5.com |
| |
//+------------------------------------------------------------------+
| |
#include "TradeState.mqh"
| |
#include "PositionFilter.mqh"
| |
#include "OrderFilter.mqh"
| |
#include "DealFilter.mqh"
| |
#include "AutoPtr.mqh"
| |
#include "ArrayUtils.mqh"
| |
#include "TypeName.mqh"
| |
| |
//+------------------------------------------------------------------+
| |
//| Base class with array of objects storing trade properties for |
| |
//| orders/deals/positions. |
| |
//| T should be a TradeState class, F should be a TradeFilter, |
| |
//| E is a property with identifier (usually SOME_TICKET) from ENUM |
| |
//+------------------------------------------------------------------+
| |
template<typename T,typename F,typename E>
| |
class TradeCache
| |
{
| |
AutoPtr<T> data[];
| |
const E property;
| |
const int NOT_FOUND_ERROR;
| |
| |
static bool isNull(const AutoPtr<T> &element)
| |
{
| |
return element[] == NULL;
| |
}
| |
| |
public:
| |
TradeCache(const E id, const int error): property(id), NOT_FOUND_ERROR(error) { }
| |
| |
virtual string rtti() const
| |
{
| |
return TYPENAME(this);
| |
}
| |
| |
virtual void onAdded(const T &state)
| |
{
| |
Print(rtti(), " added: ", state.get(property));
| |
#ifdef PRINT_DETAILS
| |
state.print();
| |
#endif
| |
}
| |
| |
virtual bool onRemoved(const T &state)
| |
{
| |
Print(rtti(), " removed: ", state.get(property));
| |
return true; // please, wipe out the reference (false would keep it)
| |
}
| |
| |
virtual void onUpdated(T &state, const int &changes[], const bool unexpected = false)
| |
{
| |
const ulong ticket = state.get(property);
| |
Print(rtti(), " changed: ", ticket, (unexpected ? " unexpectedly" : ""));
| |
for(int k = 0; k < ArraySize(changes); ++k)
| |
{
| |
switch(T::TradeState::type(changes[k]))
| |
{
| |
case PROP_TYPE_INTEGER:
| |
Print(T::label(changes[k]), ": ",
| |
state.stringify(changes[k]), " -> ",
| |
state.stringifyRaw(changes[k]));
| |
break;
| |
case PROP_TYPE_DOUBLE:
| |
Print(T::label(changes[k]), ": ",
| |
state.stringify(changes[k]), " -> ",
| |
state.stringifyRaw(changes[k]));
| |
break;
| |
case PROP_TYPE_STRING:
| |
Print(T::label(changes[k]), ": ",
| |
state.stringify(changes[k]), " -> ",
| |
state.stringifyRaw(changes[k]));
| |
break;
| |
}
| |
}
| |
}
| |
| |
int size() const
| |
{
| |
return ArraySize(data);
| |
}
| |
| |
T *operator[](int i) const
| |
{
| |
return data[i][]; // return pure pointer (T*) from inside AutoPtr object
| |
}
| |
| |
void reset()
| |
{
| |
ArrayResize(data, 0); // this will call destructors of objects on pointers
| |
}
| |
| |
void scan(F &f)
| |
{
| |
const int existedBefore = ArraySize(data);
| |
| |
ulong tickets[];
| |
ArrayResize(tickets, existedBefore);
| |
for(int i = 0; i < existedBefore; ++i)
| |
{
| |
tickets[i] = data[i][].get(property);
| |
}
| |
| |
ulong objects[];
| |
f.select(objects);
| |
// if(_LastError != 0) Print(rtti(), " scan error: ", E2S(_LastError));
| |
for(int i = 0, j; i < ArraySize(objects); ++i)
| |
{
| |
const ulong ticket = objects[i];
| |
for(j = 0; j < existedBefore; ++j)
| |
{
| |
if(tickets[j] == ticket)
| |
{
| |
tickets[j] = 0; // mark as found
| |
break;
| |
}
| |
}
| |
| |
if(j == existedBefore) // not found
| |
{
| |
const T *ptr = new T(ticket);
| |
PUSH(data, ptr);
| |
onAdded(*ptr);
| |
}
| |
else
| |
{
| |
ResetLastError();
| |
int changes[];
| |
if(data[j][].getChanges(changes))
| |
{
| |
onUpdated(data[j][], changes);
| |
data[j][].update();
| |
}
| |
if(_LastError)
| |
{
| |
PrintFormat("%s: %lld (%s)", rtti(), ticket, E2S(_LastError));
| |
if(_LastError == NOT_FOUND_ERROR) // for example, ERR_TRADE_POSITION_NOT_FOUND
| |
{
| |
if(onRemoved(data[j][]))
| |
{
| |
data[j] = NULL; // free up the object and array element
| |
}
| |
}
| |
}
| |
}
| |
}
| |
| |
for(int j = 0; j < existedBefore; ++j)
| |
{
| |
if(tickets[j] == 0) continue; // skip already processed element
| |
| |
// this ticket was not found anymore, most likely deleted
| |
int changes[];
| |
ResetLastError();
| |
if(data[j][].getChanges(changes))
| |
{
| |
if(_LastError == NOT_FOUND_ERROR) // for example, ERR_TRADE_POSITION_NOT_FOUND
| |
{
| |
if(onRemoved(data[j][]))
| |
{
| |
data[j] = NULL; // free up the object and array element
| |
}
| |
continue;
| |
}
| |
| |
// NB! We should not normally fall down here
| |
PrintFormat("Unexpected ticket: %lld (%s) %s", tickets[j], E2S(_LastError), rtti());
| |
onUpdated(data[j][], changes, true);
| |
data[j][].update();
| |
}
| |
else
| |
{
| |
#ifdef PRINT_DETAILS
| |
PrintFormat("Orphaned element: %lld (%s) %s", tickets[j], E2S(_LastError), rtti());
| |
#endif
| |
}
| |
}
| |
| |
ArrayPurger<AutoPtr<T>> p(data, isNull);
| |
}
| |
};
| |
| |
//+------------------------------------------------------------------+
| |
//| Concrete class with array of positions and their properties |
| |
//+------------------------------------------------------------------+
| |
class PositionCache: public TradeCache<PositionState,PositionFilter,ENUM_POSITION_PROPERTY_INTEGER>
| |
{
| |
public:
| |
PositionCache(const ENUM_POSITION_PROPERTY_INTEGER selector = POSITION_TICKET,
| |
const int error = ERR_TRADE_POSITION_NOT_FOUND): TradeCache(selector, error) { }
| |
virtual string rtti() const override
| |
{
| |
return TYPENAME(this);
| |
}
| |
};
| |
| |
//+------------------------------------------------------------------+
| |
//| Concrete class with array of active orders and their properties |
| |
//+------------------------------------------------------------------+
| |
class OrderCache: public TradeCache<OrderState,OrderFilter,ENUM_ORDER_PROPERTY_INTEGER>
| |
{
| |
public:
| |
OrderCache(const ENUM_ORDER_PROPERTY_INTEGER selector = ORDER_TICKET,
| |
const int error = ERR_TRADE_ORDER_NOT_FOUND): TradeCache(selector, error) { }
| |
virtual string rtti() const override
| |
{
| |
return TYPENAME(this);
| |
}
| |
};
| |
| |
//+------------------------------------------------------------------+
| |
//| Concrete class with array of history orders and their properties |
| |
//+------------------------------------------------------------------+
| |
class HistoryOrderCache: public TradeCache<HistoryOrderState,HistoryOrderFilter,ENUM_ORDER_PROPERTY_INTEGER>
| |
{
| |
public:
| |
HistoryOrderCache(const ENUM_ORDER_PROPERTY_INTEGER selector = ORDER_TICKET,
| |
const int error = ERR_TRADE_ORDER_NOT_FOUND): TradeCache(selector, error) { }
| |
virtual string rtti() const override
| |
{
| |
return TYPENAME(this);
| |
}
| |
};
| |
| |
//+------------------------------------------------------------------+
|