129 lines
5.6 KiB
Markdown
129 lines
5.6 KiB
Markdown
# riskgate-service-realcase
|
|
|
|
Gate risk manager service — real scenario use case.
|
|
|
|
Implements `RiskGateHandler` from the [`@douglasrechia/riskgate`](https://forge.mql5.io/DouglasRechia/riskgate) package as a MetaTrader 5 service that listens on a socket and approves or rejects incoming trade signals based on a set of risk rules.
|
|
|
|
---
|
|
|
|
## Prerequisites
|
|
|
|
To build and use this project, you will need:
|
|
|
|
1. [**MetaTrader 5**](https://www.mql5.com/) installed.
|
|
2. **KnitPkg CLI**: The KnitPkg package manager for MetaTrader. If you don't have it, you can install it by following the instructions in the [main KnitPkg repository](https://github.com/knitpkg-dev/knitpkg-mt.git).
|
|
3. **KnitPkg homepage**: See [https://knitpkg.dev](https://knitpkg.dev) for an overview and [https://docs.knitpkg.dev](https://docs.knitpkg.dev) for documentation.
|
|
|
|
## Installation
|
|
|
|
Download and build the latest stable version in a single command via the [KnitPkg](https://knitpkg.dev) registry. If MetaTrader is installed in more than one location, first change to your MetaTrader Data Folder root:
|
|
|
|
```bash
|
|
cd "C:\Users\<username>\AppData\Roaming\MetaQuotes\Terminal\<TERMINAL_ID>"
|
|
kp get mql5 @douglasrechia/riskgate-service-realcase
|
|
```
|
|
|
|
If needed, restart MetaTrader afterward to refresh the Navigator. Double-click to start the service at Services/riskgate-service-realcase/bin/RiskgateServiceRealCase.
|
|
|
|
---
|
|
|
|
## Risk Rules
|
|
|
|
| Rule | Detail |
|
|
|---|---|
|
|
| Signal validation | Signal must contain `symbol`, `sl_points`, and `magic`; `symbol` must be non-empty and `sl_points` must be > 0 |
|
|
| Lot sizing | 0.5% of account equity (core equity model — closed trades), sized from SL in points |
|
|
| Daily loss limit | Daily P&L < −2% of equity → all trades banned until midnight |
|
|
| Daily trade limit | Max 6 trades per day (open + closed combined) |
|
|
| Per-symbol limit | Max 2 open positions per symbol |
|
|
| Correlated exposure | 50% lot reduction when another open position shares the same magic number on a different symbol |
|
|
|
|
---
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
src/
|
|
RiskgateServiceRealCase.mq5 # Service entry point — starts the RiskGateServer on port 5555
|
|
RiskgateHandler.mqh # MyHandler implementation with all risk rules
|
|
RiskgateHandlerDep.mqh # KnitPkg entrypoint — declares external dependency includes
|
|
AccountInfo.mqh # IAccountInfo interface + Real/Mock implementations
|
|
TradeHistory.mqh # ITradeHistory interface + Real/Mock implementations
|
|
OpenPositions.mqh # IOpenPositions interface + Real/Mock implementations
|
|
SymbolInfo.mqh # ISymbolInfo interface + Real/Mock implementations
|
|
tests/
|
|
UnitTests.mq5 # Unit tests for MyHandler
|
|
```
|
|
|
|
---
|
|
|
|
## How It Works
|
|
|
|
The service starts a `RiskGateServer` on port `5555`. When a signal arrives, `MyHandler.OnSignal()` runs the following pipeline:
|
|
|
|
1. **Signal validation** — rejects if required keys are missing or values are invalid (`invalid_signal`)
|
|
2. **Daily stats** — computes today's closed P&L and closed position count from trade history
|
|
3. **Daily trade limit** — rejects if open + closed positions ≥ 6 (`daily_max_positions_limit`)
|
|
4. **Daily loss limit** — rejects if today's P&L < −2% of balance (`daily_loss_limit`)
|
|
5. **Per-symbol limit** — rejects if ≥ 2 open positions already exist on the same symbol (`max_positions_reached`)
|
|
6. **Lot calculation** — sizes lot to 0.5% risk from SL in points; halves it if correlated exposure is detected
|
|
7. **Approval** — returns `approved = true` with the calculated lot
|
|
|
|
### Signal format (JSON)
|
|
|
|
```json
|
|
{
|
|
"symbol": "EURUSD",
|
|
"sl_points": 50,
|
|
"magic": 1
|
|
}
|
|
```
|
|
|
|
### Response format (JSON)
|
|
|
|
```json
|
|
{
|
|
"approved": true,
|
|
"lot": 0.05,
|
|
"reason": ""
|
|
}
|
|
```
|
|
|
|
Possible `reason` values when `approved = false`:
|
|
|
|
- `invalid_signal`
|
|
- `daily_max_positions_limit`
|
|
- `daily_loss_limit`
|
|
- `max_positions_reached`
|
|
- `lot_too_small`
|
|
|
|
---
|
|
|
|
## Tests
|
|
|
|
Unit tests are in `tests/UnitTests.mq5` and cover `MyHandler` in isolation using mock implementations of all external dependencies (`MockTradeHistory`, `MockAccountInfo`, `MockOpenPositions`, `RealSymbolInfo`). The command `kp get` above already compiles the tests as a MetaTrader script under Services/riskgate-service-realcase/bin/UnitTests, just double-click on it.
|
|
|
|
| Test | Description |
|
|
|---|---|
|
|
| `Test_ApprovedTrade_LotRisksHalfPercentOfBalance` | Approves trade and sizes lot to exactly 0.5% risk with no history or open positions |
|
|
| `Test_ApprovedTrade_With5OpenPositions` | Approves trade when 5 positions are open on other symbols |
|
|
| `Test_RejectedTrade_With6OpenPositions` | Rejects when 6 positions are already open (`daily_max_positions_limit`) |
|
|
| `Test_RejectedTrade_With2OpenPositionsOnSameSymbol` | Rejects when 2 positions are open on the same symbol (`max_positions_reached`) |
|
|
| `Test_ApprovedTrade_LotHalvedWithCorrelatedExposure` | Approves and halves lot when a correlated position (same magic, different symbol) exists |
|
|
| `Test_ApprovedTrade_DailyLossUnder2Percent` | Approves when daily loss is below the 2% threshold |
|
|
| `Test_RejectedTrade_DailyLossOver2Percent` | Rejects when daily loss exceeds the 2% threshold (`daily_loss_limit`) |
|
|
| `Test_RejectedTrade_InvalidSignal_MissingSymbol` | Rejects signal missing the `symbol` key (`invalid_signal`) |
|
|
| `Test_RejectedTrade_InvalidSignal_SlPointsZero` | Rejects signal with `sl_points = 0` (`invalid_signal`) |
|
|
|
|
---
|
|
|
|
## License
|
|
|
|
This project is released under the **MIT License**. See `LICENSE` for details.
|
|
|
|
---
|
|
|
|
## Disclaimer
|
|
|
|
This code is provided **as-is**, for **educational purposes only**.
|
|
No warranty (express or implied) is provided. Use at your own risk.
|