Context
The Carr-Madan decomposition is used in quant finance to break any payoff into a (continuous) combination of calls and puts, plus a forward. Namely, for any twice differentiable function:
where is the positive part function.
This result translates what quants intuitively know: for a derivative product with any given payoff, we can approximate it from calls and puts (plus a forward).
More specifically, we will approximate the integrals as Riemann sums: let be some sufficiently large value. Then
We will then have a total of calls and puts.
import numpy as np
from numba import jit
import matplotlib.pyplot as plt
# original function
def f(x):
return np.cos(x)
@jit(nopython=True)
def series_expansion(x, y, N=100, L=10):
def f(x):
return np.cos(x)
def df(x):
return -np.sin(x)
def ddf(x):
return - f(x)
summand = f(y) + df(y)*(x-y)
dz = (y+L)/N
for i in range(1,N):
z = -L + i*dz
summand += ddf(z)*np.maximum(z-x, 0)*dz
dz = (L-y)/N
for i in range(1,N):
z = y + i*dz
summand += ddf(z)*np.maximum(x-z, 0)*dz
return summand
Using 100 terms:
x_range = np.arange(0, 10, 0.01)
carr_madan = [series_expansion(x, 1, N=100, L=50) for x in x_range]
plt.plot(x_range, carr_madan, label='Carr-Madan approx')
plt.plot(x_range, f(x_range), label='True function')
plt.legend()
plt.show()

Increasing to 10,000 terms: the approximation gets much better.
x_range = np.arange(0, 10, 0.01)
carr_madan = [series_expansion(x, 1, N=10000, L=50) for x in x_range]
plt.plot(x_range, carr_madan, label='Carr-Madan approx')
plt.plot(x_range, f(x_range), label='True function')
plt.legend()
plt.show()
