Visualizing FX Options: From Yield Curves to 3D Volatility Surfaces

CrazyTomato1 pts0 comments

From Yield Curves to 3D Vol Surfaces: A Practical Guide to FX Options Visualization | by DolphinDB | Apr, 2026 | MediumSitemapOpen in appSign up<br>Sign in

Medium Logo

Get app<br>Write

Search

Sign up<br>Sign in

From Yield Curves to 3D Vol Surfaces: A Practical Guide to FX Options Visualization

DolphinDB

6 min read·<br>Apr 22, 2026

Listen

Share

Press enter or click to view image in full size

FX options, volatility smiles, term structures — how do you turn complex financial data into something you can actually read? In this article, we walk through DolphinDB’s built-in plot() function using an FX option volatility surface as a running example, showing you how to build professional 2D and 3D charts with mouse-driven rotation, zoom, and hover tooltips.<br>1. Core Parameters of the plot() Function<br>DolphinDB’s plot() is a lightweight yet powerful charting function. No third-party libraries required — it takes you straight from data to visualization entirely within the database.<br>plot(data, [labels], title, chartType, [stacking], [extras])<br>2. Setting Up the Data: USDCNY FX Option Volatility Surface<br>Before we can plot anything, we need a volatility surface to work with. We’ll build one using DolphinDB’s built-in fxVolatilitySurfaceBuilder. The inputs are option quotes across 13 tenors for five quote types — ATM, D25_RR, D25_BF, D10_RR, D10_BF — which gives us enough resolution for smooth charts.<br>refDate = 2025.08.18<br>ccyPair = "USDCNY"<br>spot = 7.1627<br>quoteTerms = ["1d","1w","2w","3w","1M","2M","3M","6M","9M","1y","18M","2y","3y"]<br>quoteNames = ["ATM","D25_RR","D25_BF","D10_RR","D10_BF"]<br>rawQuotes = [<br>0.030000,-0.007500, 0.003500,-0.010000, 0.005500,<br>...(13×5 = 65 values in total)<br>0.044750, 0.006250, 0.003400, 0.009000, 0.008550]<br>quotes = reshape(rawQuotes, 5:13).transpose() // → 13-row × 5-column matrix<br>// Build interest rate curves and volatility surface<br>domesticCurve = parseMktData(domesticCurveInfo) // CNY IrYieldCurve<br>foreignCurve = parseMktData(foreignCurveInfo) // USD IrYieldCurve<br>surf = fxVolatilitySurfaceBuilder(<br>refDate, ccyPair, quoteNames, quoteTerms, quotes, spot,<br>domesticCurve, foreignCurve, "SVI")With the surface ready, we can now start plotting!<br>Example 1: 2D Line Chart (LINE) — CNY vs USD Interest Rate Curves<br>Many quant strategies require monitoring both CNY and USD interest rate levels simultaneously. The catch is that CNY and USD rates often sit at very different absolute levels — plot them on a shared Y-axis and the smaller series gets squashed into a flat line, making it unreadable.<br>plot() solves this with dual Y-axes, letting both curves display clearly on the same chart.<br>In this example, we use curvePredict to sample rate values at 100 evenly-spaced dates from the vol surface object we built earlier, then plot both curves as smooth lines.<br>// Prepare date array: 100 evenly-spaced points from 1M to 3Y<br>startDate = curveDates[4] // start at 1M, skipping short-end noise<br>endDate = curveDates[12] // 3Y<br>nPts = 100<br>span = endDate - startDate<br>dates100 = array(DATE, nPts)<br>for (i in 0:nPts) { dates100[i] = startDate + int(i * span / (nPts - 1.0)) }

cnyDense = curvePredict(domesticCurve, dates100) * 100.0<br>usdDense = curvePredict(foreignCurve, dates100) * 100.0

// Build matrix & assign labels<br>labels100 = array(STRING, nPts)<br>for (i in 0:nPts) { labels100[i] = string(dates100[i]) }\

mCurves = matrix(cnyDense, usdDense) // 100-row × 2-col<br>mCurves.rename!(labels100, ["CNY (%)", "USD (%)"])<br>// ↑ X-axis ↑ legend labels

// Plot<br>plot(mCurves, ,<br>["CNY vs USD Interest Rate Curves", "Date", "Rate (%)"],<br>LINE, false, { multiYAxes: true, autoScaleYAxes: true })Press enter or click to view image in full size

Example 2: 3D Surface Chart (SURFACE) — Volatility Surface by Strike<br>Implied volatility in FX options isn’t flat — it varies across both expiry and strike, forming a complex surface. A 3D surface chart makes this easy to read at a glance: you can immediately see where vol is elevated and where it’s suppressed.<br>In this example, we use optionVolPredict to query 630 IV values across a 30-date × 21-strike grid, then render them as an interactive 3D surface.<br>// Data preparation<br>nK = 21; nT = 30<br>denseStrikes = globalKmin + ... * double(0..(nK - 1)) // 21 evenly-spaced strikes<br>denseDates = ... // 30 evenly-spaced dates<br>volMat = optionVolPredict(surf, denseDates, denseStrikes) * 100.0 // 30×21 matrix<br>volMat.rename!(dateLabels, strikeLabels)<br>// ↑ Y-axis (row labels) ↑ X-axis (col labels)<br>// Plot<br>plot(volMat, ,<br>["USDCNY Volatility Surface (Strike, 30×21)", "Strike", "Maturity Date", "Implied Vol (%)"],<br>SURFACE)Press enter or click to view image in full size

Example 3: 3D Surface Chart (SURFACE) — Volatility Surface by Delta<br>FX options traders often prefer to look at volatility through the lens of Delta — the option’s sensitivity to the spot rate — rather than strike. The reason is practical: ATM strikes differ across tenors, so using Delta provides a standardized basis for comparison.<br>Before plotting, we need to build a 9-point...

surface plot volatility curves rate options

Related Articles