Skip to main content
Glama
simoneb

ibkr-risk-mcp

by simoneb

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault
IBKR_HOSTNoWhere TWS listens. A remote host must also be in the API's Trusted IPs127.0.0.1
IBKR_PORTNo7496/7497 TWS live/paper, 4001/4002 Gateway live/paper7496
IBKR_ACCOUNTNo**Required** on a multi-account login. Without it every position/account tool refuses rather than combining accounts
IBKR_CLIENT_IDNoMust differ from every other script on this TWS. **Never 0** — TWS reserves that for orders placed by hand17
IBKR_ENABLE_WHATIFNoThe gate on `whatif_order`. Also decides whether the connection itself is opened read-onlyfalse
IBKR_GREEKS_TIMEOUTNoSeconds to wait for greeks on one contract. Short by design — IB answers fast or never, and explicit refusals cut the wait short anyway4
IBKR_RISK_FREE_RATENoUsed to discount and to carry spot to the forward in the local repricing0.04
IBKR_WHATIF_TIMEOUTNoSeconds to wait for a what-if reply. IB sometimes never sends one5
IBKR_CONNECT_TIMEOUTNoSeconds for the API handshake6
IBKR_MARKET_DATA_TYPENo1 live, 2 frozen, 3 delayed, 4 delayed-frozen. 3 works and does carry greeks1
IBKR_MAX_MKT_DATA_LINESNoConcurrent market data subscriptions. IB allows about 5040

Instructions

Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.

This server publishes no instructions, or was last inspected before Glama recorded them.

Capabilities

Features and capabilities supported by this server

Protocol revision2025-11-25

CapabilityDetails
tools
{
  "listChanged": false
}
prompts
{
  "listChanged": false
}
resources
{
  "subscribe": false,
  "listChanged": false
}
experimental
{}

Tools

Functions exposed to the LLM to take actions

NameDescription
check_connectionA

Check whether TWS or IB Gateway is reachable and an account is logged in.

Call this first whenever another tool fails. It separates the four situations that all present as "cannot connect" and need different fixes:

  • not_listening — nothing is on the port. TWS is not running, or IBKR_PORT points at the wrong one; the response lists which of the four default ports (7496 TWS live, 7497 TWS paper, 4001/4002 Gateway) are answering.

  • api_not_enabled — the port answers but the API handshake never completes. "Enable ActiveX and Socket Clients" is off in TWS.

  • client_id_in_use — another script holds this client id.

  • not_logged_in — the API is up but no account is loaded.

  • connected — everything is in place.

hint says what to do about the state in each case.

get_margin_summaryA

Margin and liquidity, split by segment.

Returns NetLiquidation, EquityWithLoanValue, FullInitMarginReq, FullMaintMarginReq, AvailableFunds, ExcessLiquidity, TotalCashValue, BuyingPower and Leverage — each as the account total and, where IB reports it, separately for the securities (-S) and commodities (-C) segments.

The segments are the point. Futures margin must be met in the commodities segment; IB covers a shortfall there by sweeping cash out of the securities segment. An account whose total excess liquidity looks healthy can still be one bad day away from a forced liquidation if the shortfall lands in commodities while securities is also falling. Report the segment figures, not only the totals, whenever futures are involved.

get_position_greeksA

IB's model greeks for every option position — its numbers, not ones implied locally.

Returns per position: conid, symbol, secType, right, strike, lastTradeDate, settlementDate, daysToExpiry, position, multiplier, undPrice, impliedVol, delta, gamma, vega, theta, optPrice and pvDividend.

