Sign up

Products

Status

Risk Analytics

Risk Analytics proposes index hedges for SPY, QQQ and IWM from live dealer positioning: protective puts when dealers are short gamma and protection is cheap, collars when calls are rich relative to puts, and a note when the volatility term structure is inverted. It also computes empirical historical Value at Risk and expected shortfall for a portfolio you send.

How the proposals are made

/api/v3/institutional/hedging reads the latest trading day's options signals for each of the three index ETFs and applies three rules. Every number in a proposal is a raw value from that day, not a model estimate:

hedge_typeFires whenaction
protective_putsdealer_gamma_net is below 0 (dealers short gamma, so moves are amplified) and iv_rank_pct is below 25 (protection is cheap)buy index puts
collarrisk_reversal_25d is above 0 (25-delta calls rich relative to puts)sell calls to finance protective puts
term_structure_notefront implied volatility is above 30-day implied volatility (backwardation)note only: a stress regime, where hedges cost more

When no rule fires, proposals is an empty list and note says no hedge trigger active; the per-index metrics are still in market_context. An empty list is an answer, not an error. The proposals do not know your positions: they describe the index hedge market, not a hedge sized for a portfolio.

Results are cached for 5 minutes. Each proposal and each market_context entry carries as_of, the trading date of the signals it used; check it rather than the response timestamp.

Access

Note:

5 credits per call, Pro and up.

Endpoints

MethodPathMCP toolDescription
GET/api/v3/institutional/hedgingindex_hedge_proposalsIndex hedge proposals (SPY/QQQ/IWM): protective puts and collars from dealer gamma, IV rank and the 25-delta risk reversal, plus IV term-structure context. No parameters.
POST/api/v3/risk/historical_var_scenarionone (REST only)Historical VaR and expected shortfall for an explicit USD equity portfolio you send; see Historical VaR.
GET/api/v3/validation/{ticker}/kelly_uncertaintykelly_uncertaintyKelly fraction for one ticker from its stored prediction, in decimals, capped at ±25%. Withheld (null) while the expected return is not a calibrated forecast or the book is more than two sessions old; see Kelly sizing. Billed as Quant Signals (Starter+, 3 credits).

Example

Shell
curl -H "Authorization: Bearer $WEALTHNOW_API_KEY" \  "https://firm.wealthnow.io/api/v3/institutional/hedging" # Illustrative response structure; values are placeholders, not live data:# {#   "ok": true,#   "timestamp": "2026-09-24T14:10:00Z",#   "kind": "index_hedge_proposals",#   "hedge_set": ["SPY", "QQQ", "IWM"],#   "proposals": [#     {#       "instrument": "QQQ",#       "hedge_type": "collar",#       "action": "collar: sell calls to finance protective puts",#       "rationale": "25-delta risk reversal positive (+0.0120) ... collar financing is attractive",#       "dealer_gamma_net": ...,#       "dealer_gamma_z": ...,#       "iv_rank_pct": ...,#       "risk_reversal_25d": 0.012,#       "term_structure_signal": ...,#       "as_of": "2026-09-23"#     }#   ],#   "market_context": {#     "SPY": {#       "dealer_gamma_net": ..., "dealer_gamma_z": ..., "iv_rank_pct": ...,#       "risk_reversal_25d": ..., "term_structure_iv0": ..., "term_structure_iv30": ...,#       "term_structure_signal": ..., "term_structure_state": "contango",#       "as_of": "2026-09-23",#       "dealer_greeks": {"date": "2026-09-23", "call_gamma": ..., "put_gamma": ...,#                         "gamma_net": ..., "call_vanna": ..., "put_vanna": ...,#                         "call_charm": ..., "put_charm": ...}#     },#     "QQQ": {"...": "..."},#     "IWM": {"...": "..."}#   }# }

If no signals are available for any of the three indices, there is no answer: the route returns 503 with status: "unavailable_options_signals" and warning: "no_options_signals_data", and the call consumes no credits. It means the signals are missing, not that no hedge is needed.

Kelly sizing

/api/v3/validation/{ticker}/kelly_uncertainty sizes one ticker from its latest stored ensemble prediction (the book). When it serves a size, it computes in decimal returns, from the prediction's return estimate and 90% interval, both stored in percent:

Text
mu_decimal     = predicted_return_pct / 100sigma_decimal  = (interval_half_width / 1.645) / 100kelly_uncapped = mu_decimal / sigma_decimal^2 x convictionkelly_fraction = kelly_uncapped, capped at -0.25 and +0.25

Before 2026-09-25 the route divided a percent by a percent squared, so every fraction it served was 100 times too small.

