149 lines
6.1 KiB
Markdown
149 lines
6.1 KiB
Markdown
# 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 <StopFirst\StopFirst.mqh>
| |||
```
| |||
| |||
Nothing to compile or link — it is header-only.
| |||
| |||
## Example
| |||
| |||
```mql5
| |||
#include <StopFirst\StopFirst.mqh>
| |||
| |||
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).
|