Platform Platform overview Modules Solutions Industries Commodities Roles Quant More Pricing Customers Knowledge Center Blog Company Request Demo
Knowledge Center · Learn

Building forward curves: from interpolation to a production curve engine

A forward curve is a page of maths to fit and a system to trust. This in-depth guide covers both: sensible interpolation that fills the gaps without inventing arbitrage, and the production machinery around the fit, market-data cleaning, arbitrage validation, published uncertainty, curve sensitivities, solver choice, scaling, monitoring, and governance, that lets real money trade against the number.

18 min read · Updated 2026 · ETRM glossary

Why interpolation matters

The market quotes a handful of points; a usable curve needs hundreds. Gas for next month, next winter, and next year all trade, but April and May in between may not, and every position that settles in an unquoted period still needs a price. Interpolation fills the gaps between known points, and it is deceptively easy to do badly.

Interpolation. estimating values between known, quoted points. Done well it is invisible; done badly it silently poisons every price read off the gap, manufacturing forward prices that permit risk-free arbitrage or imply impossible market views.

The skill is not drawing lines between dots. It is filling the silence between quotes without inventing information the market never gave, which requires understanding what makes a curve economically sensible, not just visually smooth.

Linear interpolation: the safe default

Between two known points, assume a straight line. To find the value at a point that is a fraction t of the way from the left point (value a) to the right point (value b):

$$ \text{value} = a + t\,(b - a), \qquad t = \frac{x - x_{\text{left}}}{x_{\text{right}} - x_{\text{left}}} $$

At t = 0 you get a; at t = 1 you get b; at t = 0.5 the midpoint. A straight line between two positive prices can never go negative or overshoot, the interpolated value always sits between its two neighbours. It is boring, but it is bullet-proof, which is why desks lean on it.

A worked example. March = 64, June = 76 (three months apart). May is two months after March, so two-thirds of the way to June: t = 2/3 ≈ 0.667, giving May = 64 + 0.667 × (76 - 64) = 72. No overshoot, no nonsense, that is linear interpolation doing its safe job.

Linear interpolation holds the curve flat before the first quote and after the last (flat extrapolation), a deliberate, safe choice: every interpolated value sits inside the quoted range, so no overshoot is possible.

When linear is not enough: splines and overshoot

Linear is safe but can be too crude when the underlying curve genuinely bends. Suppose quoted points are Jan 60, Apr 48, Jul 40, a curve falling toward summer but flattening as it goes. Linear interpolation for mid-February draws a straight line from 60 to 48, giving roughly 56, but if the real seasonal curve is convex, the true value might be nearer 57-58. A straight line under-reads a convex curve between its knots.

This is why richer methods exist. Splines fit a smooth curve through the points that respects curvature and can better capture gentle seasonal bends. The trade-off is that smoother methods can overshoot, wiggling above or below all the data, sometimes producing negative prices, manufacturing the very arbitrage linear interpolation avoids.

PCHIP. a shape-preserving (monotone) interpolant. Unlike a natural cubic spline, it will not overshoot between data points, so it captures curvature without inventing peaks, troughs, or negative prices. It is the safe default when a curve genuinely bends.

The practical rule: start linear (safe, predictable), reach for shape-preserving splines only when the data genuinely curves, and always check the result introduces no spurious wiggles. Smoothness is a means, not the goal; economic sensibility is the goal.

Interpolation in code

Filling a monthly curve from a few quotes is a few lines. The safety check, that every interpolated price lies within the known range, is the line that catches overshoot instantly:

pythonimport numpy as np

# Known quote points: month index (0 = Jan) -> price.
known_months = np.array([2, 5, 8])          # Mar, Jun, Sep
known_prices = np.array([64.0, 76.0, 70.0]) # currency / MWh

# Interpolate every month Jan..Sep
all_months  = np.arange(0, 9)
interp      = np.interp(all_months, known_months, known_prices)

# Safety check: every interpolated price must lie within the known range.
lo, hi = known_prices.min(), known_prices.max()
safe = np.all((interp >= lo) & (interp <= hi))
print("all prices within range:", "PASS" if safe else "FAIL (overshoot!)")

