5,6 KiB
riskgate-service-realcase
Gate risk manager service — real scenario use case.
Implements RiskGateHandler from the @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:
- MetaTrader 5 installed.
- 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.
- KnitPkg homepage: See https://knitpkg.dev for an overview and https://docs.knitpkg.dev for documentation.
Installation
Download and build the latest stable version in a single command via the KnitPkg registry. If MetaTrader is installed in more than one location, first change to your MetaTrader Data Folder root:
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:
- Signal validation — rejects if required keys are missing or values are invalid (
invalid_signal) - Daily stats — computes today's closed P&L and closed position count from trade history
- Daily trade limit — rejects if open + closed positions ≥ 6 (
daily_max_positions_limit) - Daily loss limit — rejects if today's P&L < −2% of balance (
daily_loss_limit) - Per-symbol limit — rejects if ≥ 2 open positions already exist on the same symbol (
max_positions_reached) - Lot calculation — sizes lot to 0.5% risk from SL in points; halves it if correlated exposure is detected
- Approval — returns
approved = truewith the calculated lot
Signal format (JSON)
{
"symbol": "EURUSD",
"sl_points": 50,
"magic": 1
}
Response format (JSON)
{
"approved": true,
"lot": 0.05,
"reason": ""
}
Possible reason values when approved = false:
invalid_signaldaily_max_positions_limitdaily_loss_limitmax_positions_reachedlot_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.