Things to know about the values:

  • undPrice is the forward IB used, not the index spot. For a futures option that is the future's price, which differs from the cash index by the basis; any repricing has to start from it.

  • multiplier comes from the contract. ES is 50 and MES is 5, and the difference is a factor of ten in every exposure figure.

  • settlementDate, not lastTradeDate, is the expiry. The quarterly ES options settle AM and stop trading the afternoon before, so TWS shows 17 December for something that expires on the 18th, while a weekly settling the same morning shows the 18th. Both are returned; pair positions on settlementDate.

  • Missing greeks are listed, not dropped. A contract IB never published model greeks for appears under missing with the reason. The usual causes are no market data subscription for that instrument, or a strike too illiquid for IB to imply a volatility. Note that delayed data does carry model greeks, so a missing row is rarely explained by the market data type alone.

get_vol_surfaceA

IB's implied volatility surface for an underlying: a grid of (expiry, strike) with impliedVol, delta, optPrice, undPrice and daysToExpiry.

This is the input that makes local repricing deterministic. With IB's own volatilities in hand, a constant-volatility scenario needs no proprietary model — only Black-76 arithmetic on top of numbers IB published.

Do not collapse the surface to one number. Volatility has a term structure: ES at 139 days can sit near 15% at the money while the front month prints 12%. Using the front month for a longer tenor understates a long-dated position badly. Read the tenor you need, and interpolate between tenors in total variance if you must.

daysToExpiry counts to the settlement date. For AM-settled expiries that is one day past the last trading date TWS shows.

Strikes with no published volatility come back under missing with the reason rather than being silently absent — a surface missing its left wing looks identical to one that has none.

Check which instrument you got. A bare root is ambiguous: ES is the E-mini S&P 500 future and Eversource Energy on NYSE, and with no sec_type the stock wins. The resolved contract comes back under underlying, any collision is listed in warnings, and a surface on the wrong instrument looks perfectly reasonable until you notice the strikes are two orders of magnitude off.

stress_portfolioA

Reprice the whole portfolio — options, equities, futures — across underlying shocks and return the P&L curve and its trough.

The trough is the primary output: the worst point of the curve and the shock at which it sits. troughRefined interpolates between grid points for where the minimum actually falls, and is labelled as interpolated.

The model, returned with every result in assumptions:

  • only equity underlyings are on the axis by default (scope='equity'). FX, rates and anything else is excluded outright and listed under excluded with its market value. This is what Risk Navigator's Equity tab does, and it is what makes the two comparable — verified against a live account, where the engine and Risk Navigator agreed to 7 dollars on 29,000 at a 15% fall once the FX leg was off both. Off the equity axis the single shock is meaningless: the same account's CAD strangle was contributing -21,716 at -20% and -7,183 at +10%, dominating both tails.

  • all underlyings in scope are shocked by the same percentage at once, which is Risk Navigator's own default assumption. betas scales that shock per symbol and reaches every class that responds to one — an option is repriced at its own beta-scaled move, not at the index move. Use it to stand a foreign underlying down off an equity axis, and read the warning it produces: a beta of 0 takes a position off this curve, it does not measure that position's own risk.

  • options are repriced with Black-76 on the shocked forward using IB's implied volatility. Equity options are carried from spot using IB's pvDividend, so both kinds go through one pricer.

  • the volatility level is flat along the shock axis unless you say otherwise. Neither vol mode raises it: sticky_strike pins volatility to the strike, sticky_moneyness slides a strike along today's smile. Real volatility rises when an index falls, and a net short option book pays for that on top of the delta and gamma this curve already counts. vol_bump does not fill the gap — it is constant across shocks. vol_slope_down does: 1.0 adds one volatility point per 1% fall. It is your input, not a measurement, and it is applied as a parallel shift across every tenor.

  • P&L is model-price-now against model-price-shocked, so the curve is exactly zero at zero shock by construction. The gap between the local model and IB's own price is reported per position as modelVsMarket instead of being folded into the curve. Expect it to be small out of the money and a couple of percent in the money, where IB prices the early exercise that Black-76 has no room for — so the curve understates losses slightly once options go deep in the money.

  • bonds are held flat unless bond_rate_shift_bp is set; anything this server does not model is held flat and named in warnings.

  • breakdown='expiry' adds pnl_by_expiry to every point and troughByExpiry beside the trough — each expiry's own worst point and what it contributes at the portfolio's trough. On a book holding one root across many expiries those are the two different numbers behind "which short is the problem", and pnl_by_symbol can answer neither.