Linear interpolation holds the ends flat (Jan and Feb equal March), and every value sits inside the quoted band, so the safety check passes. On a shape-preserving spline the same check is what proves the smoother fit did not overshoot into an impossible price.

The curve is a product, not a calculation

Fitting the curve is a page of maths. Everything around that fit, the ninety percent of production curve work that is not the maths, is what lets real money trade against the number. The desk question is not "how do I build a curve?" but "how does a trading desk build, validate, deploy, monitor, and trust a curve?"

Two facts drive everything below. First, most failures enter upstream of the fit, in the market-data layer: a crossed broker quote, a settlement that did not refresh, a duplicate that quietly makes the constraint matrix rank-deficient. By the time these reach the solver, the damage is done, and the solver may even "succeed" while producing a poisoned curve. Second, most failures are only caught downstream, by diagnostics, a spline that dips negative, a stale strip distorting a storage valuation, none visible in the fitted number itself, only if you test for them and publish the uncertainty.

The organising idea is a pipeline with a gate at each end: Market Data → Validation → Cleaning → Curve Builder → Diagnostics → Publication → Valuation → Risk. Every stage has a single responsibility and a typed interface, so a change in interpolation policy cannot reach into market-data handling, and a new feed cannot silently alter the fit.

Market data: the failures that happen before interpolation

Ask an experienced curve engineer where curve bugs come from and they will not say "the interpolation method." They will say "the data." The market-data layer is where a production curve is won or lost, and it is almost entirely absent from textbook treatments.

A single delivery product, say the Q1 gas quarter, may be quoted in the same minute by several sources that disagree: an exchange settlement (authoritative, but only struck once, at close), an exchange last-trade (live, but possibly hours old for an illiquid product), one or more broker bid/ask pairs (indicative, sometimes crossed or wide), and a trader mark (a manual override, lowest authority). You cannot feed all of them to the solver; you must resolve them to one clean number per product, and record why.

That resolution is a sequence of decisions, each a place production curves break:

  • Bid/ask to mid. The usable price is the mid, but only if the market is not crossed (bid > ask, a feed error) and not pathologically wide (a huge spread means no real liquidity). Both are rejected, not averaged.
  • Source hierarchy. For a liquid front product the last trade is freshest; for an illiquid back product the settlement is the daily reference. The engine prefers settlement, then trade, then broker, then manual mark.
  • Stale-quote thresholds. Every quote carries a timestamp; anything older than a freshness threshold is excluded rather than trusted. A stale mark that survives into the board is the classic silent poison.
  • Missing quotes. A product with no usable quote is simply absent, and its tenor becomes one of the inferred months the constrained fit must pin from the prior. Missingness is normal, not an error.
  • Duplicates. The same product from two sources must be deduplicated, because a duplicated constraint is not harmless: two identical rows make the system singular and the solve rank-deficient.
  • Time synchronisation. Gas and power, and different exchanges, close at different times and on different calendars. A curve struck "as of 17:00" must assemble quotes all valid as of that instant, applying each market’s close convention, not mixing a post-close settlement from one venue with a pre-close broker quote from another.
pythonfrom market_data import MarketDataEngine, RawQuote, QuoteSource

raws = [
    RawQuote("Q1", bid=87.8, ask=88.2, source=QuoteSource.BROKER,  ts=now-5m),
    RawQuote("Q1", settlement=88.0, source=QuoteSource.SETTLEMENT, ts=now-2m),  # duplicate, higher authority
    RawQuote("Q2", bid=62.0, ask=70.0, source=QuoteSource.BROKER,  ts=now-1m),  # spread too wide -> reject
    RawQuote("Q3", bid=56.0, ask=54.0, source=QuoteSource.BROKER,  ts=now),     # crossed (bid>ask) -> reject
    RawQuote("Q4", last_trade=78.0,    source=QuoteSource.TRADE,   ts=now-3h),  # stale -> reject
    RawQuote("Jan", settlement=95.0,   source=QuoteSource.SETTLEMENT, ts=now),
]
clean, report = MarketDataEngine().clean(raws, now)
# clean board: {'Q1': 88.0, 'Jan': 95.0}
# Q1 source used: exchange_settlement
# rejected -> stale: ['Q4'] | wide: ['Q2'] | crossed: ['Q3'] | deduped: ['Q1']

