Read this firstConventions

These are the mistakes that produce a plausible number rather than an error. Every one of them has been made.

Units

Rates and volatilities are decimal fractions

20% is 0.2, not 20. So is a 20% return. A volatility of 20 is not refused, because it describes a market that moves 2000% a year — which is a market, so the server prices it.

Time is a year fraction

Thirty days is about 0.082. One year is 1.0. Nothing here takes a count of days or a date for an option's life.

Losses are positive

A value at risk of 0.023 is a 2.3% loss. An expected shortfall is always at least as large as the value at risk at the same confidence; if it comes back smaller, the arguments are not what you think they are.

A Sharpe ratio is per period

The figure people quote is annualised. An annualised 1.0 on daily data is about 0.063 per period, and everything in the validation group takes the per-period one. Pass periodsPerYear to get both back.

periodsPerYear is required, not assumed

252 for daily equity data, 52 for weekly, 12 for monthly. Annualising weekly data with 252 overstates volatility by a factor of about 2.2, which does not look wrong enough to notice.

Weights

Weights are not normalised for you. Weights summing to 0.98 are either a 2% cash position or a typo, and the two want opposite treatment; normalising quietly would turn the second into a plausible answer. The sum is reported on every result that takes weights, and a sum far from one is refused with the total named.

Handles

Three tools mint handles, and the three kinds are not interchangeable. Each payload carries a kind tag, so presenting the wrong one is refused rather than misread as an empty one of the right kind.

Minted byCarriesCannot be used for
open_position_book option and underlying legs, the spot, the rate anything expecting a covariance estimate
estimate_return_moments a mean vector and a covariance matrix — not the returns the historical and drawdown tools, which read the path
bootstrap_discount_curve the pillar times and their quotes either of the above

A handle caps at 8192 characters, which is why the moments handle carries second moments rather than the matrix. A year of daily returns on four assets encodes to roughly 6000 characters and five years on ten assets to about 69,000, so a handle carrying the matrix would refuse almost every portfolio worth asking about. A mean vector and a covariance matrix are n + n² numbers however long the history is. A curve does fit — five pillars is 264 characters — so the curve handle carries its pillars and quotes.

What that buys: the parametric estimators run from a handle, so a matrix is sent once and then reweighted and decomposed across as many calls as you like. What it costs: the historical estimators and every drawdown statistic read the return path, and a second-moment summary has thrown it away. Those tools require the matrix and say so when handed a handle.

Failures

The specification draws a line between two kinds of failure and this server draws it in the same place, because the two have different audiences.

Protocol errors are JSON-RPC errors. They say the request was not a thing the server could act on — an unknown tool, a malformed body — and a model is unlikely to recover, because the fault is in the plumbing rather than in the arguments.

Execution errors come back as ordinary results with isError set. They say the call arrived intact and the server declined it, and they are addressed to the model: which field, what was wrong, what would be accepted instead. A schema violation is one of these, not a protocol error — a volatility given as 20 instead of 0.2 is a fixable mistake, and saying so is more useful than refusing the request.

Absent numbers

An absent number comes back as null, and means something specific.

Infinity and NaN are never sent. Neither is JSON, and a strict parser rejects the whole message over one of them — so a single unbounded quantity would destroy every number beside it.