Check reconciled before quoting anything. At zero shock the portfolio is rebuilt from its positions and compared against NetLiquidation; a residual over 1% returns reconciled: false with the residual attached. A curve that does not reconcile is missing something, and the number it gives for the trough is missing it too.

Risk Navigator's own volatility shock model is not published. sticky_strike is the approximation that corresponds to its default curve, not a reproduction of it — expect the shape to match and the last few percent not to.

stress_curveA

The portfolio P&L curve under several volatility regimes at once — the data behind a risk graph, for plotting rather than for reading point by point.

Risk Navigator draws two curves: a constant-volatility line and one from its own implied-volatility model, which is not documented and cannot be reproduced. This returns as many as you ask for, and the volatility assumption behind each is a number in the output rather than a black box: volSlopeDown is volatility points per 1% fall.

Read the slope-0 curve first. It is the constant-volatility case and the only one with an external check — it should sit close to Risk Navigator's blue line. If it does not, the volatility lookup is wrong and no other scenario in the result means anything.

Check volSurfaceUsed. It lists every quote the repricing actually read, as (underlying, tenor, strike, iv). Empty under sticky_moneyness means no expiry held three strikes, so no smile could be built and every option silently fell back to sticky_strike — the result looks perfectly normal and is not the model you asked for. fetch_skew=true fixes it at the cost of extra market data requests.

How each curve is built, and where it is weakest:

  • Every scenario reprices one loading of the portfolio and one surface, so the curves differ by assumption alone. Calling the single-curve tool three times could not promise that: the book moves between calls.

  • The starting volatility is IB's own, per contract, out of a model that prices American exercise. The surface is used only for the change in volatility as a strike slides to new moneyness, which keeps IB's better number as the anchor and keeps every curve exactly zero at zero shock.

  • vol_coord reproduces IB's own model: volatility is multiplied, not shifted — a fall of X moves it by 10X relatively, a rise by -X, damped across tenors. Being relative is what makes the surface steepen by itself, since a wing already quoted at 41% takes more points than a 31% at-the-money out of the same scenario. The asymmetry is IB's documented one; the damping is fitted here and is not published, so vol_coord_decay is an input you should refit against your own Risk Navigator.

  • The additive volSlopeDown alternative is a parallel shift, flat across tenors. It cannot steepen at any value, and on a ratio book that is the difference between a curve that keeps falling and one that turns back up. Prefer vol_coord unless you specifically want a flat regime.

  • Equities move by the shock times their beta, futures and options by their own underlying's beta-scaled move. Bonds are flat unless bond_rate_shift_bp is set. FX is off the axis by default and reported under excluded with its market value — not held flat in silence.

  • Options IB would not model are repriced from a locally implied volatility where a mark price exists, flagged per position and in warnings, and held flat only when even that fails.

  • date_offsets=[0, 3] (or valuation_dates) crosses the scenarios with a family of valuation dates, so "today against Monday, when the August wings expire" is one call rather than two that the market moves between. Each entry under curves carries its own valuationDate and dateOffsetDays; name stays the scenario's and label distinguishes them. Time is the only thing that moves — today's spot and today's volatilities with the clock advanced, which is decay and not a forecast.

  • breakdown='expiry' puts pnl_by_expiry on every point and troughByExpiry on every curve, keyed on the option's settlement date. On a book running one root across many expiries that is the only way to see which expiry owns the trough; pnl_by_symbol shows one "ES" number for all of them.

pnl_pct_of_nlv is on every point, and netLiquidation at the top. Quote the fraction rather than the amount when comparing two dates or two accounts. Check reconciled before quoting any of it.

stress_whatifA

The same stress run, with hypothetical legs added — three curves and three troughs: the portfolio as it stands, the portfolio plus the legs, and the difference.

