Skip to main content

Pricing engine

Computes the fair rate for a contract and signs it as a quote. It runs off-chain in the BlockFinaX service; the currency pool accepts a quote only with a valid signature from the pricing key set by set_pricing_key, then applies the pool's markup and fees on-chain. The methodology behind every function is in Pricing methodology.

Off-chain (pricing service) · server/pricing · View on GitHub

Pricing: what these functions compute and why is explained in Pricing methodology, each formula in the Formula handbook, and you can run them in the Interactive calculator.

All rates use the pair's quote currency per unit of base currency (for USD/GHS, cedis per dollar). Volatility, interest rates and drift are yearly decimals (0.12 is 12%). Time is in years.

Quotes​

quote​

READ
quote(req: QuoteRequest): Promise<QuoteResponse>
// QuoteRequest: { pair, strike, payoutCap, expiryUnixSeconds, notional, strikeAbove }

Who can call: the BlockFinaX service

Prices an Up cover (strikeAbove: true, a call spread) or Down cover (a put spread) between strike and payoutCap. Gathers the spot rate, USD rate, drift, volatility and jump settings in parallel, values the spread with Merton jump diffusion, and returns premiumRate (fraction of notional) with premiumRateFixed6 (millionths, the value signed into the quote) and a breakdown of every input. Results are cached for 60 seconds.

Refuses when: the premium rounds to zero at 6 decimals; maximum payout is over 100 times the premium; maximum payout is under 1.5 times the premium; the pair's price history is stale or sparse; the expiry is in the past.

signQuote​

ADMIN
signQuote(economic: EconomicQuote, buyer: string, poolId: number): Promise<ProtectionQuote>

Who can call: the pricing service, which holds the pricing key

Turns a priced quote into the parameters buy_protection verifies: pool, notional, tenor, structure, direction, spot, strike, limit and fair rate, plus max_cost, a 120-second deadline, a timestamp and a random nonce. Signs their digest with ed25519. The buyer's wallet submits the result.

Option formulas​

The models are explained step by step in the Formula handbook.

From server/pricing/kernel.ts. Pure functions, no I/O.

normalCDF​

READ
normalCDF(x: number): number

Standard normal distribution Φ(x)\Phi(x), using the Abramowitz and Stegun 7.1.26 approximation (accurate to about 7.5×10−87.5 \times 10^{-8}). See handbook chapter 10.

garmanKohlhagenCall​

READ
garmanKohlhagenCall({ S, K, T, r, q, sigma }): number
garmanKohlhagenPut({ S, K, T, r, q, sigma }): number

Currency option value per unit of base currency: Se−qTΦ(d1)−Ke−rTΦ(d2)S e^{-qT}\Phi(d_1) - K e^{-rT}\Phi(d_2) for a call, and the matching put. See handbook chapter 7.

mertonCall​

READ
mertonCall({ S, K, T, r, q, sigma, lambda, jumpMean, jumpStd, nTerms? }): number
mertonPut({ S, K, T, r, q, sigma, lambda, jumpMean, jumpStd, nTerms? }): number

Option value with jumps: the Garman-Kohlhagen value for each possible number of jumps, weighted by its Poisson chance and summed up to nTerms (default 50) or until the weight falls below 10−1510^{-15}. With lambda 0 it equals Garman-Kohlhagen. See handbook chapter 8.

mertonCallSpread​

READ
mertonCallSpread({ S, K_lower, K_upper, T, r, q, sigma, lambda, jumpMean, jumpStd }): number
mertonPutSpread({ S, K_lower, K_upper, T, r, q, sigma, lambda, jumpMean, jumpStd }): number

Value of the payout between two levels: mertonCall(K_lower) − mertonCall(K_upper) for an Up cover, mertonPut(K_upper) − mertonPut(K_lower) for a Down cover. callSpreadValue and putSpreadValue are the same without jumps. See handbook chapter 9.

Errors: K_upper must be > K_lower

premiumInBaseCurrency​

READ
premiumInBaseCurrency(perUnitValue: number, notional: number, S: number): number

Converts a spread value in quote currency per unit to a premium in USDC: notional × perUnitValue / S.

Market inputs​

Where each input comes from and how it changes the price: Pricing model, inputs.

getRate​

READ
getRate(pair: string): Promise<FXRate | null>

Today's rate for a pair from the market data feed (server/fx-oracle.ts), refreshed every 5 minutes. Returns null when the pair has no live rate, and quoting stops.

fetchUsdRate​

READ
fetchUsdRate(): Promise<number>

USD risk-free rate rr: the latest average US Treasury bill rate from Treasury Direct, as a decimal, cached for 6 hours.

computeEmpiricalDrift​

READ
computeEmpiricalDrift(pair: string): Promise<DriftResult>

The pair's yearly trend μ=ln⁡(Stoday/SY years ago)/Y\mu = \ln(S_{\text{today}} / S_{Y \text{ years ago}}) / Y over up to five years of daily closes (at least three), capped at ±50% a year. The quote uses q=r−μq = r - \mu. See handbook chapter 5.

getPricingVol​

READ
getPricingVol(pair: string): Promise<VolResult>

Today's volatility σ0\sigma_0: exponentially weighted (λ\lambda = 0.94) over the last 60 daily returns, yearly, raised by the market type's margin, held above its minimum, kept between 5% and 300%. Refuses stale or sparse history. See handbook chapter 3.

Market typeMarginMinimum
Major currencies12.5%5%
Major emerging25%5%
Less liquid emerging40%5%
Frontier75%15%
Pegged150%30%

integratedVariance​

READ
integratedVariance({ sigma0, sigmaLongRun, TYears, persistence? }): { varianceIntegrated, sigmaIntegrated, decay }
longRunVolFromCloses(closes: number[]): number

The average volatility over the contract's life: σL2+w(σ02−σL2)\sigma_L^2 + w(\sigma_0^2 - \sigma_L^2) with w=(1−pN)/(N(1−p))w = (1-p^N)/(N(1-p)) and NN = trading days to expiry. longRunVolFromCloses gives σL\sigma_L from five years of closes. Persistence is fitted per pair where available, otherwise the market type's default (frontier 0.93). See handbook chapter 4.

getPairJumpParams​

READ
getPairJumpParams(pair: string): Promise<{ lambda, jumpMean, jumpStd }>

Jump settings for the pair's market type, unless set for the pair itself.

Market typeJumps a yearAverage jumpJump spread
Major currencies0
Major emerging0.52%8%
Less liquid emerging1.04%10%
Frontier2.08%12%
Pegged0.120%5%

Configuration​

limits​

READ
SettingValue
Most payout per unit of premium100
Least payout per unit of premium1.5
Quote cache60 seconds
Signed quote valid for120 seconds
Volatility window61 daily closes (60 returns), newest at most 8 days old
Volatility bounds5% to 300%
Drift window and cap3 to 5 years, ±50% a year
Fair rate precisionmillionths of notional