Skip to main content
Glama
Ademscodeisnotsobad

Quant Companion MCP

sabr.test.ts4.1 kB
/** * Tests for SABR model */ import { describe, it, expect } from "vitest"; import { sabrImpliedVol, calibrateSabrSmile, buildSabrSmile } from "./sabr"; describe("sabrImpliedVol", () => { const baseParams = { forward: 100, timeToMaturityYears: 1, alpha: 0.2, beta: 1.0, rho: -0.3, nu: 0.4, }; it("returns ATM vol at strike = forward", () => { const vol = sabrImpliedVol({ ...baseParams, strike: 100 }); expect(vol).toBeCloseTo(0.2, 1); // Should be close to alpha for beta=1 }); it("produces skew with negative rho", () => { const otmPutVol = sabrImpliedVol({ ...baseParams, strike: 90 }); const otmCallVol = sabrImpliedVol({ ...baseParams, strike: 110 }); // With rho < 0, OTM puts should have higher IV than OTM calls expect(otmPutVol).toBeGreaterThan(otmCallVol); }); it("produces smile with positive nu", () => { const atmVol = sabrImpliedVol({ ...baseParams, strike: 100 }); const otmPutVol = sabrImpliedVol({ ...baseParams, strike: 85 }); const otmCallVol = sabrImpliedVol({ ...baseParams, strike: 115 }); // Wings should be elevated relative to ATM (smile shape) expect(otmPutVol).toBeGreaterThan(atmVol * 0.95); expect(otmCallVol).toBeGreaterThan(atmVol * 0.85); }); it("handles zero time to maturity", () => { const vol = sabrImpliedVol({ ...baseParams, strike: 100, timeToMaturityYears: 0 }); expect(vol).toBe(baseParams.alpha); }); it("returns positive vol for various strikes", () => { const strikes = [70, 80, 90, 100, 110, 120, 130]; for (const strike of strikes) { const vol = sabrImpliedVol({ ...baseParams, strike }); expect(vol).toBeGreaterThan(0); expect(vol).toBeLessThan(2); // Reasonable upper bound } }); }); describe("buildSabrSmile", () => { it("builds consistent smile curve", () => { const params = { forward: 100, timeToMaturityYears: 0.5, alpha: 0.25, beta: 0.5, rho: -0.25, nu: 0.5, }; const strikes = [80, 90, 100, 110, 120]; const vols = buildSabrSmile(params, strikes); expect(vols.length).toBe(strikes.length); vols.forEach(v => { expect(v).toBeGreaterThan(0); expect(v).toBeLessThan(2); }); }); }); describe("calibrateSabrSmile", () => { it("recovers known parameters from synthetic smile", () => { // Generate synthetic smile from known SABR params const trueParams = { forward: 100, timeToMaturityYears: 0.5, alpha: 0.22, beta: 1.0, rho: -0.35, nu: 0.45, }; const strikes = [85, 90, 95, 100, 105, 110, 115]; const marketIvs = strikes.map(strike => sabrImpliedVol({ ...trueParams, strike }) ); // Calibrate const result = calibrateSabrSmile({ forward: trueParams.forward, timeToMaturityYears: trueParams.timeToMaturityYears, strikes, marketIvs, beta: 1.0, // Fix beta }); // Check fit quality expect(result.error).toBeLessThan(0.01); // RMSE < 1% // Check recovered parameters are reasonable expect(result.alpha).toBeGreaterThan(0.1); expect(result.alpha).toBeLessThan(0.5); expect(result.rho).toBeGreaterThan(-1); expect(result.rho).toBeLessThan(1); expect(result.nu).toBeGreaterThan(0); expect(result.nu).toBeLessThan(2); }); it("handles flat smile", () => { const strikes = [90, 95, 100, 105, 110]; const flatVol = 0.20; const marketIvs = strikes.map(() => flatVol); const result = calibrateSabrSmile({ forward: 100, timeToMaturityYears: 1, strikes, marketIvs, beta: 1.0, }); // For flat smile, nu should be relatively small and fit should be good expect(result.nu).toBeLessThan(0.5); expect(result.error).toBeLessThan(0.02); }); it("throws for insufficient data", () => { expect(() => calibrateSabrSmile({ forward: 100, timeToMaturityYears: 1, strikes: [100, 105], marketIvs: [0.2, 0.21], }) ).toThrow(); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Ademscodeisnotsobad/Quant-Companion-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server