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.
|