The two Q1 quotes were deduplicated with the settlement winning over the broker; Q2 dropped for a wide spread, Q3 for being crossed, Q4 for being stale. Crucially, the engine tells you why the others did not survive, the difference between a black box and an auditable pipeline. The lesson experienced engineers repeat: a great fit on a bad board is a bad curve.

The arbitrage validation suite

Arbitrage-freeness is a property to prove, not assume. Production turns it into an explicit validation suite that runs after construction and gates publication: a curve that fails any hard check never reaches the books; a curve that passes carries the report as its audit trail.

  • Negative forwards. No delivery price may be negative (barring genuinely negative-price power regimes, handled explicitly). A single negative is almost always an interpolation overshoot and can hand a downstream storage or swing model "free" energy. Hard fail.
  • Calendar-spread violations. Every quoted block must reprice from the fitted monthly curve. If the day-weighted average of a quarter’s months does not equal the quarter’s quote, the curve contains a calendar-spread arbitrage. Hard fail above tolerance.
  • Butterfly / convexity. Large sign flips in the second difference across three consecutive deliveries flag implausible local concavity, usually a bad interpolation or a mis-keyed mark. A soft diagnostic, because genuine seasonality is convex in places.
  • Peak/off-peak consistency. For power, the hour-weighted blend of the peak and off-peak curves must equal the baseload, or the three products form an arbitrage. Hard check.
  • Block repricing error. The direct constraint check across all quoted products; for an interpolant it must be at solver precision. A nonzero value means the solve did not honour the quotes.
  • Monthly to quarterly aggregation. Aggregating fitted months to quarters and quarters to the cal-year must be self-consistent, catching day-weighting bugs.

A validation suite is only convincing if you see it fail on a broken curve. Fed a curve with an overshoot-induced negative in July, a calendar-spread arbitrage, and a peak/off-peak split that does not reblend, each check fires exactly on its failure and localises it, naming the month, the mispriced quarter, and the baseload miss. In production these are the gates between a corrupted curve and the books, and because each returns where it failed, the on-call engineer gets a pointer, not just a red light.

Publishing uncertainty and auditability

A production curve builder does not publish only prices. It publishes how much to trust each price, because a curve where February is a hard outright and one where February is inferred from a quarter and a seasonal prior are very different objects, even if both print the same number. Five artefacts, all cheap by-products of the fit, make the difference visible:

  • Confidence score. A per-month value in [0, 1]: how strongly a real quote pins that month versus how much it leans on the prior. Months with their own outright score near 1; months known only through a broad block and the prior score lower.
  • Number of inferred months. How many months have no outright quote and exist only because the fit inferred them, a fact a risk manager should see stamped on the curve.
  • Per-quote sensitivity. For every published point, how much it moves if each input quote moves by one unit, telling a trader instantly which mark their exposure really depends on.
  • Residual contribution. When the board is inconsistent and reconciled by weighted least squares, each quote’s residual says how much it was bent to reach agreement; the largest residual localises a stale mark.
  • Shape-prior influence. The complement of the confidence score, what fraction of each month’s value comes from the seasonal prior rather than the market. A curve whose summer months are 90% prior-driven is a curve whose summer view is your model’s, not the market’s.

On a worked board where only two months trade outright plus the four quarters, ten of the twelve months are inferred, months anchored by a nearby outright or a tight block score high (0.90-1.00), while months known only through a broad quarter score around 0.45. Publishing these vectors alongside the price is what makes the curve auditable: a trader can see, per month, how much of the number is market and how much is model.

