38 lines
2.4 KiB
Markdown
38 lines
2.4 KiB
Markdown
|
|
# ADR-001: Error Codes Over Exceptions
|
||
|
|
|
||
|
|
## Context
|
||
|
|
|
||
|
|
Clean Code (Robert C. Martin) mandates: *"Use exceptions, never return codes."* This principle assumes a language with `try`/`catch`/`throw` semantics (Java, C++, Python). MQL5 has **none of these constructs**. The language provides no mechanism to throw, propagate, or catch exceptions.
|
||
|
|
|
||
|
|
The native MQL5 API itself is built entirely on error codes:
|
||
|
|
- `OrderSend()` returns `bool` and fills `MqlTradeResult.retcode`
|
||
|
|
- `GetLastError()` returns the last runtime error code
|
||
|
|
- Every trade operation follows the pattern: `bool success` + `int error_code`
|
||
|
|
|
||
|
|
## Decision
|
||
|
|
|
||
|
|
Use the **bool return + output parameter** pattern for all fallible operations in the framework:
|
||
|
|
|
||
|
|
```mql5
|
||
|
|
bool TryExecuteOrder(MqlTradeRequest &request, MqlTradeResult &result);
|
||
|
|
```
|
||
|
|
|
||
|
|
This mirrors the native `OrderSend()` contract exactly. Additionally:
|
||
|
|
|
||
|
|
1. **Named constants** (`SYNTH_ERR_OFF_QUOTES`, `SYNTH_ERR_REQUOTE`, etc.) replace raw integer literals to make error-handling paths self-documenting.
|
||
|
|
2. **A guard function** (`IsExecutionError()`) centralizes the "what counts as failure?" logic, preventing scattered `!= 0` comparisons.
|
||
|
|
3. **The method name uses `Try` prefix** (`TryExecuteOrder`, not `ExecuteOrder`) to signal at the call site that failure is a normal, expected outcome — not an exceptional case.
|
||
|
|
|
||
|
|
## Consequences
|
||
|
|
|
||
|
|
### Positive
|
||
|
|
- **Zero friction for MQL5 developers**: the pattern matches what they already use daily with `OrderSend()`.
|
||
|
|
- **Named constants** are grep-searchable and self-documenting, capturing the *spirit* of Clean Code's error handling guidelines.
|
||
|
|
- **Guard function** prevents the bug class where a developer compares against the wrong integer value.
|
||
|
|
|
||
|
|
### Negative
|
||
|
|
- **Callers must always check the return value**. Unlike exceptions, nothing forces the caller to handle the error. This is mitigated by the `Try` prefix convention (signals fallibility) and by the test suite (which validates both success and failure paths).
|
||
|
|
- **CQS violation**: `TryExecuteOrder()` both performs an action AND returns a result. This is unavoidable without exceptions. Splitting into separate `ExecuteOrder()` + `GetLastExecutionError()` would create a fragile two-step protocol where the caller can forget step 2.
|
||
|
|
|
||
|
|
### Risk
|
||
|
|
- If a future version of MQL5 adds exception support, this pattern should be reevaluated. The interface `IMarketEnvironment` would need a new version.
|