Survival analysis applied to MetaTrader 5 deal history: Kaplan-Meier and hazard curves that answer how long a trade lasts and when it is most likely to die, not just how often it wins.
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
ayantrader 8e5f45bb2a Add standard errors, and correct two explanations flagged in review
CSurvival gains SurvivalSE (Greenwood), IncidenceSE and
ConditionalIncidenceSE (Aalen), and a free SurvProbCI that puts a
log-log interval around a probability so it cannot leave [0,1] on a
thin risk set. One IncidenceVar(from,to,cause) worker serves both the
unconditional and the conditional figures, since conditioning on
survival past a bar only re-bases the same increments.

TradeHistory: the comment on the unbounded HistorySelect gave the
wrong reason. A position whose only close falls after the cutoff is
censored correctly under a narrowed range; the case that actually
breaks is a position partially closed before the cutoff and fully
closed after, whose partial exit would be read as its final one.

SurvivalPanel: the refresh gate watched PositionsTotal() alone, which
misses every change that leaves the count intact - a partial close, a
netting reversal, a close and an open inside one bar, or a swap of
which position is described. LiveKey() folds the count together with
the tracked position's ticket, volume and open time.

SurvivalCore: ConditionalIncidence's header now states the
conditioning convention exactly rather than describing it as the
trades that reached the age.

SurvivalDemo: the iCustom note now states the input group behaviour
positively instead of contrasting it with the documentation.

README rewritten to match what the project became: competing risks
rather than cumulative hazard, the standard errors, and an honest
disclaimer about the demonstration Expert's result.
2026-09-24 00:29:26 +05:00
Experts/Survival Add standard errors, and correct two explanations flagged in review 2026-09-24 00:29:26 +05:00
Include/Survival Add standard errors, and correct two explanations flagged in review 2026-09-24 00:29:26 +05:00
Indicators/Survival Add standard errors, and correct two explanations flagged in review 2026-09-24 00:29:26 +05:00
README.md Add standard errors, and correct two explanations flagged in review 2026-09-24 00:29:26 +05:00

Survival

Survival analysis over your own MetaTrader 5 deal history. Answers what a position that has already survived N bars is likely to do next, which a win rate cannot tell you.

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

What it does

A win rate describes every trade a strategy ever opened. The question you actually have while holding a position is conditional: given that this trade has survived ten bars without resolving, what happens over the next ten? The survivors are a different population from the entrants, and only one of those two numbers is about the position in front of you. On the run in the article they differ by roughly a factor of three, 6.28% against 18.18%.

The part most attempts get wrong is what counts as censored. It is tempting to treat "reached target" as the event and everything else as censoring, but censoring means the outcome is unobserved, not impossible. A stopped-out trade is not unobserved; the target can never be reached because there is nothing left to watch. Wins and losses are competing risks, two terminal events racing for the same position, and only a position still open at the moment of measurement is censored. Call a loss "censored" and the estimator hands each dead trade the future of the survivors, so the estimate inflates. The direction is guaranteed by construction, never downward.

CSurvival therefore computes both. The Aalen-Johansen cumulative incidence is the correct answer; a Kaplan-Meier curve on wins alone, losses called censored, is the wrong one. On the article's run, at the last bar whose risk set could support a figure, those two read 17.82% and 50.98%. All four curves plus the hazard and the risk sets come out of one sweep of a bucket array, so nothing is sorted and every lookup is an array index.

The estimator also supplies its own error bars. SurvivalSE is Greenwood's formula for the survival curve; IncidenceSE and ConditionalIncidenceSE are Aalen's variance for a cumulative incidence under competing risks, which one routine serves for both the unconditional and the conditional figures because conditioning on survival past a bar only re-bases the same increments. SurvProbCI puts a log-log interval around a probability so it cannot run outside zero and one on a thin risk set. Those numbers are worth having: on the article's run the naive 50.98% sits about thirteen standard errors above the correct estimate, so that gap is emphatically not sampling noise, while the apparent decay in the aging profile past the median is well inside the noise and should not be read as an established shape.

Two smaller decisions matter more than they look. Durations are measured in bars rather than clock time, because a position carried over a weekend accrues hours in which price could not move, and charging the trade for them smears every curve. And positions are reconstructed from deals on DEAL_POSITION_ID rather than the order ticket, because one position can be built and dismantled by any number of orders. The outcome is taken from the sign of net money, profit plus swap plus commission, so the two competing events are strictly "closed in profit" and "closed at a loss" rather than "take-profit fired" and "stop-loss fired". Those coincide for the demonstration Expert and may not for yours.

The article covers the edge cases, which is where this kind of analysis usually goes wrong: partial closes straddling the cutoff, netting reversals, thin samples in the right-hand tail, and lookback windows that quietly drop the longest-lived trades.

Layout

Include/Survival/SurvivalTypes.mqh     three-outcome enum, observation struct, bar-count duration
Include/Survival/TradeHistory.mqh      deals joined into positions, censored at a cutoff
Include/Survival/SurvivalCore.mqh      Kaplan-Meier, Aalen-Johansen, hazard, naive curve, standard errors
Indicators/Survival/SurvivalPanel.mq5  the chart panel
Experts/Survival/SurvivalDemo.mq5      Donchian breakout that generates a history to read

Start with SurvivalDemo.mq5 in the Strategy Tester with visual mode enabled, and pause it once some trades have accumulated. The panel is embedded in the Expert as a resource, so it follows the Expert into the tester agent's sandbox. To put the panel on your own program, add the #resource line, the iCustom call in OnInit with a "" placeholder for every input group, and an IndicatorRelease in OnDeinit. The panel reads your account's own history, so it shows nothing until deals are present.

Disclaimer

Educational code. The Donchian breakout Expert exists only to generate a trade history for the panel to analyse. It is untuned and it loses money: over EURUSD H4 from January 2021, a 10,000 USD account finishes around 7,545. That is not a defect in the demonstration, since a wide target against a tight stop is precisely the shape that makes these curves worth looking at, but it is not a strategy and should not be run on a live account.

The panel is descriptive. It reports probabilities estimated from history, it never issues a close instruction, and it conditions on the age of a position and on nothing else. The distance left to the stop or target, the unrealised result, the volatility since entry and the market regime are all outside the model by construction, and they are most of what a decision to hold or close would actually need. Past behaviour of any model or dataset says nothing about future results.