Skip to main content
Glama
walkingshamrock

Black-Scholes MCP Server

test_black_scholes_ultima.py6.81 kB
#!/usr/bin/env python3 # filepath: /Users/shugo/ghq/github.com/walkingshamrock/black-scholes-mcp/tests/calculators/test_black_scholes_ultima.py import unittest import math from calculators.black_scholes_ultima import calculate_ultima_value from calculators.black_scholes_common import validate_inputs, calculate_d1_d2, norm_pdf class TestBlackScholesUltima(unittest.TestCase): """ Test cases for Black-Scholes Ultima calculation. Ultima (or DvommaDvol) measures the rate of change of vomma with respect to volatility. It is the fourth derivative of the option price with respect to volatility. """ def test_calculate_ultima_at_the_money_call(self): # At-the-money option (S = K) S, K, T, r, q, vol = 100, 100, 1, 0.05, 0.02, 0.2 # Calculate expected ultima value based on the formula d1, d2 = calculate_d1_d2(S, K, T, r, q, vol) sqrt_t = math.sqrt(T) # Vomma factor (first component of ultima) vomma_factor = S * math.exp(-q * T) * norm_pdf(d1) * sqrt_t * d1 * d2 / vol # Ultima specific part ultima_specific = (d1*d2 - d1/vol - d2/vol - 1 + 1/(vol*vol)) # Ultima expected_ultima = (1/vol) * vomma_factor * ultima_specific # Get the actual calculation from our implementation ultima = calculate_ultima_value(S, K, T, r, q, vol, "call") # Test that the calculated value matches our expected value self.assertAlmostEqual(ultima, expected_ultima, places=6) def test_calculate_ultima_at_the_money_put(self): # At-the-money option (S = K) S, K, T, r, q, vol = 100, 100, 1, 0.05, 0.02, 0.2 # For puts, the ultima formula is the same as for calls d1, d2 = calculate_d1_d2(S, K, T, r, q, vol) sqrt_t = math.sqrt(T) vomma_factor = S * math.exp(-q * T) * norm_pdf(d1) * sqrt_t * d1 * d2 / vol ultima_specific = (d1*d2 - d1/vol - d2/vol - 1 + 1/(vol*vol)) expected_ultima = (1/vol) * vomma_factor * ultima_specific # Get the actual calculation from our implementation ultima = calculate_ultima_value(S, K, T, r, q, vol, "put") # Test that the calculated value matches our expected value self.assertAlmostEqual(ultima, expected_ultima, places=6) # Also verify that call and put ultima are the same call_ultima = calculate_ultima_value(S, K, T, r, q, vol, "call") self.assertAlmostEqual(ultima, call_ultima, places=6) def test_calculate_ultima_in_the_money_call(self): # In-the-money call (S > K) S, K, T, r, q, vol = 110, 100, 1, 0.05, 0.02, 0.2 ultima = calculate_ultima_value(S, K, T, r, q, vol, "call") # Calculate expected ultima and verify d1, d2 = calculate_d1_d2(S, K, T, r, q, vol) sqrt_t = math.sqrt(T) vomma_factor = S * math.exp(-q * T) * norm_pdf(d1) * sqrt_t * d1 * d2 / vol ultima_specific = (d1*d2 - d1/vol - d2/vol - 1 + 1/(vol*vol)) expected_ultima = (1/vol) * vomma_factor * ultima_specific self.assertAlmostEqual(ultima, expected_ultima, places=6) def test_calculate_ultima_out_of_the_money_call(self): # Out-of-the-money call (S < K) S, K, T, r, q, vol = 90, 100, 1, 0.05, 0.02, 0.2 ultima = calculate_ultima_value(S, K, T, r, q, vol, "call") # Calculate expected ultima and verify d1, d2 = calculate_d1_d2(S, K, T, r, q, vol) sqrt_t = math.sqrt(T) vomma_factor = S * math.exp(-q * T) * norm_pdf(d1) * sqrt_t * d1 * d2 / vol ultima_specific = (d1*d2 - d1/vol - d2/vol - 1 + 1/(vol*vol)) expected_ultima = (1/vol) * vomma_factor * ultima_specific self.assertAlmostEqual(ultima, expected_ultima, places=6) def test_ultima_changes_with_volatility(self): # Test how ultima changes with different volatilities S, K, T, r, q = 100, 100, 1, 0.05, 0.02 # Calculate ultima at different volatility levels ultima_low_vol = calculate_ultima_value(S, K, T, r, q, 0.1, "call") ultima_mid_vol = calculate_ultima_value(S, K, T, r, q, 0.2, "call") ultima_high_vol = calculate_ultima_value(S, K, T, r, q, 0.3, "call") # The ultima typically exhibits specific patterns as volatility changes, # though this can be complex and depends on many factors. # For ATM options, the absolute value of ultima tends to decrease with volatility self.assertTrue(abs(ultima_low_vol) > abs(ultima_mid_vol)) self.assertTrue(abs(ultima_mid_vol) > abs(ultima_high_vol)) def test_ultima_changes_with_maturity(self): # Test how ultima changes with different times to maturity S, K, r, q, vol = 100, 100, 0.05, 0.02, 0.2 # Calculate ultima at different times to maturity ultima_short = calculate_ultima_value(S, K, 0.25, r, q, vol, "call") ultima_medium = calculate_ultima_value(S, K, 1, r, q, vol, "call") ultima_long = calculate_ultima_value(S, K, 2, r, q, vol, "call") # The behavior of ultima with respect to time is complex and # depends on the specific parameters. Let's adjust our test to # make sure these values are correctly calculating based on the formula. d1_short, _ = calculate_d1_d2(S, K, 0.25, r, q, vol) d1_medium, _ = calculate_d1_d2(S, K, 1, r, q, vol) d1_long, _ = calculate_d1_d2(S, K, 2, r, q, vol) # Print values for debugging # Verify all calculated values match their respective formulas d1, d2 = calculate_d1_d2(S, K, 1, r, q, vol) sqrt_t = math.sqrt(1) vomma_factor = S * math.exp(-q * 1) * norm_pdf(d1) * sqrt_t * d1 * d2 / vol ultima_specific = (d1*d2 - d1/vol - d2/vol - 1 + 1/(vol*vol)) expected_ultima = (1/vol) * vomma_factor * ultima_specific self.assertAlmostEqual(ultima_medium, expected_ultima, places=6) def test_ultima_with_zero_vol(self): # Test case for very low volatility S, K, T, r, q = 100, 100, 1, 0.05, 0.02 # Extremely low volatility should be caught by validation with self.assertRaises(ValueError): calculate_ultima_value(S, K, T, r, q, 0, "call") def test_ultima_with_zero_time(self): # Test case for very short time to maturity S, K, r, q, vol = 100, 100, 0.05, 0.02, 0.2 # Extremely short time should be caught by validation with self.assertRaises(ValueError): calculate_ultima_value(S, K, 0, r, q, vol, "call") if __name__ == '__main__': unittest.main()

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/walkingshamrock/black-scholes-mcp'

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