spb/vquant Public MIT
VibeQuant — AI-powered institutional-grade financial intelligence platform.
TypeScript 84.3%
Python 11.7%
JavaScript 1.6%
CSS 1.5%
HTML 0.7%
1#!/usr/bin/env python32# =============================================================================3# VibeQuant (vquant) — AI-Powered Financial Intelligence Platform4# -----------------------------------------------------------------------------5# File: server/services/optionsPricingService.py6#7# Author: Simon-Pierre Boucher8# Contact: contact@spboucher.ai9# Website: https://www.spboucher.ai10# Demo: https://www.vquant.ai11# License: MIT (see LICENSE)12#13# Copyright © 2026 Simon-Pierre Boucher. All rights reserved.14# =============================================================================1516"""17Black-Scholes Options Pricing Model18Calculates theoretical option prices and Greeks for European options19Supports direct API integration for fetching real market data20"""2122import sys23import json24import math25import os26from scipy import stats2728# Import our FMP client29sys.path.insert(0, os.path.dirname(__file__))30from fmpClient import get_historical_prices, get_stock_quote, calculate_historical_volatility3132def black_scholes_call(S, K, T, r, sigma):33 """Calculate Black-Scholes call option price"""34 d1 = (math.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * math.sqrt(T))35 d2 = d1 - sigma * math.sqrt(T)3637 call_price = S * stats.norm.cdf(d1) - K * math.exp(-r * T) * stats.norm.cdf(d2)38 return call_price3940def black_scholes_put(S, K, T, r, sigma):41 """Calculate Black-Scholes put option price"""42 d1 = (math.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * math.sqrt(T))43 d2 = d1 - sigma * math.sqrt(T)4445 put_price = K * math.exp(-r * T) * stats.norm.cdf(-d2) - S * stats.norm.cdf(-d1)46 return put_price4748def calculate_greeks(S, K, T, r, sigma, option_type):49 """Calculate option Greeks: Delta, Gamma, Theta, Vega, Rho"""50 d1 = (math.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * math.sqrt(T))51 d2 = d1 - sigma * math.sqrt(T)5253 # Delta54 if option_type == "call":55 delta = stats.norm.cdf(d1)56 else: # put57 delta = stats.norm.cdf(d1) - 15859 # Gamma (same for call and put)60 gamma = stats.norm.pdf(d1) / (S * sigma * math.sqrt(T))6162 # Vega (same for call and put)63 vega = S * stats.norm.pdf(d1) * math.sqrt(T) / 100 # divided by 100 for 1% change6465 # Theta66 if option_type == "call":67 theta = (-(S * stats.norm.pdf(d1) * sigma) / (2 * math.sqrt(T))68 - r * K * math.exp(-r * T) * stats.norm.cdf(d2)) / 36569 else: # put70 theta = (-(S * stats.norm.pdf(d1) * sigma) / (2 * math.sqrt(T))71 + r * K * math.exp(-r * T) * stats.norm.cdf(-d2)) / 3657273 # Rho74 if option_type == "call":75 rho = K * T * math.exp(-r * T) * stats.norm.cdf(d2) / 100 # divided by 100 for 1% change76 else: # put77 rho = -K * T * math.exp(-r * T) * stats.norm.cdf(-d2) / 1007879 return {80 "delta": round(delta, 4),81 "gamma": round(gamma, 4),82 "theta": round(theta, 4),83 "vega": round(vega, 4),84 "rho": round(rho, 4)85 }8687def calculate_implied_volatility(option_price, S, K, T, r, option_type, max_iterations=100, tolerance=0.0001):88 """Calculate implied volatility using Newton-Raphson method"""89 sigma = 0.3 # Initial guess9091 for i in range(max_iterations):92 if option_type == "call":93 price = black_scholes_call(S, K, T, r, sigma)94 else:95 price = black_scholes_put(S, K, T, r, sigma)9697 # Vega for Newton-Raphson98 d1 = (math.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * math.sqrt(T))99 vega = S * stats.norm.pdf(d1) * math.sqrt(T)100101 if abs(vega) < 1e-10:102 break103104 diff = option_price - price105 if abs(diff) < tolerance:106 return round(sigma, 4)107108 sigma = sigma + diff / vega109110 # Ensure sigma stays positive111 if sigma <= 0:112 sigma = 0.01113114 return round(sigma, 4)115116def main():117 try:118 # Read input from stdin119 input_data = json.loads(sys.stdin.read())120121 # Check if symbol is provided (new API mode)122 symbol = input_data.get('symbol')123124 if symbol:125 # DIRECT API MODE - Fetch data from FMP126 print(f"Fetching market data for {symbol} from FMP API...", file=sys.stderr)127128 # Get current stock quote for current price129 quote = get_stock_quote(symbol)130 S = quote['price']131 print(f"Current stock price: ${S:.2f}", file=sys.stderr)132133 # Get historical prices to calculate volatility134 hist_data = get_historical_prices(symbol)135 closing_prices = [h['close'] for h in reversed(hist_data['historical'])]136137 # Calculate historical volatility (annualized)138 sigma_provided = input_data.get('volatility')139 if sigma_provided:140 sigma = sigma_provided141 print(f"Using provided volatility: {sigma*100:.2f}%", file=sys.stderr)142 else:143 # Use 30 days of history for volatility calculation144 recent_prices = closing_prices[-30:] if len(closing_prices) >= 30 else closing_prices145 sigma = calculate_historical_volatility(recent_prices, annualize=True)146 print(f"Calculated 30-day historical volatility: {sigma*100:.2f}%", file=sys.stderr)147148 # Extract other required parameters149 K = input_data.get('strike_price')150 T = input_data.get('time_to_maturity')151 r = input_data.get('risk_free_rate', 0.05)152 option_type = input_data.get('option_type', 'call').lower()153154 # Validate required inputs155 if K is None or T is None:156 raise ValueError("strike_price and time_to_maturity are required")157 else:158 # MANUAL MODE - Use provided parameters159 S = input_data.get('stock_price')160 K = input_data.get('strike_price')161 T = input_data.get('time_to_maturity')162 r = input_data.get('risk_free_rate', 0.05)163 sigma = input_data.get('volatility')164 option_type = input_data.get('option_type', 'call').lower()165166 # Validate inputs167 if S is None or K is None or T is None:168 raise ValueError("Either provide 'symbol' OR all of: stock_price, strike_price, time_to_maturity")169170 # If volatility not provided, use default171 if sigma is None:172 sigma = 0.3 # 30% default volatility173 print("Using default volatility: 30%", file=sys.stderr)174175 # Common validation for all modes176 if S <= 0 or K <= 0 or T <= 0:177 raise ValueError("stock_price, strike_price, and time_to_maturity must be positive")178179 if option_type not in ['call', 'put']:180 raise ValueError("option_type must be 'call' or 'put'")181182 if sigma <= 0:183 raise ValueError("volatility must be positive")184185 # Calculate option prices186 call_price = black_scholes_call(S, K, T, r, sigma)187 put_price = black_scholes_put(S, K, T, r, sigma)188189 # Calculate Greeks for both call and put190 call_greeks = calculate_greeks(S, K, T, r, sigma, "call")191 put_greeks = calculate_greeks(S, K, T, r, sigma, "put")192193 # Calculate intrinsic and time value194 call_intrinsic = max(0, S - K)195 put_intrinsic = max(0, K - S)196 call_time_value = call_price - call_intrinsic197 put_time_value = put_price - put_intrinsic198199 # Calculate moneyness200 if S > K:201 moneyness = "ITM" if option_type == "call" else "OTM"202 elif S < K:203 moneyness = "OTM" if option_type == "call" else "ITM"204 else:205 moneyness = "ATM"206207 # Prepare result208 result = {209 "success": True,210 "parameters": {211 "stock_price": round(S, 2),212 "strike_price": round(K, 2),213 "time_to_maturity": round(T, 4),214 "risk_free_rate": round(r, 4),215 "volatility": round(sigma, 4),216 "option_type": option_type217 },218 "call_option": {219 "price": round(call_price, 4),220 "intrinsic_value": round(call_intrinsic, 4),221 "time_value": round(call_time_value, 4),222 "greeks": call_greeks,223 "moneyness": "ITM" if S > K else ("ATM" if S == K else "OTM")224 },225 "put_option": {226 "price": round(put_price, 4),227 "intrinsic_value": round(put_intrinsic, 4),228 "time_value": round(put_time_value, 4),229 "greeks": put_greeks,230 "moneyness": "ITM" if S < K else ("ATM" if S == K else "OTM")231 },232 "parity_check": {233 "call_minus_put": round(call_price - put_price, 4),234 "stock_minus_pv_strike": round(S - K * math.exp(-r * T), 4),235 "parity_holds": bool(abs((call_price - put_price) - (S - K * math.exp(-r * T))) < 0.01)236 }237 }238239 # Output result as JSON240 print(json.dumps(result))241242 except Exception as e:243 error_result = {244 "success": False,245 "error": str(e)246 }247 print(json.dumps(error_result))248 sys.exit(1)249250if __name__ == "__main__":251 main()252