The size is withheld today. kelly_fraction is null when either condition below holds, and withheld_reasons lists each one that does:

withheld_reasons entryMeaning
magnitude_unsupportedThe expected return is predicted_return_pct, which is the blended score × 100, not a calibrated forecast, so no size can be derived from it. The same rule withholds predicted_return_pct itself on Quant Signals. magnitudes_withheld names the withheld fields (kelly_fraction, inputs) and the reason, carries the live measurement's direction, measured_ic, horizon_days and blockers, and names what to use instead in use_instead (rank, percentile, decile).
book_staleThe book is more than two US equity sessions old (stale_after_sessions: 2).

No stored return is a calibrated forecast yet, so every ticker with a complete prediction answers with magnitude_unsupported. A withheld answer also sets inputs to null, because its return estimate is the unsupported number, keeps cap, and repeats the first reason in warning. It is a refusal, not a size of zero: do not substitute a size of your own.

Every answer for a ticker in the book says how old the book is: as_of_ts (the prediction's own timestamp), book_age_sessions (US equity sessions after that date, up to and including today), is_stale and stale_after_sessions.

Response for AAPL on 2026-09-25. kelly_fraction, inputs, withheld_reasons, magnitudes_withheld.reason, book_age_sessions and is_stale are the captured values; the other fields are shown as the route builds them, and ... marks values that were not captured:

JSON
{  "ok": true,  "timestamp": "...",  "ticker": "AAPL",  "kelly_fraction": null,  "warning": "book_stale",  "as_of_ts": "...",  "book_age_sessions": 9,  "is_stale": true,  "stale_after_sessions": 2,  "inputs": null,  "cap": 0.25,  "withheld_reasons": ["book_stale", "magnitude_unsupported"],  "magnitudes_withheld": {    "fields": ["kelly_fraction", "inputs"],    "reason": "the expected return is blended_score*100, not a calibrated forecast, so no size can be derived from it",    "use_instead": ["rank", "percentile", "decile"],    "...": "..."  },  "method": "fractional_kelly_uncertainty_discounted"}

When a size is served, the response adds kelly_uncapped and cap_binding (true when the cap cut the size), and inputs holds mu_pct and sigma_pct in percent (mu and sigma_estimate are the same two values under their earlier names), mu_decimal, sigma_decimal, conviction, interval_half_width_pct and units.

Other null answers name their cause in warning:

  • ticker_not_in_predictions (the ticker has no stored prediction) or no_predictions (no prediction table was available). These carry no book age.
  • A fund or ADR names its cause in an excluded block instead of warning (error_code: "excluded_from_equity_ranking"), with no book age: its row carries an equity-model score for characteristics a fund or ADR does not have, so no size is derived from it (see Funds and ADRs).
  • no_predicted_return, no_interval_half_width or no_conviction: the stored prediction lacks that input. Nothing is substituted for it; the route used to put a fixed volatility of 0.02 in place of a missing interval, and a conviction of 0.5 in place of a missing one.

Every answer, withheld or not, is a 200 and is billed. Answers are cached for 60 seconds.

A Kelly fraction is a research input, not an instruction to trade.

Historical VaR for a portfolio you send

POST /api/v3/risk/historical_var_scenario computes empirical historical Value at Risk and expected shortfall. You supply the positions, their market values and their daily return history; the API runs non-overlapping historical scenarios over that history and returns the loss at your chosen confidence and horizon. It reads no account: the numbers describe the portfolio in your request.

The route is served on REST. It is not an MCP tool, and the public OpenAPI spec does not list it yet. GET /api/capabilities carries its full request schema under tengu_v3_risk_historical_var_scenario.

The request

The body has four top-level fields:

FieldRequiredWhat it is
portfolioyessnapshot_id, as_of, nav_usd, cash_usd, and 1 to 20 positions, each with a permno, a side (long or short) and its market_value_usd
historyyesThe dated daily total returns for each position, which the scenarios are built from
confidenceno0.90 to 0.99, default 0.95
horizon_sessionsno1 to 30 trading sessions, default 1

The calculation needs at least ten tail observations at the chosen confidence and horizon. With fewer, the route refuses rather than estimating.

Reading the result

The response states the VaR and expected shortfall in USD, the scenario window and its age, and where each input came from. It does not verify that the positions are held anywhere, it does not calibrate future coverage, and it is not an instruction to trade.

Handle unavailable output

Inspect ok, status, and any availability or warning fields before using a numeric risk field. A successful HTTP exchange can carry an unavailable result. Do not use the response timestamp as the time of the underlying market data. See API reference for error handling and pricing and credits for settlement behavior.