Skip to main content
Glama
Ademscodeisnotsobad

Quant Companion MCP

getVolSmile.ts5.33 kB
/** * MCP Tool: get_vol_smile * * Fetches options chain and computes volatility smile for analysis. */ import { z } from "zod"; import { computeVolSmile, type VolSmile, type QuantError } from "@quant-companion/core"; import { getDefaultProvider } from "../marketData"; import { createError } from "../errors"; // Input schema export const getVolSmileSchema = z.object({ symbol: z.string().describe("Stock/ETF ticker symbol (e.g., AAPL, SPY, NVDA)"), expiration: z .string() .optional() .describe("Expiration date in ISO format (YYYY-MM-DD). If omitted, uses nearest expiration."), minOpenInterest: z .number() .min(0) .default(10) .describe("Minimum open interest to include a strike (default: 10)"), maxBidAskSpreadPct: z .number() .min(0) .max(1) .default(0.5) .describe("Maximum bid-ask spread as fraction of mid to include (default: 0.5 = 50%)"), useOtmOnly: z .boolean() .default(true) .describe("Use only OTM options for cleaner smile (default: true)"), }); export type GetVolSmileInput = z.infer<typeof getVolSmileSchema>; export interface GetVolSmileOutput { symbol: string; spot: number; expiration: string; timeToMaturityYears: number; atmIv: number; points: Array<{ strike: number; moneyness: number; logMoneyness: number; iv: number; optionType: "call" | "put"; bidAskSpreadPct: number; openInterest: number; }>; skew: { riskReversal25d: number | null; butterfly25d: number | null; simpleSkew: number | null; }; interpretation: { skewDirection: "put" | "call" | "neutral"; skewMagnitude: "steep" | "moderate" | "flat"; smileShape: "smile" | "smirk" | "flat"; }; } // Tool definition for MCP export const getVolSmileDefinition = { name: "get_vol_smile", description: `Fetch options chain and compute volatility smile (IV vs strike) for a symbol. Returns: - ATM implied volatility - Smile curve points (strike, moneyness, IV) - Skew metrics (risk reversal, butterfly, simple skew) - Interpretation (skew direction, magnitude, smile shape) Use this to analyze: - Is the skew steep (puts expensive vs calls)? - Are wings overpriced vs ATM? - Is the market pricing tail risk?`, inputSchema: { type: "object" as const, properties: { symbol: { type: "string", description: "Stock/ETF ticker symbol (e.g., AAPL, SPY, NVDA)", }, expiration: { type: "string", description: "Expiration date in ISO format (YYYY-MM-DD). If omitted, uses nearest expiration.", }, minOpenInterest: { type: "number", description: "Minimum open interest to include a strike (default: 10)", default: 10, }, maxBidAskSpreadPct: { type: "number", description: "Maximum bid-ask spread as fraction of mid (default: 0.5)", default: 0.5, }, useOtmOnly: { type: "boolean", description: "Use only OTM options for cleaner smile (default: true)", default: true, }, }, required: ["symbol"], }, }; /** * Interpret the smile metrics */ function interpretSmile(smile: VolSmile): GetVolSmileOutput["interpretation"] { const { riskReversal25d, butterfly25d } = smile.skew; // Skew direction: positive RR means puts are more expensive let skewDirection: "put" | "call" | "neutral" = "neutral"; if (riskReversal25d !== null) { if (riskReversal25d > 0.02) skewDirection = "put"; else if (riskReversal25d < -0.02) skewDirection = "call"; } // Skew magnitude let skewMagnitude: "steep" | "moderate" | "flat" = "flat"; if (riskReversal25d !== null) { const absRR = Math.abs(riskReversal25d); if (absRR > 0.08) skewMagnitude = "steep"; else if (absRR > 0.03) skewMagnitude = "moderate"; } // Smile shape: butterfly > 0 means wings elevated (smile), else smirk let smileShape: "smile" | "smirk" | "flat" = "flat"; if (butterfly25d !== null) { if (butterfly25d > 0.02) smileShape = "smile"; else if (butterfly25d < -0.01) smileShape = "smirk"; } return { skewDirection, skewMagnitude, smileShape }; } /** * Get vol smile handler */ export async function getVolSmile( input: GetVolSmileInput ): Promise<GetVolSmileOutput | { error: QuantError }> { const provider = getDefaultProvider(); try { // Fetch options chain const chain = await provider.getOptionsChain({ symbol: input.symbol.toUpperCase(), expiration: input.expiration, }); // Compute smile const smile = computeVolSmile({ chain, expiration: input.expiration, minOpenInterest: input.minOpenInterest, maxBidAskSpreadPct: input.maxBidAskSpreadPct, useOtmOnly: input.useOtmOnly, }); // Add interpretation const interpretation = interpretSmile(smile); return { symbol: smile.symbol, spot: smile.spot, expiration: smile.expiration, timeToMaturityYears: smile.timeToMaturityYears, atmIv: smile.atmIv, points: smile.points, skew: smile.skew, interpretation, }; } catch (error) { const message = error instanceof Error ? error.message : "Unknown error"; return { error: createError("DATA_NOT_FOUND", `Failed to compute vol smile: ${message}`) }; } }

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