# StopFirst A header-only MQL5 library of the calculations an Expert Advisor keeps re-implementing: position sizing from a risk percentage, pip and price conversion, broker constraint checks, bar queries and candle patterns, trading sessions in GMT, total risk across open positions, broker symbol suffixes, an economic-calendar filter, and account metrics. No DLLs, no `WebRequest`, no external dependencies beyond the standard `Include\Trade\` classes that ship with MetaTrader 5. --- ## Install **From the CodeBase archive:** unpack it into the terminal's data folder (File > Open Data Folder). It adds `MQL5\Include\StopFirst\*.mqh` and the example `MQL5\Scripts\StopFirst\LotByRisk.mq5`, and touches nothing else. **From Algo Forge:** clone the repository, then copy its `Include\StopFirst\` folder into `MQL5\Include\`, so the headers sit at `MQL5\Include\StopFirst\*.mqh`. The example is in `Examples\`. Then: ```mql5 #include ``` Nothing to compile or link — it is header-only. ## Example ```mql5 #include void OnStart() { // Lot size whose 30-pip stop costs 1% of the balance double lots = CalcLotSizeByRisk(1.0, 30); if(lots <= 0) { Print("Risk budget does not cover one minimum lot."); return; } PrintFormat("%.2f lots risks %.2f %s over 30 pips", lots, CalcValueAtRisk(lots, 30), AccountInfoString(ACCOUNT_CURRENCY)); } ``` `LotByRisk.mq5` is the same idea with the output filled in. It places no orders. ## Two things to know before using it **Sizing fails to zero, never upward.** `CalcLotSizeByRisk` returns `0` when the risk budget will not cover one minimum lot, rather than rounding up to the minimum. Rounding up would silently risk more than you asked for. Check the return value. **Pip size is a forex convention.** The default rule — `point * 10` on a 3- or 5-digit symbol, `point` otherwise — is right for currency pairs and wrong anywhere a "pip" means something else. It is out by 10× on gold quoted to two decimals and 100× on most crypto. No symbol property distinguishes these cases, so declare it once at startup: ```mql5 SetPipSize("XAUUSD", 0.1); ``` or take the conventional value for metals, indices, oil and crypto from the built-in table with `ApplyBuiltInPipSize("XAUUSD")`. Everything downstream — lot sizing, value at risk, stop distances — reads from that one definition. ## In the Strategy Tester Everything works in the tester except two things the tester does not have: - **The broker's GMT offset cannot be measured** (`TimeGMT()` there is not model time). `DetectGmtOffset()` reports `reliable = false`; call `SetGmtOffset(hours)` in `OnInit`, or sessions and `IsWithinTradingHours` assume GMT+0. - **There is no economic calendar.** `IsCalendarAvailable()` returns false and `IsNewsNear()` returns false, so a news filter neither blocks nor allows on invented data. ## Files | Header | Contents | |---|---| | `StopFirst.mqh` | Umbrella. Include this. | | `Defs.mqh` | Constants, sentinels, `ENUM_SF_PATTERN`, `ENUM_SF_SIGNAL` | | `Symbol.mqh` | Pip size and overrides, price/pip conversion, lot normalisation, broker constraint checks, whether the market is open | | `PipTable.mqh` | `BuiltInPipSize`, `ApplyBuiltInPipSize` — opt-in conventional pips for metals, indices, oil, crypto | | `Risk.mqh` | `CalcLotSizeByRisk`, `CalcMaxLotSize`, `CalcValueAtRisk`, `CalcRiskRewardRatio`, pip value and margin per lot | | `Bars.mqh` | `CNewBarDetector`, bar extremes, candle patterns | | `Atr.mqh` | `CAtr` with a cached indicator handle, plus `GetATRPips` | | `Sessions.mqh` | Broker GMT offset, Sydney/Tokyo/London/New York sessions, server/GMT conversion | | `Time.mqh` | Timeframe and weekday names, trading-hour windows in GMT | | `Positions.mqh` | Read-only position queries | | `Account.mqh` | Floating loss, peak drawdown, compounding, summary string | | `AccountRisk.mqh` | Money at risk across open positions and pending orders, positions without a stop, risk budget check | | `Symbols.mqh` | Resolve `EURUSD` to the broker's `EURUSD.m`, `EURUSDpro` and similar | | `News.mqh` | Economic-calendar filter: news near, upcoming events, minutes to the next one | ## Conventions - `symbol = ""` means the current chart symbol — **except** on the position filter functions (`GetPositionCount`, `GetTotalProfit`, `GetTotalVolume`, and the totals in `AccountRisk.mqh`), where it means *every* symbol. Widening a filter and defaulting a subject are different operations; they happen to share a spelling. - `magic = STOPFIRST_ANY_MAGIC` matches any magic number. Magic is `ulong`, so there is no negative value to borrow as a sentinel. - Functions returning a price or a count return `0` on failure. Where `0` is also a plausible real answer, the doc comment on the function says so. - Every public function documents what it returns **and what it returns on failure**. ## Tests The library is exercised by read-only harnesses that can be attached to any chart, and by demo-only ones that place and close real positions, on a live demo account and in the Strategy Tester, on forex, gold, indices and stocks. The test programs are not part of this archive. Compiling proves the types line up. It proves nothing about a broker: tick values, minimum stop distances and close-time races all behave differently on different servers, and every defect found on 2026-09-24 compiled cleanly. ## Requirements MetaTrader 5, any recent build. Uses `Include\Trade\` (`CSymbolInfo`, `CAccountInfo`, `CPositionInfo`), which ships with the terminal. No DLL imports. No `WebRequest`. Nothing in this package sends a trade request — it reads and calculates. Order execution lives in a separate, unpublished part of the project. The one change it makes to the terminal is adding symbols to Market Watch, so that they have quotes: `ResolveSymbol` adds the symbol it finds (pass `false` to stop it), `EnsureSymbolSelected` exists to do exactly that, and the currency conversion behind pip value and margin adds the conversion pair it needs, such as `USDJPY` for a JPY-quoted symbol on a USD account. ## License MIT. See [LICENSE](LICENSE).