PFHedge is a PyTorch-based framework for Deep Hedging.
Deep Hedging is a deep learning-based framework to hedge financial derivatives.
Hedging financial derivatives in the presence of market frictions (e.g., transaction cost) is a challenging task. In the absence of market frictions, the perfect hedge is accessible based on the Black-Scholes model. The real market, in contrast, always involves frictions and thereby makes hedging optimization much more challenging. Since the analytic formulas (such as the Black-Scholes formula) are no longer available in such a market, it may be necessary to adjust model-based Greeks to hedge and price derivatives based on experiences.
Deep Hedging is a ground-breaking framework to optimize such hedging operations. In this framework, a neural network is trained to hedge derivatives so that it minimizes a proper risk measure. By virtue of the high representability of a neural network and modern optimization algorithms, one can expect to achieve the optimal hedge by training a neural network. Indeed, the experiments in Bühler et al. 18 and Imaki et al. 21 show high feasibility and scalability of Deep Hedging algorithms for options under transaction costs.
Global investment banks are looking to rethink the Greeks-based hedging with Deep Hedging and slash considerable amount of hedging costs. This could be the "game-changer" in the trillion-dollar industry of derivatives.
PFHedge enables you to experience this revolutionary framework on your own. You can try, tweak, and delve into Deep Hedging algorithms using PyTorch. We hope PFHedge accelerates the research and development of Deep Hedging.
Hedger
and then fit
and price
derivatives right away.Module
and trained by any Optimizer
.Module
.Module
s for Deep Hedging in pfhedge.nn
.pip install pfhedge
Financial instruments are provided in pfhedge.instruments
and classified into two types:
Primary
instruments: A primary instrument is a basic financial instrument that is traded on a market, and therefore their prices are accessible as the market prices. Examples include stocks, bonds, commodities, and currencies.Derivative
instruments: A derivative is a financial instrument whose payoff is contingent on a primary instrument. An (over-the-counter) derivative is not traded on the market, and therefore the price is not directly accessible. Examples include EuropeanOption
, LookbackOption
, VarianceSwap
, and so forth.We consider a BrownianStock
, which is a stock following the geometric Brownian motion, and a EuropeanOption
which is contingent on it.
We assume that the stock has a transaction cost of 1 basis point.
from pfhedge.instruments import BrownianStock
from pfhedge.instruments import EuropeanOption
stock = BrownianStock(cost=1e-4)
derivative = EuropeanOption(stock)
derivative
# EuropeanOption(
# strike=1., maturity=0.0800
# (underlier): BrownianStock(sigma=0.2000, cost=1.0000e-04, dt=0.0040)
# )
A Hedger
in Deep Hedging is basically characterized by three elements:
'log_moneyness'
: Log-moneyness of the stock.'expiry_time'
: Remaining time to the maturity of the option.'volatility'
: Volatility of the stock.'prev_hedge'
: The hedge ratio at the previous time step.MultiLayerPerceptron
: Multi-layer perceptron.BlackScholes
: Black-Scholes' delta-hedging strategy.WhalleyWilmott
: Whalley-Wilmott's asymptotically optimal strategy for small costs.Module
which you build.EntropicRiskMeasure
: Entropic Risk Measure, a risk measure derived from exponential utility.ExpectedShortFall
: Expected Shortfall or CVaR, a common measure to assess portfolio risk.We here use a multi-layer perceptron as our model.
from pfhedge.nn import Hedger
from pfhedge.nn import MultiLayerPerceptron
model = MultiLayerPerceptron()
hedger = Hedger(model, inputs=["log_moneyness", "expiry_time", "volatility", "prev_hedge"])
The hedger
is also a Module
.
hedger
# Hedger(
# inputs=['log_moneyness', 'expiry_time', 'volatility', 'prev_hedge']
# (model): MultiLayerPerceptron(
# (0): LazyLinear(in_features=0, out_features=32, bias=True)
# (1): ReLU()
# (2): Linear(in_features=32, out_features=32, bias=True)
# (3): ReLU()
# (4): Linear(in_features=32, out_features=32, bias=True)
# (5): ReLU()
# (6): Linear(in_features=32, out_features=32, bias=True)
# (7): ReLU()
# (8): Linear(in_features=32, out_features=1, bias=True)
# (9): Identity()
# )
# (criterion): EntropicRiskMeasure()
# )
Now we train our hedger
so that it minimizes the risk measure through hedging.
The hedger
is trained as follows.
In each epoch, we generate Monte Carlo paths of the asset prices and let the hedger
hedge the derivative by trading the stock.
The hedger's risk measure (EntropicRiskMeasure()
in our case) is computed from the resulting profit and loss distribution, and the parameters in the model
are updated.
hedger.fit(derivative, n_epochs=200)
Once we have trained the hedger
, we can evaluate the derivative price as utility indifference price (For details, see Deep Hedging and references therein).
price = hedger.price(derivative)
To employ the desired device
and/or dtype
in fitting and pricing, use to
method.
dtype = torch.float64
device = torch.device("cuda:0")
derivative = EuropeanOption(BrownianStock()).to(dtype, device)
hedger = Hedger(...).to(dtype, device)
In this strategy, a hedger incessantly rebalances their portfolio and keeps it delta-neutral. The hedge-ratio at each time step is given by the Black-Scholes' delta.
This strategy is the optimal one in the absence of cost. On the other hand, this strategy transacts too frequently and consumes too much transaction cost.
from pfhedge.nn import BlackScholes
from pfhedge.nn import Hedger
derivative = EuropeanOption(BrownianStock(cost=1e-4))
model = BlackScholes(derivative)
hedger = Hedger(model, inputs=model.inputs())
This strategy is proposed by Whalley et al. 1997 and is proved to be optimal for asymptotically small transaction costs.
In this strategy, a hedger always maintains their hedge ratio in the range (called no-transaction band) while they never transact inside this range. This strategy is supposed to be optimal in the limit of small transaction costs, while suboptimal for large transaction costs.
from pfhedge.nn import Hedger
from pfhedge.nn import WhalleyWilmott
derivative = EuropeanOption(BrownianStock(cost=1e-3))
model = WhalleyWilmott(derivative)
hedger = Hedger(model, inputs=model.inputs())
You can employ any Module
you build as a hedging model.
The input/output shapes is (N, H_in) -> (N, 1)
, where N
is the number of Monte Carlo paths of assets and H_in
is the number of input features.
Here we show an example of No-Transaction Band Network, which is proposed in Imaki et al. 21.
import torch
import torch.nn.functional as fn
from torch import Tensor
from torch.nn import Module
from pfhedge.nn import BlackScholes
from pfhedge.nn import Clamp
from pfhedge.nn import Hedger
from pfhedge.nn import MultiLayerPerceptron
class NoTransactionBandNet(Module):
def __init__(self, derivative):
super().__init__()
self.delta = BlackScholes(derivative)
self.mlp = MultiLayerPerceptron(out_features=2)
self.clamp = Clamp()
def inputs(self):
return self.delta.inputs() + ["prev_hedge"]
def forward(self, input: Tensor) -> Tensor:
prev_hedge = input[..., [-1]]
delta = self.delta(input[..., :-1])
width = self.mlp(input[..., :-1])
min = delta - fn.leaky_relu(width[..., [0]])
max = delta + fn.leaky_relu(width[..., [1]])
return self.clamp(prev_hedge, min=min, max=max)
model = NoTransactionBandNet(derivative)
hedger = Hedger(model, inputs=model.inputs())
A module pfhedge.autogreek
provides functions implementing automatic evaluation of greeks using automatic differentiation.
import pfhedge.autogreek as autogreek
from pfhedge.instruments import BrownianStock
from pfhedge.instruments import EuropeanOption
from pfhedge.nn import Hedger
from pfhedge.nn import WhalleyWilmott
derivative = EuropeanOption(BrownianStock(cost=1e-4))
model = WhalleyWilmott(derivative)
hedger = Hedger(model, inputs=model.inputs())
def pricer(spot):
return hedger.price(derivative, init_state=(spot,), enable_grad=True)
delta = autogreek.delta(pricer, spot=torch.tensor(1.0))
# tensor(0.5092)
gamma = autogreek.gamma(pricer, spot=torch.tensor(1.0))
# tensor(0.0885)
Any contributions to PFHedge are more than welcome!
Please take a look at CONTRIBUTING.md before creating a pull request.
This project is owned by Preferred Networks and maintained by Shota Imaki and Masanori Hirano.