2026-08-14 00:09:54 +00:00 | | | # Survival
|
| | |
|
 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 | | | 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.
|
2026-08-14 00:09:54 +00:00 | | |
|
| | | Companion code for the MQL5 article: https://www.mql5.com/en/articles/23928
|
| | |
|
| | | ## What it does
|
| | |
|
 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 | | | 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%.
|
2026-08-14 00:09:54 +00:00 | | |
|
 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 | | | 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.
|
2026-08-14 00:09:54 +00:00 | | |
|
 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 | | | `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.
|
2026-08-14 00:09:54 +00:00 | | |
|
| | | The article covers the edge cases, which is where this kind of analysis usually
|
 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 | | | 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.
|
2026-08-14 00:09:54 +00:00 | | |
|
| | | ## Layout
|
| | |
|
| | | ```
|
 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 | | | 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
|
2026-08-14 00:09:54 +00:00 | | | ```
|
| | |
|
 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 | | | 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.
|
2026-08-14 00:09:54 +00:00 | | |
|
| | | ## Disclaimer
|
| | |
|
 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 | | | 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.
|