MQL5-SyntheticTestHarness/MQL5/Include/SyntheticTestHarness/CSyntheticEngine.mqh

391 lines
15 KiB
MQL5

//+------------------------------------------------------------------+
//| CSyntheticEngine.mqh |
//| Synthetic Test Harness for MetaTrader 5 |
//| Institutional Architecture in MQL5 - Part 2 Series |
//+------------------------------------------------------------------+
#ifndef SYNTHETIC_TEST_HARNESS_CSYNTHETIC_ENGINE_MQH
#define SYNTHETIC_TEST_HARNESS_CSYNTHETIC_ENGINE_MQH
#include "IMarketEnvironment.mqh"
#include "ITickProvider.mqh"
#include "MockTick.mqh"
#include "ErrorCodes.mqh"
#include "BehaviorDelegates.mqh"
#include "MockPositionStore.mqh"
//+------------------------------------------------------------------+
//| CSyntheticEngine: Dual-Mode Deterministic Testing Adapter. |
//| |
//| Execution Priority: |
//| Injected Function Pointer -> Current MockTick -> Safe Default |
//| |
//| Delegates position, order, and deal history tracking to the |
//| dedicated CMockPositionStore data container. |
//+------------------------------------------------------------------+
class CSyntheticEngine : public IMarketEnvironment
{
private:
ITickProvider *m_provider;
MockTick m_current_tick;
bool m_tick_loaded;
int m_ticks_processed;
//--- Dynamic Behavior Delegates (Function Pointers)
TOrderSendBehavior m_orderSendBehavior;
TGetBidBehavior m_getBidBehavior;
TGetAskBehavior m_getAskBehavior;
TGetSpreadBehavior m_getSpreadBehavior;
TGetTimeCurrentBehavior m_getTimeCurrentBehavior;
TMarketBookGetBehavior m_marketBookGetBehavior;
//--- Dedicated In-Memory Mock Position & Deal Store
CMockPositionStore m_position_store;
//--- Configurable Mock Account State
double m_mock_balance;
double m_mock_equity;
double m_mock_margin_free;
long m_mock_leverage;
//--- Configurable Mock Symbol Properties
double m_mock_point;
double m_mock_tick_value;
double m_mock_tick_size;
double m_mock_vol_min;
double m_mock_vol_max;
double m_mock_vol_step;
long m_mock_calc_mode;
long m_mock_digits;
public:
//+---------------------------------------------------------------+
//| Constructor & Destructor |
//+---------------------------------------------------------------+
CSyntheticEngine(ITickProvider *provider = NULL)
{
m_provider = provider;
m_tick_loaded = false;
m_ticks_processed = 0;
ZeroMemory(m_current_tick);
// Default Account Mocking
m_mock_balance = 100000.0;
m_mock_equity = 100000.0;
m_mock_margin_free = 100000.0;
m_mock_leverage = 100;
// Default Symbol Mocking (Standard EURUSD-like profile)
m_mock_point = 0.00001;
m_mock_tick_value = 1.0;
m_mock_tick_size = 0.00001;
m_mock_vol_min = 0.01;
m_mock_vol_max = 100.0;
m_mock_vol_step = 0.01;
m_mock_calc_mode = (long)SYMBOL_CALC_MODE_FOREX;
m_mock_digits = 5;
ResetAllBehaviors();
}
~CSyntheticEngine(void)
{
m_provider = NULL;
}
//+---------------------------------------------------------------+
//| Behavior Injection Hooks |
//+---------------------------------------------------------------+
void MockOrderSend(TOrderSendBehavior behavior) { m_orderSendBehavior = behavior; }
void MockGetBid(TGetBidBehavior behavior) { m_getBidBehavior = behavior; }
void MockGetAsk(TGetAskBehavior behavior) { m_getAskBehavior = behavior; }
void MockGetSpread(TGetSpreadBehavior behavior) { m_getSpreadBehavior = behavior; }
void MockGetTimeCurrent(TGetTimeCurrentBehavior behavior) { m_getTimeCurrentBehavior = behavior; }
void MockMarketBookGet(TMarketBookGetBehavior behavior) { m_marketBookGetBehavior = behavior; }
void ResetOrderSend(void) { m_orderSendBehavior = NULL; }
void ResetGetBid(void) { m_getBidBehavior = NULL; }
void ResetGetAsk(void) { m_getAskBehavior = NULL; }
void ResetGetSpread(void) { m_getSpreadBehavior = NULL; }
void ResetGetTimeCurrent(void) { m_getTimeCurrentBehavior = NULL; }
void ResetMarketBookGet(void) { m_marketBookGetBehavior = NULL; }
void ResetAllBehaviors(void)
{
m_orderSendBehavior = NULL;
m_getBidBehavior = NULL;
m_getAskBehavior = NULL;
m_getSpreadBehavior = NULL;
m_getTimeCurrentBehavior = NULL;
m_marketBookGetBehavior = NULL;
}
//+---------------------------------------------------------------+
//| Position Store Direct Accessor (for Test Setup) |
//+---------------------------------------------------------------+
CMockPositionStore* GetPositionStore(void)
{
return(&m_position_store);
}
//+---------------------------------------------------------------+
//| Setters for Mock Account and Symbol Environment Properties |
//+---------------------------------------------------------------+
void SetMockBalance(const double b) { m_mock_balance = b; }
void SetMockEquity(const double e) { m_mock_equity = e; }
void SetMockMarginFree(const double m) { m_mock_margin_free = m; }
void SetMockLeverage(const long l) { m_mock_leverage = l; }
void SetMockPoint(const double p) { m_mock_point = p; }
void SetMockTickValue(const double tv) { m_mock_tick_value = tv; }
void SetMockTickSize(const double ts) { m_mock_tick_size = ts; }
void SetMockVolumeLimits(const double min_vol, const double max_vol, const double step_vol)
{
m_mock_vol_min = min_vol;
m_mock_vol_max = max_vol;
m_mock_vol_step = step_vol;
}
void SetMockCalcMode(const long mode) { m_mock_calc_mode = mode; }
void SetMockDigits(const long d) { m_mock_digits = d; }
//+---------------------------------------------------------------+
//| Tick Provider Control |
//+---------------------------------------------------------------+
void SetTickProvider(ITickProvider *provider)
{
m_provider = provider;
m_tick_loaded = false;
m_ticks_processed = 0;
ZeroMemory(m_current_tick);
}
bool ProcessNextTick(void)
{
if(m_provider == NULL) return(false);
if(!m_provider.HasNext()) return(false);
m_tick_loaded = m_provider.GetNextTick(m_current_tick);
if(m_tick_loaded) m_ticks_processed++;
return(m_tick_loaded);
}
//+---------------------------------------------------------------+
//| IMarketEnvironment: Price & Time Queries |
//+---------------------------------------------------------------+
double GetBid(void)
{
if(m_getBidBehavior != NULL) return(m_getBidBehavior());
if(m_tick_loaded) return(m_current_tick.bid);
return(1.10000);
}
double GetAsk(void)
{
if(m_getAskBehavior != NULL) return(m_getAskBehavior());
if(m_tick_loaded) return(m_current_tick.ask);
return(1.10020);
}
double GetSpread(void)
{
if(m_getSpreadBehavior != NULL) return(m_getSpreadBehavior());
if(m_tick_loaded) return(m_current_tick.spread_points);
return(20.0);
}
datetime GetTimeCurrent(void)
{
if(m_getTimeCurrentBehavior != NULL) return(m_getTimeCurrentBehavior());
if(m_tick_loaded) return(m_current_tick.time);
return(D'2026.01.15 10:00:00');
}
//+---------------------------------------------------------------+
//| IMarketEnvironment: Order Execution |
//+---------------------------------------------------------------+
bool OrderSend(MqlTradeRequest &request, MqlTradeResult &result)
{
if(m_orderSendBehavior != NULL)
return(m_orderSendBehavior(request, result));
ZeroMemory(result);
if(!m_tick_loaded)
{
result.retcode = SYNTH_ERR_PRICE_OFF;
return(false);
}
if(IsExecutionError(m_current_tick.injected_error))
{
result.retcode = (uint)m_current_tick.injected_error;
return(false);
}
result.retcode = TRADE_RETCODE_DONE;
result.volume = request.volume;
result.bid = m_current_tick.bid;
result.ask = m_current_tick.ask;
result.price = (request.type == ORDER_TYPE_BUY) ? m_current_tick.ask : m_current_tick.bid;
result.deal = 12345678;
result.order = 87654321;
return(true);
}
bool MarketBookGet(const string symbol, MqlBookInfo &book[])
{
if(m_marketBookGetBehavior != NULL)
return(m_marketBookGetBehavior(symbol, book));
ArrayFree(book);
return(false);
}
//+---------------------------------------------------------------+
//| IMarketEnvironment: Generic Symbol Info Bridge |
//+---------------------------------------------------------------+
double SymbolInfoDouble(const string symbol, const ENUM_SYMBOL_INFO_DOUBLE property)
{
switch(property)
{
case SYMBOL_BID: return(GetBid());
case SYMBOL_ASK: return(GetAsk());
case SYMBOL_POINT: return(m_mock_point);
case SYMBOL_TRADE_TICK_VALUE: return(m_mock_tick_value);
case SYMBOL_TRADE_TICK_SIZE: return(m_mock_tick_size);
case SYMBOL_VOLUME_MIN: return(m_mock_vol_min);
case SYMBOL_VOLUME_MAX: return(m_mock_vol_max);
case SYMBOL_VOLUME_STEP: return(m_mock_vol_step);
default: return(0.0);
}
}
long SymbolInfoInteger(const string symbol, const ENUM_SYMBOL_INFO_INTEGER property)
{
switch(property)
{
case SYMBOL_SPREAD: return((long)GetSpread());
case SYMBOL_TRADE_CALC_MODE: return(m_mock_calc_mode);
case SYMBOL_DIGITS: return(m_mock_digits);
default: return(0);
}
}
//+---------------------------------------------------------------+
//| IMarketEnvironment: Generic Account Info Bridge |
//+---------------------------------------------------------------+
double AccountInfoDouble(const ENUM_ACCOUNT_INFO_DOUBLE property)
{
switch(property)
{
case ACCOUNT_BALANCE: return(m_mock_balance);
case ACCOUNT_EQUITY: return(m_mock_equity);
case ACCOUNT_MARGIN_FREE: return(m_mock_margin_free);
default: return(0.0);
}
}
long AccountInfoInteger(const ENUM_ACCOUNT_INFO_INTEGER property)
{
switch(property)
{
case ACCOUNT_LEVERAGE: return(m_mock_leverage);
default: return(0);
}
}
//+---------------------------------------------------------------+
//| IMarketEnvironment: Margin Calculation |
//+---------------------------------------------------------------+
bool OrderCalcMargin(const ENUM_ORDER_TYPE action,
const string symbol,
const double volume,
const double price,
double &margin)
{
// Deterministic synthetic margin estimation based on leverage
if(m_mock_leverage <= 0) return(false);
margin = (volume * 100000.0 * price) / (double)m_mock_leverage;
return(true);
}
//+---------------------------------------------------------------+
//| IMarketEnvironment: Delegations to CMockPositionStore |
//+---------------------------------------------------------------+
int PositionsTotal(void)
{
return(m_position_store.PositionsTotal());
}
string PositionGetSymbol(const int index)
{
return(m_position_store.PositionGetSymbol(index));
}
long PositionGetInteger(const ENUM_POSITION_PROPERTY_INTEGER property)
{
return(m_position_store.PositionGetInteger(property));
}
double PositionGetDouble(const ENUM_POSITION_PROPERTY_DOUBLE property)
{
return(m_position_store.PositionGetDouble(property));
}
int OrdersTotal(void)
{
return(m_position_store.OrdersTotal());
}
ulong OrderGetTicket(const int index)
{
return(m_position_store.OrderGetTicket(index));
}
long OrderGetInteger(const ulong ticket, const ENUM_ORDER_PROPERTY_INTEGER property)
{
return(m_position_store.OrderGetInteger(ticket, property));
}
string OrderGetString(const ulong ticket, const ENUM_ORDER_PROPERTY_STRING property)
{
return(m_position_store.OrderGetString(ticket, property));
}
bool HistorySelect(const datetime from_date, const datetime to_date)
{
return(m_position_store.HistorySelect(from_date, to_date));
}
int HistoryDealsTotal(void)
{
return(m_position_store.HistoryDealsTotal());
}
ulong HistoryDealGetTicket(const int index)
{
return(m_position_store.HistoryDealGetTicket(index));
}
long HistoryDealGetInteger(const ulong ticket, const ENUM_DEAL_PROPERTY_INTEGER property)
{
return(m_position_store.HistoryDealGetInteger(ticket, property));
}
double HistoryDealGetDouble(const ulong ticket, const ENUM_DEAL_PROPERTY_DOUBLE property)
{
return(m_position_store.HistoryDealGetDouble(ticket, property));
}
string HistoryDealGetString(const ulong ticket, const ENUM_DEAL_PROPERTY_STRING property)
{
return(m_position_store.HistoryDealGetString(ticket, property));
}
//+---------------------------------------------------------------+
//| Diagnostics |
//+---------------------------------------------------------------+
int GetTicksProcessed(void) const { return(m_ticks_processed); }
bool IsTickLoaded(void) const { return(m_tick_loaded); }
};
#endif
//+------------------------------------------------------------------+