forked from ayantrader/Survival
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.
96 lines
5.4 KiB
Markdown
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.
|