# riskgate-service-realcase Gate risk manager service — real scenario use case. Implements `RiskGateHandler` from the [`@douglasrechia/riskgate`](https://knitpkg.dev) 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. --- ## 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` --- ## Dependencies Managed by [KnitPkg](https://knitpkg.dev). | Package | Version | |---|---| | `@douglasrechia/riskgate` | `^1.0.0` | | `@douglasrechia/logger` | `^1.0.0` (transitive) | | `@douglasrechia/sockets` | `^1.0.0` (transitive) | | `@knitpkg-labs/jason` | `^1.0.0` (transitive) | Install dependencies: ```bash kp install ``` --- ## Build Compile the service and unit tests: ```bash kp compile ``` Output binaries are placed in `bin/`. --- ## Tests Unit tests are in `tests/UnitTests.mq5` and cover `MyHandler` in isolation using mock implementations of all external dependencies (`MockTradeHistory`, `MockAccountInfo`, `MockOpenPositions`, `RealSymbolInfo`). | 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.