403 lines
13 KiB
MQL5
403 lines
13 KiB
MQL5
//+------------------------------------------------------------------+
| |||
//| MockPositionStore.mqh |
| |||
//| Synthetic Test Harness for MetaTrader 5 |
| |||
//| Institutional Architecture in MQL5 - Part 2 Series |
| |||
//+------------------------------------------------------------------+
| |||
#ifndef SYNTHETIC_TEST_HARNESS_MOCK_POSITION_STORE_MQH
| |||
#define SYNTHETIC_TEST_HARNESS_MOCK_POSITION_STORE_MQH
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Mock data structure for an open position |
| |||
//+------------------------------------------------------------------+
| |||
struct SMockPosition
| |||
{
| |||
ulong ticket;
| |||
string symbol;
| |||
long magic;
| |||
ENUM_POSITION_TYPE type;
| |||
double volume;
| |||
double price_open;
| |||
double price_current;
| |||
double sl;
| |||
double tp;
| |||
double profit;
| |||
double swap;
| |||
datetime time_open;
| |||
string comment;
| |||
};
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Mock data structure for a closed deal (history) |
| |||
//+------------------------------------------------------------------+
| |||
struct SMockDeal
| |||
{
| |||
ulong ticket;
| |||
ulong order;
| |||
string symbol;
| |||
long magic;
| |||
ENUM_DEAL_TYPE type;
| |||
ENUM_DEAL_ENTRY entry;
| |||
double volume;
| |||
double price;
| |||
double profit;
| |||
double commission;
| |||
double swap;
| |||
datetime time;
| |||
string comment;
| |||
};
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Mock data structure for a pending order |
| |||
//+------------------------------------------------------------------+
| |||
struct SMockPendingOrder
| |||
{
| |||
ulong ticket;
| |||
string symbol;
| |||
long magic;
| |||
ENUM_ORDER_TYPE type;
| |||
ENUM_ORDER_STATE state;
| |||
double volume_initial;
| |||
double volume_current;
| |||
double price_open;
| |||
double sl;
| |||
double tp;
| |||
datetime time_setup;
| |||
string comment;
| |||
};
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| CMockPositionStore: In-memory store for synthetic account state |
| |||
//| |
| |||
//| Single Responsibility: Decoupled container managing synthetic |
| |||
//| positions, orders, and historical deals for unit testing without |
| |||
//| coupling to terminal state or CSyntheticEngine tick simulation. |
| |||
//+------------------------------------------------------------------+
| |||
class CMockPositionStore
| |||
{
| |||
private:
| |||
SMockPosition m_positions[];
| |||
SMockDeal m_deals[];
| |||
SMockPendingOrder m_orders[];
| |||
| |||
int m_selected_position_idx;
| |||
ulong m_selected_deal_ticket;
| |||
ulong m_selected_order_ticket;
| |||
bool m_history_selected;
| |||
datetime m_history_from;
| |||
datetime m_history_to;
| |||
| |||
public:
| |||
//+---------------------------------------------------------------+
| |||
//| Constructor & Destructor |
| |||
//+---------------------------------------------------------------+
| |||
CMockPositionStore(void)
| |||
{
| |||
m_selected_position_idx = -1;
| |||
m_selected_deal_ticket = 0;
| |||
m_selected_order_ticket = 0;
| |||
m_history_selected = false;
| |||
m_history_from = 0;
| |||
m_history_to = 0;
| |||
Clear();
| |||
}
| |||
| |||
~CMockPositionStore(void)
| |||
{
| |||
Clear();
| |||
}
| |||
| |||
//+---------------------------------------------------------------+
| |||
//| Clear all mock storage data |
| |||
//+---------------------------------------------------------------+
| |||
void Clear(void)
| |||
{
| |||
ArrayFree(m_positions);
| |||
ArrayFree(m_deals);
| |||
ArrayFree(m_orders);
| |||
m_selected_position_idx = -1;
| |||
m_selected_deal_ticket = 0;
| |||
m_selected_order_ticket = 0;
| |||
m_history_selected = false;
| |||
}
| |||
| |||
//+---------------------------------------------------------------+
| |||
//| Position Setup Helpers (Used by Unit Tests) |
| |||
//+---------------------------------------------------------------+
| |||
void AddPosition(const SMockPosition &pos)
| |||
{
| |||
int size = ArraySize(m_positions);
| |||
ArrayResize(m_positions, size + 1);
| |||
m_positions[size] = pos;
| |||
}
| |||
| |||
bool RemovePosition(const ulong ticket)
| |||
{
| |||
int size = ArraySize(m_positions);
| |||
for(int i = 0; i < size; i++)
| |||
{
| |||
if(m_positions[i].ticket == ticket)
| |||
{
| |||
for(int j = i; j < size - 1; j++)
| |||
m_positions[j] = m_positions[j + 1];
| |||
ArrayResize(m_positions, size - 1);
| |||
if(m_selected_position_idx >= ArraySize(m_positions))
| |||
m_selected_position_idx = -1;
| |||
return(true);
| |||
}
| |||
}
| |||
return(false);
| |||
}
| |||
| |||
//+---------------------------------------------------------------+
| |||
//| Position Queries (Mirrors MT5 API) |
| |||
//+---------------------------------------------------------------+
| |||
int PositionsTotal(void) const
| |||
{
| |||
return(ArraySize(m_positions));
| |||
}
| |||
| |||
string PositionGetSymbol(const int index)
| |||
{
| |||
if(index < 0 || index >= ArraySize(m_positions))
| |||
{
| |||
m_selected_position_idx = -1;
| |||
return("");
| |||
}
| |||
m_selected_position_idx = index;
| |||
return(m_positions[index].symbol);
| |||
}
| |||
| |||
long PositionGetInteger(const ENUM_POSITION_PROPERTY_INTEGER property) const
| |||
{
| |||
if(m_selected_position_idx < 0 || m_selected_position_idx >= ArraySize(m_positions))
| |||
return(0);
| |||
| |||
switch(property)
| |||
{
| |||
case POSITION_TICKET: return((long)m_positions[m_selected_position_idx].ticket);
| |||
case POSITION_MAGIC: return(m_positions[m_selected_position_idx].magic);
| |||
case POSITION_TYPE: return((long)m_positions[m_selected_position_idx].type);
| |||
case POSITION_TIME: return((long)m_positions[m_selected_position_idx].time_open);
| |||
default: return(0);
| |||
}
| |||
}
| |||
| |||
double PositionGetDouble(const ENUM_POSITION_PROPERTY_DOUBLE property) const
| |||
{
| |||
if(m_selected_position_idx < 0 || m_selected_position_idx >= ArraySize(m_positions))
| |||
return(0.0);
| |||
| |||
switch(property)
| |||
{
| |||
case POSITION_VOLUME: return(m_positions[m_selected_position_idx].volume);
| |||
case POSITION_PRICE_OPEN: return(m_positions[m_selected_position_idx].price_open);
| |||
case POSITION_PRICE_CURRENT: return(m_positions[m_selected_position_idx].price_current);
| |||
case POSITION_SL: return(m_positions[m_selected_position_idx].sl);
| |||
case POSITION_TP: return(m_positions[m_selected_position_idx].tp);
| |||
case POSITION_PROFIT: return(m_positions[m_selected_position_idx].profit);
| |||
case POSITION_SWAP: return(m_positions[m_selected_position_idx].swap);
| |||
default: return(0.0);
| |||
}
| |||
}
| |||
| |||
//+---------------------------------------------------------------+
| |||
//| Deal / History Setup Helpers (Used by Unit Tests) |
| |||
//+---------------------------------------------------------------+
| |||
void AddDeal(const SMockDeal &deal)
| |||
{
| |||
int size = ArraySize(m_deals);
| |||
ArrayResize(m_deals, size + 1);
| |||
m_deals[size] = deal;
| |||
}
| |||
| |||
bool HistorySelect(const datetime from_date, const datetime to_date)
| |||
{
| |||
m_history_selected = true;
| |||
m_history_from = from_date;
| |||
m_history_to = to_date;
| |||
return(true);
| |||
}
| |||
| |||
int HistoryDealsTotal(void) const
| |||
{
| |||
if(!m_history_selected)
| |||
return(0);
| |||
| |||
int count = 0;
| |||
int size = ArraySize(m_deals);
| |||
for(int i = 0; i < size; i++)
| |||
{
| |||
if(m_deals[i].time >= m_history_from && m_deals[i].time <= m_history_to)
| |||
count++;
| |||
}
| |||
return(count);
| |||
}
| |||
| |||
ulong HistoryDealGetTicket(const int index)
| |||
{
| |||
if(!m_history_selected)
| |||
return(0);
| |||
| |||
int count = 0;
| |||
int size = ArraySize(m_deals);
| |||
for(int i = 0; i < size; i++)
| |||
{
| |||
if(m_deals[i].time >= m_history_from && m_deals[i].time <= m_history_to)
| |||
{
| |||
if(count == index)
| |||
{
| |||
m_selected_deal_ticket = m_deals[i].ticket;
| |||
return(m_deals[i].ticket);
| |||
}
| |||
count++;
| |||
}
| |||
}
| |||
m_selected_deal_ticket = 0;
| |||
return(0);
| |||
}
| |||
| |||
long HistoryDealGetInteger(const ulong ticket, const ENUM_DEAL_PROPERTY_INTEGER property) const
| |||
{
| |||
int size = ArraySize(m_deals);
| |||
for(int i = 0; i < size; i++)
| |||
{
| |||
if(m_deals[i].ticket == ticket)
| |||
{
| |||
switch(property)
| |||
{
| |||
case DEAL_TICKET: return((long)m_deals[i].ticket);
| |||
case DEAL_ORDER: return((long)m_deals[i].order);
| |||
case DEAL_TIME: return((long)m_deals[i].time);
| |||
case DEAL_TYPE: return((long)m_deals[i].type);
| |||
case DEAL_ENTRY: return((long)m_deals[i].entry);
| |||
case DEAL_MAGIC: return(m_deals[i].magic);
| |||
default: return(0);
| |||
}
| |||
}
| |||
}
| |||
return(0);
| |||
}
| |||
| |||
double HistoryDealGetDouble(const ulong ticket, const ENUM_DEAL_PROPERTY_DOUBLE property) const
| |||
{
| |||
int size = ArraySize(m_deals);
| |||
for(int i = 0; i < size; i++)
| |||
{
| |||
if(m_deals[i].ticket == ticket)
| |||
{
| |||
switch(property)
| |||
{
| |||
case DEAL_VOLUME: return(m_deals[i].volume);
| |||
case DEAL_PRICE: return(m_deals[i].price);
| |||
case DEAL_COMMISSION: return(m_deals[i].commission);
| |||
case DEAL_SWAP: return(m_deals[i].swap);
| |||
case DEAL_PROFIT: return(m_deals[i].profit);
| |||
default: return(0.0);
| |||
}
| |||
}
| |||
}
| |||
return(0.0);
| |||
}
| |||
| |||
string HistoryDealGetString(const ulong ticket, const ENUM_DEAL_PROPERTY_STRING property) const
| |||
{
| |||
int size = ArraySize(m_deals);
| |||
for(int i = 0; i < size; i++)
| |||
{
| |||
if(m_deals[i].ticket == ticket)
| |||
{
| |||
switch(property)
| |||
{
| |||
case DEAL_SYMBOL: return(m_deals[i].symbol);
| |||
case DEAL_COMMENT: return(m_deals[i].comment);
| |||
default: return("");
| |||
}
| |||
}
| |||
}
| |||
return("");
| |||
}
| |||
| |||
//+---------------------------------------------------------------+
| |||
//| Pending Order Setup Helpers (Used by Unit Tests) |
| |||
//+---------------------------------------------------------------+
| |||
void AddOrder(const SMockPendingOrder &order)
| |||
{
| |||
int size = ArraySize(m_orders);
| |||
ArrayResize(m_orders, size + 1);
| |||
m_orders[size] = order;
| |||
}
| |||
| |||
bool RemoveOrder(const ulong ticket)
| |||
{
| |||
int size = ArraySize(m_orders);
| |||
for(int i = 0; i < size; i++)
| |||
{
| |||
if(m_orders[i].ticket == ticket)
| |||
{
| |||
for(int j = i; j < size - 1; j++)
| |||
m_orders[j] = m_orders[j + 1];
| |||
ArrayResize(m_orders, size - 1);
| |||
return(true);
| |||
}
| |||
}
| |||
return(false);
| |||
}
| |||
| |||
int OrdersTotal(void) const
| |||
{
| |||
return(ArraySize(m_orders));
| |||
}
| |||
| |||
ulong OrderGetTicket(const int index)
| |||
{
| |||
if(index < 0 || index >= ArraySize(m_orders))
| |||
{
| |||
m_selected_order_ticket = 0;
| |||
return(0);
| |||
}
| |||
m_selected_order_ticket = m_orders[index].ticket;
| |||
return(m_orders[index].ticket);
| |||
}
| |||
| |||
long OrderGetInteger(const ulong ticket, const ENUM_ORDER_PROPERTY_INTEGER property) const
| |||
{
| |||
int size = ArraySize(m_orders);
| |||
for(int i = 0; i < size; i++)
| |||
{
| |||
if(m_orders[i].ticket == ticket)
| |||
{
| |||
switch(property)
| |||
{
| |||
case ORDER_TICKET: return((long)m_orders[i].ticket);
| |||
case ORDER_MAGIC: return(m_orders[i].magic);
| |||
case ORDER_TYPE: return((long)m_orders[i].type);
| |||
case ORDER_STATE: return((long)m_orders[i].state);
| |||
case ORDER_TIME_SETUP: return((long)m_orders[i].time_setup);
| |||
default: return(0);
| |||
}
| |||
}
| |||
}
| |||
return(0);
| |||
}
| |||
| |||
string OrderGetString(const ulong ticket, const ENUM_ORDER_PROPERTY_STRING property) const
| |||
{
| |||
int size = ArraySize(m_orders);
| |||
for(int i = 0; i < size; i++)
| |||
{
| |||
if(m_orders[i].ticket == ticket)
| |||
{
| |||
switch(property)
| |||
{
| |||
case ORDER_SYMBOL: return(m_orders[i].symbol);
| |||
case ORDER_COMMENT: return(m_orders[i].comment);
| |||
default: return("");
| |||
}
| |||
}
| |||
}
| |||
return("");
| |||
}
| |||
};
| |||
| |||
#endif
| |||
//+------------------------------------------------------------------+
|