Adding clause to derivative

One can customize a derivative by registering additional clauses; see pfhedge.instruments.BaseDerivative.add_clause().

Let us see how to add clauses to derivatives by taking European capped call option as an example. This option is a variant of a European option, which is given by

>>> from pfhedge.instruments import BrownianStock
>>> from pfhedge.instruments import EuropeanOption
...
>>> strike = 1.0
>>> maturity = 1.0
>>> stock = BrownianStock()
>>> european = EuropeanOption(stock, strike=strike, maturity=maturity)

The capped call associates ‘’a barrier clause’’; if the underlier’s spot reaches the barrier price \(B\), being greater than the strike \(K\) and the spot at inception, the option immediately expires and pays off its intrinsic value at that moment \(B - K\).

This clause can be registered to a derivative as follows:

>>> def cap_clause(derivative, payoff):
...     barrier = 1.4
...     max_spot = derivative.ul().spot.max(-1).values
...     capped_payoff = torch.full_like(payoff, barrier - strike)
...     return torch.where(max_spot < barrier, payoff, capped_payoff)
...
>>> capped_european = EuropeanOption(stock, strike=strike, maturity=maturity)
>>> capped_european.add_clause("cap_clause", cap_clause)

The method add_clause adds the clause and its name to the derivative. Here the function cap_caluse represents the clause to modify the payoff depending on the state of the derivative. The clause function should have the signature clause(derivative, payoff) -> modified payoff.

The payoff would be capped as intended:

>>> n_paths = 100000
>>> capped_european.simulate(n_paths=n_paths)
>>> european.payoff().max()
>>> # 1.2...
>>> capped_european.payoff().max()
>>> # 0.4

The price of the capped European call option can be evaluated by using a European option as a control variates.

>>> from math import sqrt
...
>>> payoff_european = european.payoff()
>>> payoff_capped_european = capped_european.payoff()
>>> bs_price = BlackScholes(european).price(0.0, european.maturity, stock.sigma).item()
>>> price = bs_price + (payoff_capped_european - payoff_european).mean().item()
>>> error = (payoff_capped_european - payoff_european).std().item() / sqrt(n_paths)
>>> bs_price
>>> # 0.07967...
>>> price
>>> # 0.07903...
>>> error
>>> # 0.00012...