When the board is inconsistent, the reconciliation spreads the disagreement across the quotes, and the residual vector, visualised as a heatmap, localises the culprit at a glance. A stale calendar strip shows a residual that dwarfs every other, immediately fingering it as the stale mark, turning "the curve looks off" into "the cal strip is stale, refresh it."

Curve sensitivities: how the curve moves when a quote moves

The constrained fit gives something a bumping approach would only approximate: the exact derivative of every curve point with respect to every quote, in closed form. This is the curve’s Jacobian, the natural bridge from construction to risk.

With prior b and constraints Af = q, the fitted curve is an affine function of the quote vector, so the sensitivity of the whole curve to the quotes is the constant matrix:

$$ J = \frac{\partial f}{\partial q} = A^{\top}\,(A A^{\top})^{-1} \qquad (\text{shape } n_{\text{months}} \times n_{\text{quotes}}) $$

Column j is the influence of quote j: how the entire curve shifts if product j’s quote rises by one unit. Because f is affine in q, this derivative is exact and global, no finite-difference bump, no step-size choice, valid for any move size, and it matches a finite-difference check to machine precision.

The individual numbers are readable desk facts. On a board where January trades outright inside Q1, marking Q1 up by one unit moves February and March up (dFeb/dQ1 ≈ 1.44, dMar/dQ1 ≈ 1.60) but not January (dJan/dQ1 = 0), because January has its own outright quote that pins it, so the whole quarter adjustment is absorbed by the two inferred months. The Feb and Mar sensitivities exceed 1 precisely because they alone must carry the quarter’s move.

This is the curve’s delta ladder: it tells the risk engine, for any book valued off the curve, how the book’s value flows back to the underlying market quotes. A row of the Jacobian is directly a hedging recipe, to hedge one unit of February (an illiquid month with no outright), buy 1.44 of the Q1 quarter and sell 0.50 of the January outright, isolating February by taking the quarter and backing out the January component. An inferred curve point becomes a hedgeable risk, straight out of the fit, no extra modelling.

Choosing a solver

The constrained fit can be solved several ways, and the "right" one depends on conditioning, size, sparsity, and whether you need to survive rank-deficiency. Six approaches, benchmarked on the identical problem, all agree to machine precision but differ where it counts:

  • KKT direct, factor the full symmetric indefinite system. The default: clear, robust, handles indefiniteness.
  • Normal equations, fastest when well-conditioned and quotes are few, but forming the system squares the condition number.
  • QR, factors the constraint matrix directly, avoiding the squaring; better stability cheaply.
  • SVD, most stable, reveals rank, the choice for rank-deficient or collinear boards, at a larger constant cost.
  • Interior-point / CG, matrix-vector only, the viable path once the system is large and sparse (an hourly curve) and will not fit densely.
  • Active-set, earns its keep only when inequality constraints (hard positivity, monotonicity) are added; with equalities alone it collapses to the KKT solve.

Two structural facts drive the choice. Forming the normal equations squares the condition number, so on a near-collinear board QR or SVD on the constraint matrix directly are safer. And the equality-only problem has a fixed active set, so a full active-set solver collapses to KKT here, active-set only pays off once you add inequality constraints such as hard positivity or monotonicity bounds.

The engineering rule: default to KKT for clarity, drop to Normal equations when you have proven the board is well-conditioned and need speed, and reach for QR/SVD the moment conditioning is marginal or rank-deficiency is possible. The engine encodes this as a policy, try the fast path, catch the failure, fall back to the stable solver, rather than paying for the safest solver on every curve.

Complexity and scaling: 12 months to 8,760 hours

A monthly curve is 12 points; a daily curve is 365; an hourly power curve is 8,760. The maths is identical, but the engineering is not, and a method that is instant on 12 points can be unusable on 8,760 if you ignore structure.

The expensive dimension of the constrained fit is the number of quotes (small, ~5-20), not the number of curve points. A reduction turns the full (n+m)×(n+m) KKT system into a tiny m×m system in the multipliers, on a 365-day curve that is a 116× speed-up, with the two agreeing to machine precision. Because the expensive dimension is always the small quote count, a daily or even hourly fit stays cheap as long as you exploit the reduction rather than factoring the giant block directly.

