StopFirst-Core/README.md
RUSLAN SLOBODIANIUK ccaa4ad52b StopFirst 1.00, the read-only core
Fourteen headers, a header-only MQL5 library: lot size from risk, pips on
forex, gold and indices, broker checks, bars and candle patterns, cached ATR,
sessions in GMT, account risk, symbol suffixes, a calendar filter. Sends no
trade requests. Plus the LotByRisk example script.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 21:40:34 +03:00

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).