391 lines
15 KiB
MQL5
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
|
|
//+------------------------------------------------------------------+
|