The smoothness solve is the other half. The interpolation and smoothness systems are tridiagonal or near-banded, so storing and solving them sparse is O(n) in time and memory:

text# Smoothness solve, dense vs sparse, three curve sizes
#   n=   12 (months): dense   0.037 ms | sparse 0.063 ms | speedup    1x
#   n=  365 (days)  : dense   2.094 ms | sparse 0.123 ms | speedup   17x
#   n= 8760 (hours) : dense 8821.4  ms | sparse 3.554 ms | speedup 2482x

At 12 months, dense is actually faster, the sparse machinery has overhead not worth paying, so a monthly curve should use a dense solve. At 8,760 hours, dense takes 8.8 seconds and sparse takes 3.6 milliseconds, a 2,482× speed-up, and the memory gap is decisive: the dense 8760×8760 matrix is 0.61 GB of float64, while the sparse tridiagonal is 0.21 MB. An hourly power curve simply cannot be built densely at scale; sparsity is not an optimisation, it is a prerequisite. The crossover rule: dense below a few hundred points, sparse above, and always sparse for anything hourly.

Production architecture: where the curve engine lives

The engine is one stage in a larger pricing-and-risk platform. The whole lifecycle on one line: market data → validation → cleaning → curve builder → arbitrage validation → diagnostics & confidence → versioning & approval → publication → valuation engine → risk engine, with a monitoring feedback loop watching every stage and triggering the next rebuild.

Two architectural principles matter most. Each stage has a single responsibility and a typed interface, so a change in interpolation policy cannot reach into market-data handling, and a new feed cannot silently alter the fit. And publication is a gate, not a step: a curve becomes visible to valuation only after diagnostics pass, and it is published immutable and versioned, so every downstream number can be traced to the exact curve, quotes, and config that produced it.

That traceability is the backbone of model governance: when risk asks "why did this book’s value move," the answer is a diff of two versioned curves and their quote boards. Putting the whole pipeline together, raw multi-source ticks in, a validated published curve out, is the difference between a script that builds a curve once and a service a trading floor depends on for years.

End to end on one realistic board: a duplicate broker quote is deduplicated in favour of the settlement, the constrained fit reprices every survivor exactly and infers the ten unquoted months, the arbitrage suite passes, and February publishes at a price no source quoted, produced by the whole pipeline and validated end to end. This is a curve engine as a system, not a fit.

Industrial testing

A production curve engine is tested far beyond "run it on an example and eyeball the output." Six kinds of test, each catching a different class of bug:

  • Unit tests pin specific numbers: interpolation matches the reference to machine precision; the fit reprices exactly; a known inferred month recovers to its expected value.
  • Property-based tests assert invariants across many random inputs, the most valuable kind for a numerical library. For interpolation the invariant is no overshoot: for any random monotone node set, a shape-preserving interpolant stays within the node range. Two thousand random monotone curves, zero overshoot violations, far stronger evidence than any single example.
  • Stress tests push extremes: a wildly ill-scaled board, a nearly rank-deficient board, a curve with a genuine price spike. The engine must handle them or fail loudly with a diagnostic, never silently produce a poisoned curve.
  • Monte-Carlo quote perturbation probes stability: perturb every quote by a small random amount many times and confirm the curve responds smoothly and never violates a hard check.
  • Benchmark tests track performance, so a refactor that quietly makes the solve 10× slower is caught.
  • Regression tests freeze a known-good curve for a fixed board and assert the engine still reproduces it bit-for-bit after any change, the safety net that lets you refactor the internals with confidence.

Together these turn "it worked when I tried it" into "it provably still works", the standard a front-office library is held to.

Monitoring: catching a curve that drifts in production

Testing proves the engine is correct before deployment; monitoring catches problems after, when live data does something the tests never imagined. The key production monitor is explained vs unexplained move: day over day, the curve should move only where quotes moved. A curve point that shifts with no corresponding quote change is a red flag, a config change, a data-feed glitch, or a bug, and should alert.

