Files
mt-data-mcp/docs/TRADING_RISK.md
T

11 KiB
Raw Blame History

Trading risk analytics

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 when you move from analysis to execution.

Tool Answers
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?”

Dense terms: Fixed-fraction sizing · Kelly · VaR · CVaR / ES · Drawdown

Related: Sample trade · Advanced playbook · Guardrails · Barriers


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.

# Current portfolio risk only
mtdata-cli trade_risk_analyze --json

# Size a new long: risk-based volume from entry + stop
mtdata-cli trade_risk_analyze EURUSD --direction long \
  --entry 1.0850 --stop-loss 1.0800 --sizing '{"method":"fixed_fraction","risk_pct":1.0}' --json
Parameter Default Description
symbol — Symbol for new-trade sizing; omit for portfolio-only risk.
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].
strict_risk true Return suggested_volume=0.0 if the broker minimum lot would exceed the requested sizing risk.
include_pending true Include contingent stop-loss risk from pending orders in portfolio totals.
direction — long/short (aliases accepted) for the proposed trade.
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 closed/stale quote is labeled last_available_tick_* and sizing_reference_only; refresh it before submission.
stop_loss — Proposed stop (alias sl). Required to compute risk-based volume.
take_profit — Optional target (alias tp) for reward/risk context.

New-trade sizing uses an incremental candidate-risk policy: sizing.risk_pct 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.

Candidate validation is independent of portfolio observation. A valid proposed trade returns candidate_valid: true. If its direction, stop, or target is invalid, the response retains the account and portfolio snapshot but returns success: false, candidate_valid: false, a structured error_code, and portfolio_snapshot_status: available; CLI callers receive a nonzero exit status.

Kelly sizing

Set sizing.method=kelly and supply edge statistics in the same JSON object:

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
Parameter Default Description
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).
sizing.max_risk_pct 2.0 Hard cap on account risk in percentage points (1 means 1%); must be in (0, 100].

The Kelly fraction is win_rate − (1 − win_rate) / (avg_win / |avg_loss|), then scaled by sizing.fraction_multiplier and capped by sizing.max_risk_pct. On a non-positive edge the tool reports status="kelly_no_edge" and a suggested volume of 0.0.

trade_journal_analyze reports avg_win and avg_loss in account currency. Those raw PnL averages are not Kelly inputs because deal sizes and capital at risk can vary. Normalize each historical outcome to a consistent stake or initial risk before computing the average return metrics supplied here.

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.

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.

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.

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.


trade_var_cvar_calculate

Estimates one-bar Value at Risk (VaR) and Conditional VaR (CVaR, a.k.a. Expected Shortfall) for the current open positions — either the whole portfolio or a single symbol.

# Portfolio VaR/CVaR at 95% over one H1 bar
mtdata-cli trade_var_cvar_calculate --timeframe H1 --lookback 500 --confidence 0.95 --json

# Symbol-scoped, parametric/Gaussian, percentage returns
mtdata-cli trade_var_cvar_calculate EURUSD --method parametric \
  --transform pct --lookback 300 --json
Parameter Default Description
symbol — Restrict to one symbol's exposure; omit for the full portfolio.
timeframe H1 Return interval and the one-bar holding period.
lookback 500 Historical bars used to build the return distribution.
confidence 0.95 Confidence fraction (0.95, 0.99); must satisfy 0 < confidence < 1.
method historical historical (empirical tail) or parametric (Gaussian model).
transform log_return Return transform: log_return or pct.
min_observations 50 Minimum aligned observations before estimating risk.

Output includes var and cvar, position/exposure counts, and — by detail level — per-position and per-symbol exposure breakdowns. With no open positions, --detail full returns the legacy zero-filled arrays.

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. Every open position must have usable account-currency valuation and every included symbol must have usable history. The tool returns portfolio_var_incomplete instead of silently calculating a smaller portfolio when any position lacks valuation inputs or any symbol history is unavailable.


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.

# Per-symbol shocks
mtdata-cli trade_stress_test --shocks '{"EURUSD":-2.0,"GBPUSD":-1.5}' --json

# Wildcard shock applied to every position without an explicit entry
mtdata-cli trade_stress_test --shocks '{"*":-3.0}' --detail full --json
Parameter Default Description
shocks (required) Per-symbol percentage shocks, e.g. {"EURUSD": -2.0}. Use "*" as a fallback for symbols without an explicit entry.
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.
  • 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.
  • VaR/CVaR assume the recent return distribution persists and use a single-bar holding period; they are not a guarantee of maximum loss.
  • Stress shocks are deterministic and linear in price; they do not model spread widening, gaps, swaps, or correlation breaks.
  • 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.

See also