| 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: In TWS open Risk Navigator's risk graph, on the Equity tab if you use
the default scope. 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. 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. |