diff --git a/notebooks/code_samples/capital_markets/quickstart_option_pricing_models.ipynb b/notebooks/code_samples/capital_markets/quickstart_option_pricing_models.ipynb
index 39ed384b4..c1b70c85c 100644
--- a/notebooks/code_samples/capital_markets/quickstart_option_pricing_models.ipynb
+++ b/notebooks/code_samples/capital_markets/quickstart_option_pricing_models.ipynb
@@ -35,15 +35,14 @@
" - [Preview the documentation template](#toc4_1_) \n",
"- [Model development](#toc5_) \n",
"- [Data preparation](#toc6_) \n",
- "- [Model development](#toc5_) \n",
- "- [Data preparation](#toc6_) \n",
" - [Synthetic data generation](#toc6_1_)\n",
" - [Data quality](#toc6_2_)\n",
" - [Model Calibration](#toc6_3_) \n",
"- [Model Evaluation](#toc7_) \n",
" - [Benchmark Testing](#toc7_1_) \n",
- " - [Sensitivity Testing](#toc7_2_) \n",
- " - [Stress Testing](#toc7_3_) \n",
+ " - [Sensitivity Testing](#toc7_2_) \n",
+ " - [Greeks](#toc7_3_) \n",
+ " - [Stress Testing](#toc7_4_) \n",
"- [Next steps](#toc8_) \n",
" - [Work with your model documentation](#toc8_1_) \n",
" - [Discover more learning resources](#toc8_2_) \n",
@@ -936,6 +935,7 @@
"import random\n",
"\n",
"def process_results(result: TestResult):\n",
+ "\n",
" # Convert to DataFrame\n",
" df = pd.DataFrame(result.tables[0].data)\n",
" \n",
@@ -982,6 +982,35 @@
"This test is crucial for understanding how variations in strike prices affect the valuation of financial derivatives, particularly options."
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "bb8f1cab",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "result = run_test(\n",
+ " \"my_custom_tests.Sensitivity:S0\",\n",
+ " param_grid={\n",
+ " \"model_type\": ['SV'],\n",
+ " \"N\": [N],\n",
+ " \"M\": [M],\n",
+ " \"strike\":[strike_range[0]],\n",
+ " \"barrier\": [barrier_range[0]],\n",
+ " \"S0\": list(np.linspace(S0-20, S0+20, 20)),\n",
+ " \"T\": [T],\n",
+ " \"r\": [r],\n",
+ " \"v0\": [0.2],\n",
+ " \"kappa\": [2],\n",
+ " \"theta\": [0.2],\n",
+ " \"xi\": [0.1],\n",
+ " \"rho\": [-0.5],\n",
+ " },\n",
+ " post_process_fn= process_results\n",
+ ")\n",
+ "result.log()"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -1052,16 +1081,413 @@
},
{
"cell_type": "markdown",
- "id": "14fd2cd8",
+ "id": "35d6d3cc",
"metadata": {},
"source": [
"\n",
+ "#### Greeks \n",
+ "These Greeks are crucial for traders and risk managers as they provide insights into the risk and potential price movements of options and derivatives, allowing for more informed decision-making and risk management strategies."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "07cd8d2d",
+ "metadata": {},
+ "source": [
+ "### Delta\n",
+ "Let's measures the sensitivity of the option's price to a change in the price of the underlying asset. It indicates how much the price of an option is expected to move per $1 change in the underlying asset's price."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 28,
+ "id": "31befc58",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "@vm.test(\"my_custom_tests.GreeksDelta\")\n",
+ "def calculate_delta(model_type, S0, T, r, N, M, strike=None, barrier=None, \n",
+ " sigma=None, v0=None, kappa=None, theta=None, xi=None, rho=None, \n",
+ " h=0.001): # h is the step size for finite difference\n",
+ " \"\"\"\n",
+ " Calculate delta using finite difference method.\n",
+ " Delta = (V(S0 + h) - V(S0 - h)) / (2h)\n",
+ " where V is the option price and h is a small increment\n",
+ " \"\"\"\n",
+ " # Initialize the model with S0 + h\n",
+ " if model_type == 'BS':\n",
+ " model_up = BlackScholesModel(S0 + h, strike, T, r, sigma)\n",
+ " model_down = BlackScholesModel(S0 - h, strike, T, r, sigma)\n",
+ " else:\n",
+ " model_up = StochasticVolatilityModel(S0 + h, strike, T, r, v0, kappa, theta, xi, rho)\n",
+ " model_down = StochasticVolatilityModel(S0 - h, strike, T, r, v0, kappa, theta, xi, rho)\n",
+ " \n",
+ "\n",
+ " # Calculate option prices for up and down moves\n",
+ " knockout_up = KnockoutOption(model_up, S0 + h, strike, T, r, barrier)\n",
+ " knockout_down = KnockoutOption(model_down, S0 - h, strike, T, r, barrier)\n",
+ " \n",
+ " price_up = knockout_up.price_knockout_option(N, M)\n",
+ " price_down = knockout_down.price_knockout_option(N, M)\n",
+ " \n",
+ " # Calculate delta using central difference\n",
+ " delta = (price_up - price_down) / (2 * h)\n",
+ " df = pd.DataFrame({\"Delta\": [delta], \"Price_Up\": [price_up], \"Price_Down\": [price_down], \"h\": [h]})\n",
+ " return df\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "a4305e12",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# To analyze delta sensitivity to underlying price changes\n",
+ "result = run_test(\n",
+ " \"my_custom_tests.GreeksDelta\",\n",
+ " param_grid={\n",
+ " \"model_type\": ['SV'],\n",
+ " \"N\": [1000000],\n",
+ " \"M\": [M],\n",
+ " \"strike\":[strike_range[0]],\n",
+ " \"barrier\": [barrier_range[0]],\n",
+ " \"S0\": list(np.linspace(S0-20, S0+20, 20)),\n",
+ " \"T\": [T],\n",
+ " \"r\": [r],\n",
+ " \"v0\": [0.2],\n",
+ " \"kappa\": [2],\n",
+ " \"theta\": [0.2],\n",
+ " \"xi\": [0.1],\n",
+ " \"rho\": [-0.5],\n",
+ " \"h\": [0.001]\n",
+ " },\n",
+ "post_process_fn=process_results # Using the plotting function defined earlier\n",
+ ")\n",
+ "result.log()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "27beb54f",
+ "metadata": {},
+ "source": [
+ "### Gamma\n",
+ "Let's measures the rate of change of Delta with respect to changes in the underlying asset's price. It indicates the curvature of the option's price relative to the underlying asset's price."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "b1878011",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "@vm.test(\"my_custom_tests.GreeksGamma\")\n",
+ "def calculate_gamma(model_type, S0, T, r, N, M, strike=None, barrier=None, \n",
+ " sigma=None, v0=None, kappa=None, theta=None, xi=None, rho=None, \n",
+ " h=0.01): # h is the step size for finite difference\n",
+ " \"\"\"\n",
+ " Calculate gamma using finite difference method.\n",
+ " Gamma = (V(S0 + h) - 2V(S0) + V(S0 - h)) / h^2\n",
+ " where V is the option price and h is a small increment\n",
+ " \"\"\"\n",
+ " # Initialize the models with S0 + h, S0, and S0 - h\n",
+ " if model_type == 'BS':\n",
+ " model_up = BlackScholesModel(S0 + h, strike, T, r, sigma)\n",
+ " model_center = BlackScholesModel(S0, strike, T, r, sigma)\n",
+ " model_down = BlackScholesModel(S0 - h, strike, T, r, sigma)\n",
+ " else:\n",
+ " model_up = StochasticVolatilityModel(S0 + h, strike, T, r, v0, kappa, theta, xi, rho)\n",
+ " model_center = StochasticVolatilityModel(S0, strike, T, r, v0, kappa, theta, xi, rho)\n",
+ " model_down = StochasticVolatilityModel(S0 - h, strike, T, r, v0, kappa, theta, xi, rho)\n",
+ " \n",
+ " # Calculate option prices for up, center, and down moves\n",
+ " knockout_up = KnockoutOption(model_up, S0 + h, strike, T, r, barrier)\n",
+ " knockout_center = KnockoutOption(model_center, S0, strike, T, r, barrier)\n",
+ " knockout_down = KnockoutOption(model_down, S0 - h, strike, T, r, barrier)\n",
+ " \n",
+ " price_up = knockout_up.price_knockout_option(N, M)\n",
+ " price_center = knockout_center.price_knockout_option(N, M)\n",
+ " price_down = knockout_down.price_knockout_option(N, M)\n",
+ " \n",
+ " # Calculate gamma using second-order central difference\n",
+ " gamma = (price_up - 2*price_center + price_down) / (h * h)\n",
+ " \n",
+ " df = pd.DataFrame({\n",
+ " \"Gamma\": [gamma], \n",
+ " \"Price_Up\": [price_up], \n",
+ " \"Price_Center\": [price_center],\n",
+ " \"Price_Down\": [price_down], \n",
+ " \"h\": [h]\n",
+ " })\n",
+ " return df\n",
+ "\n",
+ "# To analyze gamma sensitivity to underlying price changes\n",
+ "result = run_test(\n",
+ " \"my_custom_tests.GreeksGamma\",\n",
+ " param_grid={\n",
+ " \"model_type\": ['SV'],\n",
+ " \"N\": [1000000],\n",
+ " \"M\": [M],\n",
+ " \"strike\":[strike_range[0]],\n",
+ " \"barrier\": [barrier_range[0]],\n",
+ " \"S0\": list(np.linspace(S0-20, S0+20, 20)),\n",
+ " \"T\": [T],\n",
+ " \"r\": [r],\n",
+ " \"v0\": [0.2],\n",
+ " \"kappa\": [2],\n",
+ " \"theta\": [0.2],\n",
+ " \"xi\": [0.1],\n",
+ " \"rho\": [-0.5],\n",
+ " \"h\": [0.1]\n",
+ " },\n",
+ " post_process_fn=process_results # Using the plotting function defined earlier\n",
+ ")\n",
+ "result.log()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "58ee0864",
+ "metadata": {},
+ "source": [
+ "### Theta\n",
+ "Let's measures the sensitivity of the option's price to the passage of time, also known as time decay. It indicates how much the price of an option is expected to decrease as the option approaches its expiration date."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "20fa7300",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "@vm.test(\"my_custom_tests.GreeksTheta\")\n",
+ "def calculate_theta(model_type, S0, T, r, N, M, strike=None, barrier=None, \n",
+ " sigma=None, v0=None, kappa=None, theta=None, xi=None, rho=None, \n",
+ " dt=1/365): # dt is typically one day\n",
+ " \"\"\"\n",
+ " Calculate theta using finite difference method.\n",
+ " Theta = (V(t + dt) - V(t)) / dt\n",
+ " where V is the option price and dt is a small time increment (typically 1 day)\n",
+ " \"\"\"\n",
+ " # Initialize the models with T and T + dt\n",
+ " if model_type == 'BS':\n",
+ " model_current = BlackScholesModel(S0, strike, T, r, sigma)\n",
+ " model_future = BlackScholesModel(S0, strike, T + dt, r, sigma)\n",
+ " else:\n",
+ " model_current = StochasticVolatilityModel(S0, strike, T, r, v0, kappa, theta, xi, rho)\n",
+ " model_future = StochasticVolatilityModel(S0, strike, T + dt, r, v0, kappa, theta, xi, rho)\n",
+ " \n",
+ " # Calculate option prices for current and future time\n",
+ " knockout_current = KnockoutOption(model_current, S0, strike, T, r, barrier)\n",
+ " knockout_future = KnockoutOption(model_future, S0, strike, T + dt, r, barrier)\n",
+ " \n",
+ " price_current = knockout_current.price_knockout_option(N, M)\n",
+ " price_future = knockout_future.price_knockout_option(N, M)\n",
+ " \n",
+ " # Calculate theta using forward difference\n",
+ " # Note: We divide by dt and multiply by -1 since theta represents the negative rate of change\n",
+ " theta_value = -1 * (price_future - price_current) / dt\n",
+ " \n",
+ " df = pd.DataFrame({\n",
+ " \"Theta\": [theta_value], \n",
+ " \"Price_Current\": [price_current],\n",
+ " \"Price_Future\": [price_future],\n",
+ " \"dt\": [dt]\n",
+ " })\n",
+ " return df\n",
+ "\n",
+ "# Example usage to analyze theta sensitivity across different underlying prices\n",
+ "result = run_test(\n",
+ " \"my_custom_tests.GreeksTheta\",\n",
+ " param_grid={\n",
+ " \"model_type\": ['SV'],\n",
+ " \"N\": [1000000],\n",
+ " \"M\": [M],\n",
+ " \"strike\":[strike_range[0]],\n",
+ " \"barrier\": [barrier_range[0]],\n",
+ " \"S0\": list(np.linspace(S0-20, S0+20, 20)),\n",
+ " \"T\": [T],\n",
+ " \"r\": [r],\n",
+ " \"v0\": [0.2],\n",
+ " \"kappa\": [2],\n",
+ " \"theta\": [0.2],\n",
+ " \"xi\": [0.1],\n",
+ " \"rho\": [-0.5],\n",
+ " \"dt\": [1/365] # One day time step\n",
+ " },\n",
+ " post_process_fn=process_results # Using the plotting function defined earlier\n",
+ ")\n",
+ "result.log()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c4b3386e",
+ "metadata": {},
+ "source": [
+ "### Vega\n",
+ "Let's measures the sensitivity of the option's price to changes in the volatility of the underlying asset. It indicates how much the price of an option is expected to change with a 1% change in the underlying asset's volatility."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "ffb40f67",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "@vm.test(\"my_custom_tests.GreeksVega\")\n",
+ "def calculate_vega(model_type, S0, T, r, N, M, strike=None, barrier=None, \n",
+ " sigma=None, v0=None, kappa=None, theta=None, xi=None, rho=None, \n",
+ " h=0.001): # h is the step size for finite difference\n",
+ " \"\"\"\n",
+ " Calculate vega using finite difference method.\n",
+ " For Black-Scholes: Vega = (V(σ + h) - V(σ - h)) / (2h)\n",
+ " For Stochastic Vol: Vega = (V(v0 + h) - V(v0 - h)) / (2h)\n",
+ " where V is the option price and h is a small increment in volatility\n",
+ " \"\"\"\n",
+ " if model_type == 'BS':\n",
+ " # For Black-Scholes, perturb sigma\n",
+ " model_up = BlackScholesModel(S0, strike, T, r, sigma + h)\n",
+ " model_down = BlackScholesModel(S0, strike, T, r, sigma - h)\n",
+ " else:\n",
+ " # For Stochastic Volatility, perturb v0\n",
+ " model_up = StochasticVolatilityModel(S0, strike, T, r, v0 + h, kappa, theta, xi, rho)\n",
+ " model_down = StochasticVolatilityModel(S0, strike, T, r, v0 - h, kappa, theta, xi, rho)\n",
+ " \n",
+ " # Calculate option prices for up and down moves in volatility\n",
+ " knockout_up = KnockoutOption(model_up, S0, strike, T, r, barrier)\n",
+ " knockout_down = KnockoutOption(model_down, S0, strike, T, r, barrier)\n",
+ " \n",
+ " price_up = knockout_up.price_knockout_option(N, M)\n",
+ " price_down = knockout_down.price_knockout_option(N, M)\n",
+ " \n",
+ " # Calculate vega using central difference\n",
+ " vega = (price_up - price_down) / (2 * h)\n",
+ " \n",
+ " df = pd.DataFrame({\n",
+ " \"Vega\": [vega], \n",
+ " \"Price_Up\": [price_up], \n",
+ " \"Price_Down\": [price_down], \n",
+ " \"h\": [h]\n",
+ " })\n",
+ " return df\n",
+ "\n",
+ "# Example usage to analyze vega sensitivity across different underlying prices\n",
+ "result = run_test(\n",
+ " \"my_custom_tests.GreeksVega\",\n",
+ " param_grid={\n",
+ " \"model_type\": ['SV'],\n",
+ " \"N\": [1000000],\n",
+ " \"M\": [M],\n",
+ " \"strike\":[strike_range[0]],\n",
+ " \"barrier\": [barrier_range[0]],\n",
+ " \"S0\": list(np.linspace(S0-20, S0+20, 20)),\n",
+ " \"T\": [T],\n",
+ " \"r\": [r],\n",
+ " \"v0\": [0.2],\n",
+ " \"kappa\": [2],\n",
+ " \"theta\": [0.2],\n",
+ " \"xi\": [0.1],\n",
+ " \"rho\": [-0.5],\n",
+ " \"h\": [0.0001] # Small step size for better accuracy\n",
+ " },\n",
+ " post_process_fn=process_results # Using the plotting function defined earlier\n",
+ ")\n",
+ "result.log()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "f917f36f",
+ "metadata": {},
+ "source": [
+ "### Rho\n",
+ "Let's measures the sensitivity of the option's price to changes in the interest rate. It indicates how much the price of an option is expected to change with a 1% change in interest rates.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "618efa93",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "@vm.test(\"my_custom_tests.GreeksRho\")\n",
+ "def calculate_rho(model_type, S0, T, r, N, M, strike=None, barrier=None, \n",
+ " sigma=None, v0=None, kappa=None, theta=None, xi=None, rho=None, \n",
+ " h=0.0001): # h is the step size for finite difference\n",
+ " \"\"\"\n",
+ " Calculate rho using finite difference method.\n",
+ " Rho = (V(r + h) - V(r - h)) / (2h)\n",
+ " where V is the option price and h is a small increment in interest rate\n",
+ " \"\"\"\n",
+ " # Initialize the models with r + h and r - h\n",
+ " if model_type == 'BS':\n",
+ " model_up = BlackScholesModel(S0, strike, T, r + h, sigma)\n",
+ " model_down = BlackScholesModel(S0, strike, T, r - h, sigma)\n",
+ " else:\n",
+ " model_up = StochasticVolatilityModel(S0, strike, T, r + h, v0, kappa, theta, xi, rho)\n",
+ " model_down = StochasticVolatilityModel(S0, strike, T, r - h, v0, kappa, theta, xi, rho)\n",
+ " \n",
+ " # Calculate option prices for up and down moves in interest rate\n",
+ " knockout_up = KnockoutOption(model_up, S0, strike, T, r + h, barrier)\n",
+ " knockout_down = KnockoutOption(model_down, S0, strike, T, r - h, barrier)\n",
+ " \n",
+ " price_up = knockout_up.price_knockout_option(N, M)\n",
+ " price_down = knockout_down.price_knockout_option(N, M)\n",
+ " \n",
+ " # Calculate rho using central difference\n",
+ " rho_value = (price_up - price_down) / (2 * h)\n",
+ " \n",
+ " df = pd.DataFrame({\n",
+ " \"Rho\": [rho_value], \n",
+ " \"Price_Up\": [price_up], \n",
+ " \"Price_Down\": [price_down], \n",
+ " \"h\": [h]\n",
+ " })\n",
+ " return df\n",
+ "\n",
+ "# Example usage to analyze rho sensitivity across different underlying prices\n",
+ "result = run_test(\n",
+ " \"my_custom_tests.GreeksRho\",\n",
+ " param_grid={\n",
+ " \"model_type\": ['SV'],\n",
+ " \"N\": [1000000],\n",
+ " \"M\": [M],\n",
+ " \"strike\":[strike_range[0]],\n",
+ " \"barrier\": [barrier_range[0]],\n",
+ " \"S0\": list(np.linspace(S0-20, S0+20, 20)),\n",
+ " \"T\": [T],\n",
+ " \"r\": [r],\n",
+ " \"v0\": [0.2],\n",
+ " \"kappa\": [2],\n",
+ " \"theta\": [0.2],\n",
+ " \"xi\": [0.1],\n",
+ " \"rho\": [-0.5],\n",
+ " \"h\": [0.0001] # Small step size for better accuracy\n",
+ " },\n",
+ " post_process_fn=process_results # Using the plotting function defined earlier\n",
+ ")\n",
+ "result.log()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "14fd2cd8",
+ "metadata": {},
+ "source": [
+ "\n",
"#### Stress Testing"
]
},
{
"cell_type": "code",
- "execution_count": 27,
+ "execution_count": 34,
"id": "7bdd02ed",
"metadata": {},
"outputs": [],
@@ -1093,7 +1519,7 @@
},
{
"cell_type": "code",
- "execution_count": 36,
+ "execution_count": 35,
"id": "77d49b6a",
"metadata": {},
"outputs": [],