If Q1 quotes move and Jan/Feb/Mar move with them, that is fully explained, no alert. If no quote changed but June jumped, the monitor points straight at that month. In production this catches the overnight surprises: a prior quietly re-parameterised, a stale mark that dropped out, an interpolation-policy config that changed, before a trader discovers them by mispricing a book.

Other standard monitors: day-over-day curve distance (a large move triggers review), diagnostic drift (rising condition number or residual RMS over days signals a degrading feed), and confidence erosion (more months slipping into the inferred/low-confidence bucket as liquidity thins). Monitoring is trust made operational: a curve you do not watch is a curve you cannot trust.

Curve governance: publish versions, never overwrite

A published curve is a controlled artefact, not a mutable variable. The governing principle is simple and strict: a desk never overwrites a published curve; it publishes a new version. Yesterday’s curve, and this morning’s version, must remain exactly as they were, forever, because books were valued and risk was run against them and those numbers have to be reproducible at audit.

  • Version numbers. Every rebuild produces a new, monotonically increasing version. A downstream valuation references a specific version, so any P&L or risk number ties to the exact curve behind it.
  • Approval workflow. Publication is gated on sign-off. Within tolerance (all diagnostics green, move within thresholds) approval can be automated; an exception, a large unexplained move, a failed check, a low-confidence board, routes to a human curve owner.
  • Rollback. If a bad curve slips through, the desk rolls forward to the previous good version rather than editing the bad one in place, the bad version stays in the record, flagged, for the post-mortem.
  • Digital signatures. Each published version is signed (a hash of curve + board + config) so downstream consumers can verify they are using an authentic, untampered curve.
  • Audit trail. Every action, build, approve, publish, roll back, is logged with who/what/when, forming the immutable history a regulator or model-validation team will ask for.

Governance is not bureaucracy; it is what makes every downstream number defensible months later. When risk asks "why did this book move overnight," the answer is a diff of two versions, their quote boards, their configs, their curves, not an archaeology dig.

Determinism and reproducibility

Quantitative production systems live or die on determinism: the same inputs must always produce the same curve, bit for bit, or reproducibility and audit collapse. Given identical quotes, config, and code, the engine must return an identical curve every time, no dependence on hash-ordering, thread-scheduling, or uninitialised memory. Two builds of the same board being byte-for-byte identical is what lets the version hash be a meaningful signature and lets a regression test assert an exact match.

The precision disciplines that make this hold:

  • Floating-point tolerance, never equality. Every reprice and arbitrage check uses an absolute tolerance; recall that 0.1 + 0.2 does not equal 0.3 in floating point. "Reprices exactly" means "to ~1e-13," and the tolerance is a declared constant, not a magic number sprinkled through the code.
  • Conditioning thresholds. The engine has an explicit condition-number threshold above which it refuses the fast path and switches to SVD (or rejects the board and alerts). The threshold is a declared policy, so "how ill-conditioned is too ill-conditioned" is an auditable decision, not an accident of which solver was called.
  • Reproducibility across environments. Pinning library versions matters: different linear-algebra backends can reorder floating-point operations and change the last bits. A deterministic build pins its numerical stack and records the versions in the curve’s provenance, so a curve rebuilt months later on a different machine is still bit-comparable.

Determinism is not pedantry: it is the difference between "the curve was 86.76 because here is the exact board, config, and code that produced it" and "the curve was about 86.8, roughly, we think." Only the former survives an audit.

Real market products: conventions, not numbers

Credibility on a desk comes from handling the actual products, with their real units, calendars, and shaping. Two convention families must be handled before any maths touches the numbers.

Units, normalise to one internal unit on ingest. Gas alone spans p/therm (NBP), €/MWh (TTF), and \$/MMBtu (Henry Hub); power adds \$/MWh. Getting a unit wrong is not a rounding error, it is the ill-scaling that pushes the condition number from single digits to enormous and corrupts the whole solve. Normalise explicitly on ingest; never let a unit sneak into the constraint matrix.