This is what replaces reading Risk Navigator's What-If by hand: "if I add N puts at strike K expiring E, where does the trough move to?"

Read all three troughs. The trough of the difference is not the difference of the troughs — adding protection moves where the worst point sits as well as how deep it is, and comparing only the depths hides the move. A structure that lifts the bottom by very little may still have pushed it from −8% out to −15%, which is the part that matters.

Hypothetical options are priced off IB's current model greeks for those exact contracts, so both curves start from the same volatilities and the difference is the structure alone. A leg that cannot be resolved or priced is reported in legProblems and left out of the second curve; the comparison then covers only the legs that did resolve, and says so.

With breakdown='expiry' all three curves are broken out by settlement date, and the difference curve's pnl_by_expiry is where the structure actually landed: the expiry you traded against moves and the ones you did not read zero, which is how a hedge is told apart from a change of subject. A short expiry whose row goes to roughly nothing in withLegs is one the structure has closed out.

Nothing is sent to IB's order path here — this is pure local repricing. For what the structure costs in margin, use whatif_order.

calibrate_vol_coordA

Fit vol_coord_decay to YOUR Risk Navigator, and keep the result.

vol_coord reproduces IB's volatility-coordinated model. Its asymmetry — a fall moves volatility ten times as hard as a rise — is IB's, documented. Its term damping VR(t) is not: IB says only that the function exists and is decreasing. The number this server ships was fitted to one screenshot of somebody else's Risk Navigator, from nine points read off a chart by eye. It has no claim on your book, every vol_coord result says so in warnings, and this tool is how you replace it.

What to do:

  1. In TWS open Risk Navigator's risk graph, on the Equity tab if you use the default scope.

  2. Read the Vol.Coord. curve — the one that responds to volatility, not the constant-volatility line — at four or more shocks spread across the range you care about.

  3. Pass them in as {shock: -0.20, pnl: -28000} pairs, shocks as fractions.

The fit comes back with the residual at every point and, more usefully, with what to distrust about it: the tenor range your positions actually constrain, and the most extreme volatility the fitted decay produces. A decay that reproduces the curve by pricing a wing at 150% has fitted the chart rather than the market, and it says so rather than leaving you to find out three layers down in a P&L.

Unless persist=false, the fit is written to disk and becomes the default vol_coord_decay for every later stress_curve on this machine — no restart, no carrying the number by hand — together with what it was fitted against, which is then reported in assumptions.volCoordDecaySource. Set IBKR_CALIBRATION_FILE to move the file. It is the only thing this server writes.

A fit taken against a portfolio that does not reconcile is returned but not stored. The reason is asymmetry of failure: a curve that is missing a position announces itself through reconciled, while a decay that absorbed the same gap would go on silently deforming every vol_coord run afterwards.

Nothing here trades or quotes. It reads positions and reprices locally.

whatif_orderA

IB's own margin impact for a hypothetical structure. Nothing reaches the market.

Each order carries whatIf=True, which IB evaluates in its margin engine and discards: it is never routed, never acknowledged as live, never appears in the order book. The tool is still gated behind IBKR_ENABLE_WHATIF=true because it is the only thing in this server that touches the order path at all; with the gate closed it sends nothing and returns blocked: true.

Returns initMargin, maintMargin and equityWithLoan before/after/change, plus commission and any warningText, in two views:

  • perLeg — each leg evaluated on its own.

  • cumulative — legs 1..k as a combo, for every k, so you can see where the offset appears.

Read the combined figure, not the sum of the legs. SPAN offsets the legs against each other and against what the account already holds, so the two differ — the difference is reported as offset.spanOffset. IB's what-if on arbitrary multi-leg combos is unreliable and will sometimes return nothing; that is reported per step and does not mean the structure is invalid.

If every call fails with no margin figures, check whether TWS has "Read-Only API" enabled — that setting blocks what-if orders too.

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription

No resources

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/simoneb/ibkr-risk-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server