MQL5-SyntheticTestHarness/Documentation/decisions/ADR-003-behavior-injection-via-function-pointers.md

2,9 КиБ

ADR-003: Behavior Injection via Function Pointers for Dynamic Mocking

Status: Accepted

Context

In high-reliability institutional algorithmic trading engines, execution modules must be validated against unexpected terminal and broker behaviors (Chaos Engineering):

  • Successive requotes during low liquidity
  • Order rejections and price slippages
  • Gateway/network disconnects (TRADE_RETCODE_CONNECTION)
  • Custom Depth of Market (DOM) snapshots

In languages like C++ or C#, mocking frameworks (e.g., GoogleMock, Moq) use dynamic proxies, closures/lambdas, or template metaprogramming to configure mock responses on the fly.

MQL5 has strict language constraints:

  1. No native lambda functions or true closure captures.
  2. No reflection or runtime type introspection.
  3. No compile-time mock generation libraries.

Previous iterations of the test harness relied solely on static tick arrays (MockTick.injected_error), which required constructing rigid multi-tick datasets to test dynamic scenarios like retry loops.

Decision

Use C-style function pointers (typedef) to implement Behavior Injection directly into CSyntheticEngine:

  1. Define delegate signatures in BehaviorDelegates.mqh mirroring the IMarketEnvironment interface methods.
  2. Provide setter methods in CSyntheticEngine (e.g., MockOrderSend(), MockMarketBookGet()) to inject delegate function pointers at runtime.
  3. Use a Dual-Mode Priority Resolution:
    • Behavior Injection Hook (Highest Priority): If a function pointer is non-NULL, execute it immediately.
    • Tick-Driven Simulation (Fallback): If no delegate is assigned, fall back to sequential MockTick replay.
    • Default Safe State: If no ticks are loaded and no behavior is set, return safe defaults.
  4. For stateful behaviors across successive calls (such as counting requote retries), use local static variables inside standalone behavior functions (BehaviorLibrary.mqh).

Consequences

Positive

  • Dynamic Chaos Engineering: Behaviors can be hot-swapped mid-flight in unit tests without recreating tick arrays or restarting the test environment.
  • Zero Overhead in Production: CLiveEnvironment continues to delegate directly to global MT5 functions via the scope resolution operator (::), unaffected by function pointer indirection.
  • Clean Architecture & Decoupling: Separation of behavior definitions (BehaviorDelegates.mqh), behavior implementations (BehaviorLibrary.mqh), and the mock engine (CSyntheticEngine.mqh).
  • Idiomatic MQL5: Full compatibility with strict MQL5 compiler without requiring external tooling.

Negative

  • Manual State Reset: Stateful behavior functions relying on internal static variables require careful sequencing or automatic reset after completion cycles.
  • Function Signature Constraints: Injected functions must strictly adhere to the typedef delegate signature.