Delivery granularity and shaping. Months nest into quarters, quarters into seasons (gas Summer = Apr-Sep, Winter = Oct-Mar) and cal-years; power adds peak/off-peak and, at the finest grain, weekend, gas-day, and power-hour products. Each is a day- or hour-weighted average constraint on the finer curve. The peak/off-peak split is a calendar computation, not a fixed fraction: under the common Mon-Fri 08-20 definition, January 2026 has 264 peak hours and 480 off-peak hours (weekends are entirely off-peak), so using a nominal fraction instead of the true hour count is a subtle, recurring mispricing.

US nodal power adds a whole dimension. European power quotes a single zonal price; US ISOs quote Locational Marginal Prices at thousands of nodes, and an LMP decomposes into three components:

$$ \text{LMP}_{\text{node}} = \underbrace{\text{energy}}_{\text{system-wide}} + \underbrace{\text{congestion}}_{\text{node-specific}} + \underbrace{\text{losses}}_{\text{node-specific}} $$

A liquid hub trades outright; an individual node trades as hub + basis. The engine builds a liquid hub curve with the full machinery, then adds a (usually sparser) basis curve per node, the same constrained-fit-with-a-prior problem one level down.

Failure case studies: how real curves break

Every rule in a production engine exists because a curve once broke. Each case follows the same anatomy, symptom → root cause → detection → fix → prevention, because that discipline is itself the lesson.

  • Cubic spline created negative gas prices. A downstream storage model booked "free" summer injection. Root cause: a natural cubic spline on a steep shoulder-month curve overshot below zero between quotes. Detection: the negative-forward check flagged a sub-zero month. Fix: switch to a shape-preserving interpolant, whose no-overshoot is a theorem. Prevention: make it the default and gate publication on the negative check.
  • Duplicate exchange quote caused rank deficiency. The solve returned a garbage curve intermittently. Root cause: the same product arrived from two feeds, adding an identical constraint row and making the system singular. Detection: the solver’s rank check triggered the SVD fallback and logged it. Fix: deduplicate in the market-data layer. Prevention: the dedup stage plus the solver’s rank guard, two independent defences.
  • Stale calendar strip distorted a storage valuation. A storage desk’s intrinsic value drifted overnight with no market move. Root cause: a cal-year strip mark was not refreshed after the quarters moved, so the board was internally inconsistent and the reconciliation bent the whole curve. Detection: the residual analyzer ranked the cal strip as the top stale-mark suspect. Fix: refresh the cal and refit off the quarters. Prevention: a pre-fit consistency check on every board.
  • Daylight-saving time broke hourly power aggregation. A power day’s baseload did not match the hourly build twice a year. Root cause: the day was assumed to be 24 hours; the spring-forward day has 23 and the autumn day 25. An £80 baseload on the 23-hour day misprices by about £3.48/MWh. Fix: compute hours from the actual DST-aware calendar. Prevention: a regression test on the two DST days every year.

The pattern across all of them is the thesis in miniature: the failure was a data, convention, or numerical issue, never the interpolation maths, and each was caught (or should have been) by a cheap diagnostic gate, not by staring at the fitted number.

Business impact: why curve quality is money

Interpolation choices feel academic until you trace them into valuation. The curve is the input to every commodity structure, and its shape, not just its level, drives value.

  • Storage intrinsic value. Storage earns the summer-winter spread; its intrinsic value is a function of the shape of the curve across the year. A cubic spline’s overshoot pushing the summer trough down can inflate the winter-summer spread by several percent, a spurious, material valuation manufactured purely by the interpolation artefact.
  • Swing option valuation. A swing contract’s extrinsic value comes from optionality around the curve; a curve with spurious kinks creates phantom take-more/take-less signals a naive valuation monetises as fake extrinsic value. A clean, kink-free curve is a prerequisite for a defensible swing price.
  • Spark-spread valuation. The spark spread is power minus a heat-rate multiple of gas; it depends on two curves and their alignment. If the gas and power curves are built with inconsistent conventions (one on a gas-day calendar, one on a power-hour calendar, mis-synchronised), the spread inherits a spurious basis that shows up as phantom P&L.

