2026-07-13 17:15:01 -05:00
# Trading risk analytics
2026-06-12 22:17:00 -05:00
2026-08-13 13:05:04 -05:00
**Audience:** User
2026-07-13 17:15:01 -05:00
**Read-only** tools for sizing, tail risk, and stress tests. They inspect the account and open positions — they **never** place, modify, or close orders. Pair them with [TRADING_SAFETY.md ](TRADING_SAFETY.md ) when you move from analysis to execution.
2026-06-12 22:17:00 -05:00
| Tool | Answers |
|------|---------|
2026-07-13 17:15:01 -05:00
| `trade_risk_analyze` | “How big can this trade be?” and “How much am I risking right now?” |
| `trade_var_cvar_calculate` | “What is my statistical worst-case loss over one bar?” |
| `trade_stress_test` | “What happens to open positions under these price shocks?” |
2026-06-12 22:17:00 -05:00
2026-07-13 17:15:01 -05:00
**Dense terms:** [Fixed-fraction sizing ](GLOSSARY.md#fixed-fraction-sizing ) · [Kelly ](GLOSSARY.md#kelly-criterion ) · [VaR ](GLOSSARY.md#var-value-at-risk ) · [CVaR / ES ](GLOSSARY.md#cvar-conditional-var--expected-shortfall ) · [Drawdown ](GLOSSARY.md#drawdown )
**Related:** [Sample trade ](SAMPLE-TRADE.md ) · [Advanced playbook ](SAMPLE-TRADE-ADVANCED.md ) · [Guardrails ](ENV_VARS.md#trade-guardrails ) · [Barriers ](BARRIER_FUNCTIONS.md )
2026-06-12 22:17:00 -05:00
---
## `trade_risk_analyze`
Analyzes current open/pending risk and, when given a proposed entry and stop, sizes a
new trade. Supports two sizing methods: fixed-fraction and Kelly.
```bash
# Current portfolio risk only
mtdata-cli trade_risk_analyze --json
# Size a new long: risk-based volume from entry + stop
2026-08-24 20:28:50 -05:00
mtdata-cli trade_risk_analyze EURUSD --direction long --entry 1.0850 --stop-loss 1.0800 --sizing '{"method":"fixed_fraction","risk_pct":1.0}' --json
2026-06-12 22:17:00 -05:00
```
| Parameter | Default | Description |
|-----------|---------|-------------|
| `symbol` | — | Symbol for new-trade sizing; omit for portfolio-only risk. |
2026-08-12 18:34:05 -05:00
| `sizing` | — | JSON object selecting `fixed_fraction` with `risk_pct` , or `kelly` with its edge inputs and cap. Risk percentages use percentage points (`1` means 1%) and must be in `(0, 100]` . |
2026-08-12 11:58:51 -05:00
| `strict_risk` | `true` | Return `suggested_volume=0.0` if the broker minimum lot would exceed the requested sizing risk. |
2026-06-12 22:17:00 -05:00
| `include_pending` | `true` | Include contingent stop-loss risk from pending orders in portfolio totals. |
| `direction` | — | `long` /`short` (aliases accepted) for the proposed trade. |
2026-08-18 13:09:21 -05:00
| `entry` | — | Proposed entry price. With `symbol` +`stop_loss` but no entry, it is resolved from the latest tick (ask for long, bid for short, mid otherwise). A locked, stale, closed, or otherwise non-live quote is retained only for geometry context: the candidate returns `quote_not_live_ready` , no `suggested_volume` , and must be refreshed before sizing. Provide an explicit entry only for research-only geometry. |
2026-06-12 22:17:00 -05:00
| `stop_loss` | — | Proposed stop (alias `sl` ). Required to compute risk-based volume. |
| `take_profit` | — | Optional target (alias `tp` ) for reward/risk context. |
2026-08-12 11:58:51 -05:00
New-trade sizing uses an incremental candidate-risk policy: `sizing.risk_pct`
2026-07-16 18:28:36 -05:00
limits the proposed trade's stop risk as a percentage of account equity, and
account-wide margin stress remains a hard safety gate. Existing positions on other
symbols do not prevent sizing, but the returned `sizing_risk_policy` states that the
suggestion is not an aggregate portfolio stop-risk cap.
2026-08-24 20:21:02 -05:00
Candidate validation splits geometry from sizing eligibility.
`geometry_valid` is true when direction, stop, and target are internally
consistent. `sizing_eligible` is true only when a proposed volume can be
executed under account safety and mtdata volume guardrails.
`candidate_valid: true` means both the geometry is valid and, when sizing was
requested, the candidate is sizeable. If direction, stop, or target is invalid,
or sizing was requested and blocked (critical margin, guardrail volume, or a
non-live quote), the response retains the account and portfolio snapshot but
returns `success: false` , `candidate_valid: false` , `candidate_status: invalid`
or `blocked` , a structured `error_code` or `position_sizing_error` , and
`portfolio_snapshot_status: available` ; CLI callers receive a nonzero exit
status. Suggested volume is clamped to the same symbol volume guardrails that
`trade_place` enforces; when no compliant size exists,
`recommendation_status` is `blocked` and the binding rule is included.
2026-08-06 18:13:27 -05:00
2026-06-12 22:17:00 -05:00
### Kelly sizing
2026-08-12 11:58:51 -05:00
Set `sizing.method=kelly` and supply edge statistics in the same JSON object:
2026-06-12 22:17:00 -05:00
```bash
2026-08-24 20:28:50 -05:00
mtdata-cli trade_risk_analyze EURUSD --direction long --entry 1.0850 --stop-loss 1.0800 --sizing '{"method":"kelly","win_rate":0.55,"avg_win":0.012,"avg_loss":0.010,"fraction_multiplier":0.5,"max_risk_pct":2.0}' --json
2026-06-12 22:17:00 -05:00
```
| Parameter | Default | Description |
|-----------|---------|-------------|
2026-08-12 11:58:51 -05:00
| `sizing.win_rate` | — | Win probability as a fraction in `[0, 1]` . |
| `sizing.avg_win` | — | Average winning return normalized to a consistent stake or unit of risk (for example, an R-multiple). |
| `sizing.avg_loss` | — | Average losing return magnitude on the same normalized basis. |
| `sizing.fraction_multiplier` | `0.5` | Multiplier on the raw Kelly fraction (half-Kelly = `0.5` ). |
2026-08-12 18:34:05 -05:00
| `sizing.max_risk_pct` | `2.0` | Hard cap on account risk in percentage points (`1` means 1%); must be in `(0, 100]` . |
2026-06-12 22:17:00 -05:00
The Kelly fraction is `win_rate − (1 − win_rate) / (avg_win / |avg_loss|)` , then scaled
2026-08-12 11:58:51 -05:00
by `sizing.fraction_multiplier` and capped by `sizing.max_risk_pct` . On a non-positive edge the tool reports
2026-06-12 22:17:00 -05:00
`status="kelly_no_edge"` and a suggested volume of `0.0` .
2026-08-19 10:48:13 -05:00
`trade_journal_analyze` reports `avg_win` and `avg_loss` in account currency.
Inspect `entry_cost_coverage` before interpreting them: matched entry commission
and fees are included, while unmatched exits retain an exit-deal-only basis.
Those raw PnL averages are not Kelly inputs because deal sizes and capital at risk can vary.
2026-08-01 18:54:53 -05:00
Normalize each historical outcome to a consistent stake or initial risk before
2026-08-20 22:26:30 -05:00
computing the average return metrics supplied here. Values larger than 10 are
rejected as currency-like rather than R-multiples.
2026-08-01 18:54:53 -05:00
2026-08-12 16:02:18 -05:00
Journal side filters distinguish two directions: `side=long|short` selects the
economic position side of realized exits, while `side=buy|sell` selects the
broker fill direction. A sell fill can close a long and a buy fill can close a
short, so inspect the response's `side_filter.dimension` when automating cohorts.
2026-08-01 05:31:34 -05:00
Portfolio stop risk is the gross sum of each ticket's remaining loss from its
current MT5 mark to its stop. This measures equity at risk now; it does not reuse
the original entry-to-stop loss after unrealized P&L has changed. It is a
conservative path-risk measure, not a same-symbol net-exposure estimate; a path
can trigger both sides of a hedge sequentially. Pending-order stop risk is
reported separately as contingent and is included in the total only when
`include_pending=true` .
2026-07-11 21:10:01 -05:00
2026-08-18 22:48:11 -05:00
Default compact output retains a per-position exposure summary, including the
ticket, side, volume, current mark, stop/target, notional value, and stop-risk
status. When every included position and pending order has quantifiable stop
risk, `risk_total_complete=true` and the unconditional `total_risk_*` fields are
numeric. If any component has no stop, a breached stop, or unusable tick
metadata, those totals are `null` ; `quantified_risk_*` remains the explicitly
labeled subtotal of components that could be measured.
2026-07-11 21:10:01 -05:00
`notional_value` and portfolio notional fields are linearized account-currency
exposures derived from the broker's tick value and tick size. The per-position
`contract_price_product` diagnostic preserves raw `volume × contract_size × price`
with the explicit unit `contract_size_times_price` ; it must not be compared with
account equity or summed across unlike instruments.
2026-08-12 15:54:36 -05:00
`trade_get_open` uses the same distinction on each position row.
`notional_account` is comparable with account balance/equity and names its
currency in `notional_account_currency` ; `notional_quote` is the raw contract
price product in `notional_quote_currency` . If broker tick economics are not
available for a cross-currency conversion, `notional_account` is `null` and
`notional_account_unavailable_reason` explains why. Never sum `notional_quote`
across instruments with different quote currencies.
2026-06-12 22:17:00 -05:00
---
## `trade_var_cvar_calculate`
2026-08-25 13:04:47 -05:00
Estimates Value at Risk (VaR) and Conditional VaR (CVaR, a.k.a. Expected
2026-06-12 22:17:00 -05:00
Shortfall) for the current open positions — either the whole portfolio or a single
2026-08-25 13:04:47 -05:00
symbol. The default holding period is one bar of the selected timeframe; pass
`--horizon-bars` to scale the same return sample over multiple bars.
2026-06-12 22:17:00 -05:00
```bash
# Portfolio VaR/CVaR at 95% over one H1 bar
2026-08-12 11:58:51 -05:00
mtdata-cli trade_var_cvar_calculate --timeframe H1 --lookback 500 --confidence 0.95 --json
2026-06-12 22:17:00 -05:00
# Symbol-scoped, parametric/Gaussian, percentage returns
2026-08-24 20:28:50 -05:00
mtdata-cli trade_var_cvar_calculate EURUSD --method parametric --transform pct --lookback 300 --json
2026-08-25 13:04:47 -05:00
# EWMA-weighted tail over a six-bar horizon, including the forming bar
mtdata-cli trade_var_cvar_calculate EURUSD --method ewma --horizon-bars 6 --include-incomplete --json
2026-06-12 22:17:00 -05:00
```
| Parameter | Default | Description |
|-----------|---------|-------------|
| `symbol` | — | Restrict to one symbol's exposure; omit for the full portfolio. |
2026-08-25 13:04:47 -05:00
| `timeframe` | `H1` | Return interval. Combined with `horizon_bars` this is the holding period. |
2026-06-12 22:17:00 -05:00
| `lookback` | `500` | Historical bars used to build the return distribution. |
2026-08-25 13:12:41 -05:00
| `horizon_bars` | `1` | Holding period in bars of `timeframe` . Default is one-bar VaR; pass `5` to match `portfolio_risk_decompose` . Multi-bar results scale the one-bar return sample rather than simulating a path. |
| `include_incomplete` | `false` | When true, the currently forming candle can enter the return series. Default uses only completed bars. |
2026-08-25 20:24:13 -05:00
| `confidence` | `0.95` | Confidence fraction (`0.95` , `0.99` ); must satisfy `0.5 < confidence < 1` . |
| `method` | `historical` | `historical` (empirical tail of observed P&L), `parametric` (Gaussian), `cornish_fisher` (skew/kurtosis-adjusted Gaussian), or `ewma` (exponentially weighted historical). This empirical `historical` method is not the bootstrap used by `portfolio_risk_decompose method=bootstrap_historical` . |
2026-06-12 22:17:00 -05:00
| `transform` | `log_return` | Return transform: `log_return` or `pct` . |
2026-08-25 13:04:47 -05:00
| `min_observations` | `50` | Minimum aligned observations before estimating risk. EWMA and Cornish-Fisher need enough sample for their extra moments/weights; the tool reports the effective sample in the payload. |
2026-06-12 22:17:00 -05:00
2026-08-25 13:04:47 -05:00
**Output** includes `var` and `cvar` , position/exposure counts, method/horizon provenance, and — by detail level —
2026-06-12 22:17:00 -05:00
per-position and per-symbol exposure breakdowns. With no open positions, `--detail full`
returns the legacy zero-filled arrays.
2026-07-11 21:12:58 -05:00
VaR/CVaR converts percentage price shocks to account-currency P&L with each
symbol's broker-provided tick value and tick size (`pnl_model` is
`tick_value_linear_sensitivity` ). Positions without usable tick economics are rejected
rather than mixed into a portfolio in incompatible quote-currency units. The model is
linearized and does not include gaps, spread changes, swaps, or nonlinear payoff effects.
2026-08-06 20:39:50 -05:00
Every open position must have usable account-currency valuation and every
included symbol must have usable history. The tool returns
2026-08-01 05:31:34 -05:00
`portfolio_var_incomplete` instead of silently calculating a smaller portfolio
2026-08-06 20:39:50 -05:00
when any position lacks valuation inputs or any symbol history is unavailable.
2026-07-11 21:12:58 -05:00
2026-08-25 13:04:47 -05:00
Method notes:
2026-08-25 20:24:13 -05:00
- `historical` uses the empirical tail of equally weighted sample returns
(`scenario_generation=empirical_observed_pnl` ). Thin samples for the chosen
confidence are marked `sample_quality=insufficient` and report
`tail_observations` . The payload includes `data_start` , `data_end` , and
`as_of` for the exact return window.
2026-08-25 13:04:47 -05:00
- `parametric` assumes Gaussian returns and uses sample mean/variance.
- `cornish_fisher` starts from that Gaussian quantile and adjusts it with sample
skewness and excess kurtosis. It is most useful when the return sample is
clearly non-normal; small samples make the adjustment noisy.
- `ewma` reweights the same historical returns with exponential decay so recent
2026-08-30 12:15:32 -05:00
observations dominate. Configure the decay with `ewma_decay` (default `0.94` ).
The payload reports the decay, half-life, effective observations, and effective
tail observations used to grade sample quality.
2026-08-25 13:04:47 -05:00
- `--horizon-bars N` treats the holding period as N times the selected
timeframe. It does not simulate intra-horizon barrier hits or path dependence.
- `--include-incomplete` adds the in-progress bar to the return series. That can
make a live reading more current, but the last observation is then a partial
bar rather than a completed close.
2026-06-12 22:17:00 -05:00
---
## `trade_stress_test`
Applies deterministic percentage price shocks to open positions and reports the P&L
impact. Useful for "what if EURUSD drops 2% and everything else 3%?" scenarios.
```bash
# Per-symbol shocks
2026-08-12 21:22:07 -05:00
mtdata-cli trade_stress_test --shocks '{"EURUSD":-2.0,"GBPUSD":-1.5}' --json
2026-06-12 22:17:00 -05:00
# Wildcard shock applied to every position without an explicit entry
2026-08-12 21:22:07 -05:00
mtdata-cli trade_stress_test --shocks '{"*":-3.0}' --detail full --json
2026-06-12 22:17:00 -05:00
```
| Parameter | Default | Description |
|-----------|---------|-------------|
2026-08-25 13:12:41 -05:00
| `shocks` | (required) | Per-symbol percentage shocks, e.g. `{"EURUSD": -2.0}` . Use `"*"` as a fallback for symbols without an explicit entry. Each shock must be finite and **greater than -100** . `-100` is rejected because it would imply a zero or negative price; model a near-total wipeout with a shock such as `-99.99` . |
2026-06-12 22:17:00 -05:00
| `include_unshocked` | `false` | Include positions that received no shock (no exact match and no `"*"` fallback). |
| `detail` | `compact` | `full` adds per-position diagnostics. |
**Output** is a list of `items` (one per evaluated position) with `ticket` , `symbol` ,
`side` , `volume` , `shock_pct` , `current_price` , `shocked_price` , and `pnl_impact` , plus
totals: `total_pnl_impact` , `positions_total` /`evaluated` /`shocked` , and — when account
metadata is available — `equity_before` /`equity_after` /`impact_pct` .
---
## Caveats
- All three tools read live MT5 state; results change as positions and quotes move.
2026-08-01 05:31:34 -05:00
- A `None` position/order response from MT5 is a failed snapshot, not an empty
book. Snapshot-dependent analytics return a `*_snapshot_unavailable` error;
empty tuples/lists remain valid empty books.
2026-08-25 13:04:47 -05:00
- VaR/CVaR assume the recent return distribution persists over the requested
2026-08-25 13:12:41 -05:00
`horizon_bars` of the selected timeframe (default one bar); they are not a
guarantee of maximum loss.
2026-06-12 22:17:00 -05:00
- Stress shocks are deterministic and linear in price; they do not model spread
2026-08-25 13:12:41 -05:00
widening, gaps, swaps, or correlation breaks. A `-100` percent shock is not
supported (exclusive minimum `-100` ); use a near-total shock such as `-99.99`
when you want almost complete loss of the shocked price.
2026-08-01 18:54:53 -05:00
- Kelly sizing is only as good as its inputs — estimate `win_rate` and normalized
average win/loss returns from a sufficient out-of-sample track record.
2026-06-12 22:17:00 -05:00
2026-07-13 17:15:01 -05:00
## See also
2026-06-12 22:17:00 -05:00
- [BARRIER_FUNCTIONS.md ](BARRIER_FUNCTIONS.md ) — TP/SL hit probabilities
2026-07-13 17:15:01 -05:00
- [TRADING_SAFETY.md ](TRADING_SAFETY.md ) — Execution preview and guardrails
- [GLOSSARY.md ](GLOSSARY.md ) — VaR, CVaR, Kelly
- [SAMPLE-TRADE-ADVANCED.md ](SAMPLE-TRADE-ADVANCED.md ) — Risk gates in a full workflow