Watch how to download trading robots for free
Find us on Facebook!
Join our fan page
Interesting script?
So post a link to it -
let others appraise it
You liked the script? Try it in the MetaTrader 5 terminal
Libraries

CSetupLedger: a per-setup win rate ledger for Expert Advisors - library for MetaTrader 5

Ali Akbar
Ali Akbar
  • MQL5 Developer & Algorithmic Trading Specialist at  Freelance
  • Pakistan
  • 653
Hi, I'm Ali. 24 years old, trading for around 5 years, and developing MQL5/MQL4 tools. I mainly work on automating manual strategies, fixing broken EA logic, and building clean indicators. I care more about clean architecture and proper risk management than over-complicated code. Always down to talk
Views:
32
Rating:
(1)
Published:
CSetupLedger.mqh (13.23 KB) view
MQL5 Freelance Need a robot or indicator based on this code? Order it on Freelance Go to Freelance

Why I wrote it

My scalper trades two setups. A bullish order block and the bearish mirror of it. The Strategy Tester gave me one profit factor for both of them. I spent two weeks tuning inputs that applied to each setup equally.

Then I split the trades by setup type. On USDCAD M1 the bullish side had caused 88 percent of the loss. The bearish side was close to flat. The number I needed had been sitting in my own trade history the whole time. I just had no way to read it.

This class reads it.

What it gives you

You register your setup types once at startup. Each one gets an id back. That id is added to your magic number base so every order carries its own setup tag from the moment it is sent.

After that the ledger answers three questions for each setup. How many wins and losses. What the net result was after swap and commission. And whether the win rate has earned any trust yet.

That last question is the one I built this for. Three wins from three trades is a 100 percent win rate. It is also what a coin that lands heads 44 percent of the time manages about once in twelve attempts. So the class prints a Wilson score lower bound beside the raw rate and checks it against the breakeven win rate your reward ratio demands. At 1:2 that bar sits at 33.3 percent. A setup only reads as trusted when even its pessimistic rate clears the bar. Early on everything reads "not yet trusted". That is the honest answer when you have twenty trades.

Using it

#include <CSetupLedger.mqh>

CSetupLedger ledger;
int SETUP_BULL, SETUP_BEAR;

int OnInit()
  {
   ledger.Init(552001);                          // your magic base
   SETUP_BULL = ledger.Register("Bullish OB");   // id 0 -> magic 552001
   SETUP_BEAR = ledger.Register("Bearish OB");   // id 1 -> magic 552002

   ledger.RebuildFromHistory();                  // read the record off the account
   Print(ledger.Report(2.0));                    // 2.0 = reward-to-risk ratio
   return INIT_SUCCEEDED;
  }

Stamp the setup onto the trade before the order goes out.

trade.SetExpertMagicNumber((ulong)ledger.MagicFor(SETUP_BULL));

Fold the position in once it closes.

double net = 0.0;
if(ledger.ProcessClosedPosition(posId, setupId, net))
   Print(ledger.Report(2.0));

Report()  returns one block of text. It works as a  Print()  and as a  Comment() . Here is a real run of mine on USDCAD M1.

Per-setup ledger  (breakeven win rate at 1:2.0 = 33.3%)
---------------------------------------------------------------------
Bullish OB         235/529  raw  30.8%  wilson  27.6%  net  -5819.94  not yet trusted
Bearish OB         191/393  raw  32.7%  wilson  29.0%  net   -811.42  not yet trusted

The same report passed to  Comment()  sits in the chart corner while the EA runs. The box on the bar is the entry label. It records what that setup's record was before the trade was taken. Here the panel reads 4 wins and 6 losses. Raw rate 40 percent. Wilson lower bound 16.8 percent. So on ten trades the verdict still reads not yet trusted, which is the whole point of the column.

The counts are wins over losses. So that is 764 trades on the bullish setup and 584 on the bearish one. Both nets add back to the tester's own net loss to the cent. Run that reconciliation on your own output before you trust any ledger including this one. If the per-setup trades do not sum to the tester's trade count then positions are being miscounted and every percentage above that line is worthless.

Three traps it handles for you

Hand written versions of this usually get these wrong. I got two of them wrong myself before this class existed.

Deals are not trades. MT5 history is a list of deals. A position closed in three parts produces three OUT deals. Loop over deals and count each profitable one as a win and a single winning trade becomes three wins. The ledger aggregates by  DEAL_POSITION_ID  so a partial close still counts once.

DEAL_PROFIT  is not the result. Swap and commission sit in their own fields. On a swing EA that gap is cosmetic. On a scalper it decides whether the setup made money at all. A trade that gained 3 USD of price movement and paid 4 USD in commission is a loss. The ledger sums profit plus swap plus commission.

POSITION_TICKET  is not  POSITION_IDENTIFIER .  HistorySelectByPosition()  wants the identifier. The two hold the same value on a netting account most of the time so the bug stays invisible until a hedging account or a partial close pulls them apart. Then the selection comes back empty and says nothing.

The one rule you must not break

Registration order is a data format. The setup id lives nowhere except inside the magic numbers of trades that have already closed. Insert a new setup in the middle of the list next month and every historical trade of the setup that used to hold that id silently becomes a trade of the new one. Nothing throws an error. The numbers are simply wrong from then on.

New setups go at the end of the list. If you retire a setup then leave its  Register()  call where it is so the id stays taken. A short comment above the registration block saying this earns its keep.

What it will not do

It keeps one record per position. An EA that stacks a second entry into the same netting position will credit the whole thing to the first setup. If yours does that it needs a per-deal model instead.

Sixteen setups is the ceiling. Raise  SETUP_LEDGER_MAX  if you need more. Sixteen setups on one account also means sixteen small samples and the Wilson column will tell you so.

A net result of exactly zero counts as a loss. Scratch trades are rare and treating them as wins is the more flattering error.

ProcessClosedPosition()  replaces the global history selection because it calls  HistorySelectByPosition() . Do not call it in the middle of your own history loop without reselecting afterwards.

And it measures your exits rather than your idea. A setup that reads direction correctly but places its stop badly scores as a bad setup. For deciding what to trade tomorrow that is arguably the right answer. It is not the same as judging whether the idea was sound.

Requirements

MetaTrader 5. No dependencies beyond the standard library. Drop the file in  MQL5/Include/  and include it. Written and tested on build 6190.

One last thing. A ledger built on a single in sample backtest measures that backtest. It does not validate a strategy. Out of sample testing on a period you never tuned on is a separate job and this class does not replace it.

— Ali Rajput


Position sizing from risk, using the broker's own tick value and volume step Position sizing from risk, using the broker's own tick value and volume step

Position size from a risk amount, computed from the symbol's real tick value, tick size and volume step rather than an assumed pip value. It always rounds volume down, and when your risk is smaller than the minimum lot it says so and reports what that lot actually costs. No terminal state, so the sizing logic is testable offline - a 41-assertion test script is included.

Heiken Ashi ATR Blend Heiken Ashi ATR Blend

Heiken Ashi ATR Blend combines smoothed Heiken Ashi candles, ATR-based volatility filtering, and fast/slow EMA trend confirmation to identify potential trend-change signals.

RegimeRouter — trend/range classifier with a per-regime win rate ledger RegimeRouter — trend/range classifier with a per-regime win rate ledger

Classifies the market with ADX, the Hurst exponent and lag-1 autocorrelation, routes each bar to a breakout module or a mean-reversion module, and keeps a separate win rate ledger for each regime with a Wilson 95% lower bound, so you can see which half of it is actually working.

MACD Signals MACD Signals

Indicator edition for new platform.