Bracket Order State Machine
A bracket - an entry with an attached stop and target - is usually managed as three independent pieces of state scattered across an EA's OnTick: "is there a pending order", "did it fill", "did it hit stop or target". Each of those checks tends to get written slightly differently in every EA that needs one, and the gaps between them are exactly where a pending order gets left resting after the setup is no longer valid, or a filled position stops being watched after a code change that only updated one of the three checks. This library collapses that into one object with one explicit state: BRACKET_IDLE, PENDING_ENTRY, OPEN, CLOSED_TARGET, CLOSED_STOP, CLOSED_MANUAL, CANCELLED, or ERROR. Open() sends the entry (market or pending) and attaches the stop/target as a separate OrderModify, so a stop/target rejected for sitting inside MODE_STOPLEVEL does not also kill the entry - the position or pending order still exists and can be retried with a wider stop instead of the whole attempt being lost. Update(), called once per tick, is the only place a state transition happens after that: this library drives state entirely by polling rather than reacting to a fill event, so behaviour stays identical between MQL4 (which has no fill event at all) and MQL5 (which does have OnTradeTransaction, but a second event-driven code path would only create two ways for the tracked state to disagree). Sizing a bracket by risk needs money-per-point, and BracketCalcLotsForRisk derives it from the symbol's own tick value and tick size rather than a hardcoded contract size. A hardcoded 100000 is simply wrong for metals, indices, and most non-forex symbols - the same risk-money input would silently produce a wildly wrong lot size the moment this ran on XAUUSD instead of EURUSD. The computed lots is also normalised to the broker's volume step/min/max with an epsilon guard, because flooring a lot-step division without one can silently halve a position (0.1 / 0.01 does not land on an exact 10 in binary floating point). Notes and limitations - Fill classification (PENDING_ENTRY -> OPEN, and CLOSED_TARGET vs CLOSED_STOP vs CLOSED_MANUAL) only updates inside Update() - call it every tick (or at least every few seconds via OnTimer). A bracket that is never updated stays reporting its last-known state forever. - CLOSED_TARGET/CLOSED_STOP classification compares the close price to the stored stop/target within a small tolerance (10 points), because slippage can close a position a few points past the exact level. A position closed manually near either level can occasionally be misclassified as having hit that level - this is a best-effort read of history, not a guarantee. - One CBracketOrder instance tracks exactly one ticket (MQL4) or one pending/position ticket pair (MQL5). Managing several brackets at once needs one instance per setup (an array of CBracketOrder, or one per setup you keep in your own EA state). - BracketCalcLotsForRisk returns 0.0 on unusable broker data (zero tick value/size) or a zero/negative risk or stop distance - callers must check for a positive result before calling Open() with it. - This library does not manage trailing stops, breakeven moves or partial closes after the bracket is open - it only tracks entry-to-exit state. - The MQL5 version's history lookups (FindPositionFromHistoryOrder, ClassifyClosedPosition) depend on HistorySelect/HistorySelectByPosition having synchronised the relevant range - both are called internally by Update(), but a terminal with a very large or freshly-connected history can occasionally return stale results on the very first call after a fill.
Script · MT5 · v1.0 · Free
About Bracket Order State Machine
Bracket Order State Machine is an expert advisor (trading robot) for MetaTrader 5. It places pending buy/sell stop or limit orders. It has 3 settings, such as Place real order, Risk money and Stop pips. MQL source code included.
Files in the download: BracketOrderStateMachine.mq5.mqh, BracketOrderStateMachine.txt, BracketOrderStateMachineDemo.ex5, BracketOrderStateMachineDemo.mq5.
Trading robots can lose money. Check every setting and test on a demo account before using real funds.
Settings
| Setting | Default | What it does |
|---|---|---|
| Place real order | false | WARNING: true sends a real minimum-risk market order on this symbol |
| Risk money | 10.0 | Account-currency amount at risk for the lot-sizing demo |
| Stop pips | 50.0 | Stop distance in pips used for both demos |
How to install
- Download the ZIP from your PolyMaria library and unzip it.
- In MetaTrader 5 open File → Open Data Folder, then go to MQL5 → Experts.
- Copy the .mq5 file(s) there. Put any .mqh files in MQL5 → Include.
- Open the .mq5 file in MetaEditor and press Compile (F7) if MetaTrader does not show it.
- Drag the expert advisor from the Navigator onto a chart, check the settings, allow Algo/Auto Trading and press OK.
- Test it first on a demo account or in the Strategy Tester.
Tags: Pending Orders · MT5 Script · MQL5 Source Code