Skip to content
All library documents

Debugging a Finite Difference Call Option Valuation

Article Quant Q&A · Author: Desi_Quant

Summary

The document presents Python code intended to value a European call or put using a time and asset-price grid. It sets the payoff at expiration, then attempts to step backward through the grid while estimating option sensitivities and applying boundary conditions. The example provides enough detail to identify a key indexing problem: the loop reads column k+1 even though k advances through the final time column, so the access eventually exceeds the grid bounds and raises the shown IndexError.

The code labels its approach implicit, but its update uses values from a future column without first solving the coupled system that an implicit finite-difference method requires. It also refers to global volatility and rate variables rather than the function parameters. These issues mean the example is a debugging prompt, not a validated pricing method; it gives no corrected implementation, convergence evidence, or comparison with a benchmark price.

Key ideas

  • The code initializes a payoff grid and attempts to propagate option values across time steps.
  • The reported error is caused by an out-of-range time-column access using k+1.
  • A genuinely implicit finite-difference update requires solving a system of equations at each time step.
  • The function uses global rate and volatility variables instead of its corresponding parameters.

Tags

Full text
# Error in Call Option Valuation using Implicit Finite Difference implemented in Python


# Error in Call Option Valuation using Implicit Finite Difference implemented in Python












I am trying to valuate call option using implicit Finite difference method (Forward Marching) implemented in Python. However I am getting the error in the code. Following is the code I have developed:

```
import pandas as pd
import numpy as np

#Set max row, columns to 200
pd.set_option('max_rows', 200)

pd.set_option('max_columns', 200)

#specify the parameters
#time to maturity
T = 1

#strike price
K = 100

#riskfree rate
r = .05

#volatility
vol = .20

#Flag = 1 for call, -1 for puts
Flag = 1

#number of steps
steps = 20

#asset step size
ds = 2* K / steps

#time step size for stability
dt = (0.9/vol**2/steps**2)

#time steps
time_steps = int(T / dt) + 1

#time step size
dt = T / time_steps

#Create asset steps i*ds
s = np.arange(0,(steps+1)*ds,ds)

#Create time steps k*dt
t = T-np.arange(time_steps*dt,-dt,-dt)

grid = np.zeros((len(s),len(t)))

#Subsume the grid points into a dataframe with asset price as index and time steps as columns
grid = pd.DataFrame(grid, index=s, columns=t)
print(grid)

#Set Final or Initial condition at Expiration
if Flag == 1:
    grid.iloc[:,0] = np.maximum(s - K, 0)
else:
    grid.iloc[:,0] = np.maximum(K - s, 0)

def implicitFiniteDifference_ForwardMarching(K, Volatility, Rate, TTM, steps, Flag=1):
    # Specify Flag as 1 for calls and -1 for puts

    ds = 2*K/steps                   # asset step size
    dt = 0.9/Volatility**2/steps**2  # for stability

    time_steps = int(TTM / dt) + 1          # time step size 
    dt = TTM/time_steps                     # time step

    s = np.arange(0,(steps+1)*ds,ds)
    t = TTM-np.arange(time_steps*dt,-dt,-dt)

    # Initialize the grid with zeros
    grid = np.zeros((len(s),len(t)))
    grid = pd.DataFrame(grid, index=s, columns=t)

    # Set boundary condition at Expiration
    grid.iloc[:,0] = np.maximum(Flag * (s - K), 0)

    for k in range(1, len(t)):
        for i in range(1,len(s)-1):
            delta = (grid.iloc[i+1,k+1] - grid.iloc[i-1,k+1]) / (2*ds)
            gamma = (grid.iloc[i+1,k+1]-2*grid.iloc[i,k+1]+grid.iloc[i-1,k+1]) / (ds**2)
            theta = (-0.5* vol**2 * s[i]**2 * gamma) - (r*s[i]*delta) + (r*grid.iloc[i,k-1])
            grid.iloc[i,k] = grid.iloc[i,k-1] - dt*theta

        # Set boundary condition at S = 0 and S = infinity
        grid.iloc[0,k] = grid.iloc[0,k-1] * (1-r*dt)
        grid.iloc[len(s)-1,k] = abs(2*(grid.iloc[len(s)-2,k]) - grid.iloc[len(s)-3,k])

    # round grid values to 4 decimal places
    return np.around(grid,4)

#Call the function for pricing call
fdmcall = implicitFiniteDifference_ForwardMarching(100,0.2,0.05,1,40,Flag=1)

print(fdmcall)
```

Error starts like this:

```
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-4-9af6f8d89957> in <module>()
     90 
     91 #Call the function for pricing call
---> 92 fdmcall = implicitFiniteDifference_ForwardMarching(100,0.2,0.05,1,40,Flag=1)
     93 
     94 print(fdmcall)

<ipython-input-4-9af6f8d89957> in implicitFiniteDifference_ForwardMarching(K, Volatility, Rate, TTM, steps, Flag)
     77     for k in range(1, len(t)):
     78         for i in range(1,len(s)-1):
---> 79             delta = (grid.iloc[i+1,k+1] - grid.iloc[i-1,k+1]) / (2*ds)
     80             gamma = (grid.iloc[i+1,k+1]-2*grid.iloc[i,k+1]+grid.iloc[i-1,k+1]) / (ds**2)
     81             theta = (-0.5* vol**2 * s[i]**2 * gamma) - (r*s[i]*delta) + (r*grid.iloc[i,k-1])
```

then lots of computer-specific stack trace, then:

```
   2137         len_axis = len(self.obj._get_axis(axis))
   2138         if key >= len_axis or key < -len_axis:
-> 2139             raise IndexError("single positional indexer is out-of-bounds")
   2140 
   2141     def _getitem_tuple(self, tup):

IndexError: single positional indexer is out-of-bounds
```
```

Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.