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
READquote(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
ADMINsignQuote(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
READnormalCDF(x: number): number
Standard normal distribution , using the Abramowitz and Stegun 7.1.26 approximation (accurate to about ). See handbook chapter 10.
garmanKohlhagenCall
READgarmanKohlhagenCall({ S, K, T, r, q, sigma }): number
garmanKohlhagenPut({ S, K, T, r, q, sigma }): number
Currency option value per unit of base currency: for a call, and the matching put. See handbook chapter 7.
mertonCall
READmertonCall({ 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 . With lambda 0 it equals Garman-Kohlhagen. See handbook chapter 8.
mertonCallSpread
READmertonCallSpread({ 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
READpremiumInBaseCurrency(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
READgetRate(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
READfetchUsdRate(): Promise<number>
USD risk-free rate : the latest average US Treasury bill rate from Treasury Direct, as a decimal, cached for 6 hours.
computeEmpiricalDrift
READcomputeEmpiricalDrift(pair: string): Promise<DriftResult>
The pair's yearly trend over up to five years of daily closes (at least three), capped at ±50% a year. The quote uses . See handbook chapter 5.
getPricingVol
READgetPricingVol(pair: string): Promise<VolResult>
Today's volatility : exponentially weighted ( = 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 type | Margin | Minimum |
|---|---|---|
| Major currencies | 12.5% | 5% |
| Major emerging | 25% | 5% |
| Less liquid emerging | 40% | 5% |
| Frontier | 75% | 15% |
| Pegged | 150% | 30% |
integratedVariance
READintegratedVariance({ sigma0, sigmaLongRun, TYears, persistence? }): { varianceIntegrated, sigmaIntegrated, decay }
longRunVolFromCloses(closes: number[]): number
The average volatility over the contract's life: with and = trading days to expiry. longRunVolFromCloses gives 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
READgetPairJumpParams(pair: string): Promise<{ lambda, jumpMean, jumpStd }>
Jump settings for the pair's market type, unless set for the pair itself.
| Market type | Jumps a year | Average jump | Jump spread |
|---|---|---|---|
| Major currencies | 0 | ||
| Major emerging | 0.5 | 2% | 8% |
| Less liquid emerging | 1.0 | 4% | 10% |
| Frontier | 2.0 | 8% | 12% |
| Pegged | 0.1 | 20% | 5% |
Configuration
limits
READ| Setting | Value |
|---|---|
| Most payout per unit of premium | 100 |
| Least payout per unit of premium | 1.5 |
| Quote cache | 60 seconds |
| Signed quote valid for | 120 seconds |
| Volatility window | 61 daily closes (60 returns), newest at most 8 days old |
| Volatility bounds | 5% to 300% |
| Drift window and cap | 3 to 5 years, ±50% a year |
| Fair rate precision | millionths of notional |