44 lines
2.9 KiB
Markdown
44 lines
2.9 KiB
Markdown
# 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.
|