FilterAttribution/README.md
ayantrader ayantrader 75d005141a Add FilterAttribution sources and README
Attribution engine, the six-filter opening-range breakout test bed with
its record and enforce modes, and the report and dashboard script.
2026-10-27 19:39:53 +05:00

FilterAttribution

Shapley attribution of strategy filters in native MQL5: one unfiltered backtest that records every filter's verdict on every trade already contains the result of every filter combination, exactly, and the report shares the gain out among the filters without depending on the order they were added in.

Companion code for the MQL5 article: https://www.mql5.com/en/articles/25617

What it does

Filters are usually judged one at a time: add one, rerun the backtest, and credit the filter with the change. That credit depends on what was already in the stack when the filter went in. On the six-filter test bed here, the daily trend filter can be credited with anything from -14.7 R to +65.8 R depending on the order, so the one-at-a-time story is mostly a story about the order of the experiment. Settling it by brute force takes 2^n backtests, 64 for six filters and 4,096 for twelve.

None of them is needed when filters only veto trades. Run the strategy once with every filter evaluated and none enforced, and store each trade's verdicts as a bit mask in its entry comment, together with the money risked. The result of any combination is then a sum over the trades whose mask contains it. Three conditions make that sum exact rather than approximate: a vetoed signal must consume whatever the taken one would have (here, the day's first breakout), risk must be a fixed money amount rather than a share of equity, and positions must not overlap. The Expert Advisor's enforce mode exists to prove the conditions hold: optimising the filter mask over all 64 values runs 64 real backtests, and OnTesterDeinit compares each against the recorded table. All 64 matched in trade count and total R, with a largest difference of zero.

CFilterAttribution turns the coalition values into Moebius dividends, and everything else is read off that one decomposition: the Shapley value (each filter's average marginal gain over every order), the leave-one-out cost (what the full stack loses without it) and the pairwise interaction index (positive when two filters work together, negative when they duplicate each other). A moving-block bootstrap gives each number an interval, the sample is split at a date to see which credit survives, and a rolling Shapley value over a trailing window of trades shows when it was earned. An independent brute-force average over all 720 orderings agrees with the library to within 1e-13.

The most important number in the report is the chance level. When the base strategy loses, any veto earns credit simply by trading less, so the library computes exactly what a random veto at each filter's own pass rate would have earned, and the same for every pair's interaction. On the test bed that chance credit runs from 1.8 R to 16.3 R depending on the filter, and a third of the two trend filters' raw overlap, and half or more of two other pairs', was chance. Only the part above it is credit for choosing which trades to block.

The test bed is a London opening-range breakout on GBPUSD M5, 2023 to 2026.09, with six filters: daily and H4 trend, narrow range, midweek, strong close and active market. Unfiltered it loses 81.6 R over 955 trades; all six filters make 11.0 R on 42 trades. Read against chance, removal cost and the split, the attribution keeps only the H4 trend filter, which takes the strategy to 7.0 R on 461 trades, close to breakeven, and its margin over chance was the same before and after the split. The daily trend, midweek and active-market filters earn positive credit but cost almost nothing to remove, because other filters in the stack already do their work. Re-checking the dropped filters against the kept one finds a single candidate, the strong-close filter, which adds on both sides of the split and takes the pair to 21.4 R on 356 trades. That window has now been used to choose it, so only later data can confirm it. The method found which filter carries the edge there is; it did not create a larger one.

Layout

Include/FilterAttribution/FilterAttribution.mqh  verdict table, coalition values, Moebius dividends, Shapley value, leave-one-out and interaction with chance levels, block bootstrap, rolling credit
Experts/FilterAttribution/ORB_FilterLab.mq5      London opening-range breakout with six filters; records verdicts, or enforces a mask and verifies the table against every enforced backtest
Scripts/FilterAttribution/FilterAttribution.mq5  loads a saved table or the account's tagged history, prints the report with the split, selection and kept-stack checks, and draws the dashboard

Run ORB_FilterLab.mq5 once in the Strategy Tester in LAB_RECORD mode. When the test ends it rebuilds the table from its own deal history and saves it to the terminal's common data folder. Then, once, set InpMode to LAB_ENFORCE and optimise InpMask from 0 to 63 with the slow complete algorithm; the summary line printed to the Experts log must report zero mismatches before the table is trusted. Finally run FilterAttribution.mq5 on a chart to print the report and draw the dashboard. Set InpKeep to the mask of the filters you plan to keep to see what each dropped filter adds back to them.

To attribute your own filters, nothing in the library needs to change. In your Expert Advisor, evaluate every filter at entry, open the trade regardless, write the pass mask and the money risked into the order comment with CFilterAttribution::Tag, make sure a vetoed signal is spent exactly as a taken one would be, and size from a fixed money amount. Add an enforce mode and run the verification once before reading any attribution.

Disclaimer

Educational code. The opening-range breakout is a test bed for the attribution method, not a trading strategy: unfiltered it loses money, and the best verified result is close to breakeven, with one candidate filter that has not been tested on unseen data. The method applies only to filters that veto trades; filters that resize or reshape trades break the subset property, and the verification will report it. Past performance in the Strategy Tester does not guarantee future results. Test on your own data and broker conditions before drawing conclusions.