Browser, Worker, or API? Drawing Option Model Lab's Compute Boundary

How measurements—not model prestige—decided which pricing workflows stay local and why only Heston calibration crosses the network.

When I started Option Model Lab, every calculation ran in the browser. That was a deliberate constraint: a static application is easy to open, easy to host, and still perfectly capable of interactive quantitative work.

Adding a backend later raised an uncomfortable question. Which calculations should move there? “The complicated models” sounds plausible, but it is not a useful engineering rule.

I measured the user-facing work

I benchmarked the actual fixtures used by the interface: one contract, one displayed smile, one small calibration, one comparison. On the reference machine, median times ranged from microseconds for Black–Scholes and SABR operations to roughly 174 ms for the bounded, 15-quote Heston calibration.

These figures are comparative development measurements, not latency promises. Their purpose was to separate three cases:

  1. work cheap enough for the main thread;
  2. local deterministic work that can interrupt interaction;
  3. bounded, user-triggered work expensive enough to justify a network round trip.

The surprising result was how much should not move.

Pricing stays beside the controls

Black–Scholes pricing and Greeks, implied-volatility inversion, Heston prices and displayed smiles, Merton, SABR, the current Local Vol reconstruction, and the two-model comparison all remain local.

That choice avoids network latency on every slider movement. It also means the interface continues to work without the production API. The quantitative TypeScript modules do not depend on React, so they can be tested directly without mounting the UI.

Mathematical sophistication is not a deployment criterion. The current Heston pricer is fast enough for direct manipulation; sending it to a server would make the product feel slower.

The Worker protects the interface

Heston calibration was already the outlier. It is deterministic and local, but a bounded fit can pause the main thread long enough to be visible. Moving that model-specific calculation into a Web Worker preserved the static architecture while keeping the controls responsive.

I did not turn the Worker into a universal calibration framework. Merton and SABR use different, smaller workflows and remain easier to understand as concrete implementations.

The API handles one bounded operation

The first remote slice is narrow: POST /api/v1/heston/calibrate.

The service validates quote count and iteration limits, applies process-level admission control, and runs without user storage. A network error, a server failure, or a full service does not disable the lab: the frontend reports the execution path and falls back to the numerically matched Worker.

This boundary gives the backend a real job without making local development depend on production DNS. It also limits the public resource-exhaustion surface. “Stateless” alone is not enough security for an expensive numerical endpoint; inputs and concurrency need hard bounds.

Numerical parity is part of the boundary

Once TypeScript and Python implement overlapping Heston behavior, HTTP success is not sufficient. Shared reference fixtures and explicit tolerances check that both sides agree. Invalid financial inputs are reported rather than silently clamped, because a convenient chart is not worth hiding a model-domain error.

The rule I kept

The final decision rule is simple:

Place a calculation according to measured user-facing cost, not how advanced its mathematics sounds.

Future market-scale surfaces, larger Monte Carlo workloads, or broader calibrations will need a new benchmark. They do not inherit the Heston decision automatically.

Project evidence

← All notes