Survival/README.md
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

96 lines
5.4 KiB
Markdown

# 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.