The through-line: every downstream valuation inherits the curve’s errors, and the errors that matter most are in the shape of the illiquid, inferred region, exactly the region this machinery, shape-preserving interpolation, prior-pinned inference, published confidence, is built to get right and to flag when it cannot.

A multi-commodity book is a graph of curves, and the spreads are only arbitrage-free if the curves are built consistently, same as-of instant, compatible conventions, shared calendars. The single-curve arbitrage suite grows a cross-curve layer: does the spark spread implied by the gas and power curves match the quoted spark? A phantom basis between two internally-perfect curves is still phantom P&L.

Publishing uncertainty: the research frontier

The constrained-least-squares-with-a-prior engine is the industry workhorse, but it is not the last word. A front-office quant is expected to know where the field is going.

  • Gaussian-process curve construction. Model the curve as a draw from a Gaussian process with a seasonal-plus-smooth kernel; condition on the quotes to get a posterior mean and variance. The variance is the prize: a principled, per-point uncertainty band that is small at quoted tenors and large in the illiquid gaps, the confidence score of the workhorse engine, but derived rather than heuristic.
  • Neural curve fitting. A network can learn a flexible curve family from history, capturing nonlinear seasonal and cross-commodity structure a fixed kernel misses, at the cost of interpretability and arbitrage control. Promising for shape priors, risky as the final fitter.
  • Dynamic factor models. Represent a whole panel of curves by a few latent factors evolving over time, excellent for co-movement and scenario generation; the daily fit still needs the constrained solve to hit today’s quotes exactly.
  • Kalman filtering. Treat the curve’s latent state as evolving and update it as new quotes arrive, blending yesterday’s curve against today’s marks, a time-dynamic version of the prior-plus-quotes idea.

A worked Gaussian-process fit conditions a periodic-times-smooth kernel on a few quoted outrights and predicts every month with a band. At the quoted months the uncertainty collapses to the mark-noise floor; in the gaps it widens; and beyond the last quote it fans out furthest, the model correctly becoming least certain where it is extrapolating past all data. That published band is the honest artefact the workhorse engine approximates with its confidence score: a curve that tells you, per point, how much to trust it.

Frequently asked questions

Why must the gaps between quoted points be filled at all?

Because a usable curve needs a price for every delivery date, often 365 or more, while the market only quotes a handful of points. Any position settling in an unquoted period still needs a price, so the gaps must be filled to value arbitrary dates.

Why is linear interpolation considered safe?

A linearly interpolated value always lies between its two neighbours, so if both neighbours are positive the value cannot be negative or exceed them. No overshoot is possible, which makes it bullet-proof against impossible prices, though it can under-read a genuinely convex curve.

What danger do smooth spline interpolations introduce?

Overshoot: a spline can curve below the lowest quote or above the highest between points, sometimes producing negative prices. This manufactures arbitrage the data never implied, which is why shape-preserving methods and a range-check are used.

Where do most production curve failures actually come from?

The market-data layer, before the fit: crossed or wide broker quotes, stale settlements, duplicates that make the constraint matrix rank-deficient, and time-synchronisation errors. A great fit on a bad board is still a bad curve.

What is the curve Jacobian used for?

It is the exact, closed-form sensitivity of every curve point to every quote, J = A^T (A A^T)^-1. It serves as the curve’s delta ladder for risk and directly gives hedging recipes that map an illiquid inferred month onto tradeable quoted products.

Why does an hourly curve require sparse linear algebra?

An 8,760-point dense matrix is about 0.61 GB and takes seconds to solve, while the sparse tridiagonal equivalent is 0.21 MB and solves in milliseconds, a 2,482x speed-up. On a desk rebuilding many hourly curves, the dense version neither fits in memory nor in time, so sparsity is a prerequisite, not an optimisation.

Keep exploring

See it on your own trades

The fastest way to understand this in practice is a working walkthrough mapped to your desk.

Request a demo Platform overview