Hedger¶
- class pfhedge.nn.Hedger(model, inputs, criterion=EntropicRiskMeasure())[source]¶
- Module to hedge and price derivatives. - References - Buehler, H., Gonon, L., Teichmann, J. and Wood, B., 2019. Deep hedging. Quantitative Finance, 19(8), pp.1271-1291. [arXiv:1802.03042 [q-fin]] 
 - Parameters
- model (torch.nn.Module) – Hedging model to compute the hedge ratio at the next time step from the input features at the current time step. The input and output shapes should be \((N, F)\) and \((N, H)\) respectively, where \(N\) stands for the number simulated paths of the asset prices and \(F\) is the number of input features ( - len(inputs)), and \(H\) is the number of hedging instruments.
- inputs (list[str|Feature]) – List of the names of the input features that will be fed to the model. See - pfhedge.features.list_feature_names()for available feature names and see pfhedge.features for the details of features.
- criterion (HedgeLoss, default=EntropicRiskMeasure()) – Loss function to minimize by hedging. Default: - pfhedge.nn.EntropicRiskMeasure().
 
 - Shape:
- input: \((N, F)\) where \(N\) is the number of simulated paths and \(F\) is the number of input features. 
- output: \((N, H)\) where \(H\) is the number of hedging instruments. 
 
 - Examples - A hedger that uses Black-Scholes’ delta hedging strategy. See - pfhedge.nn.BlackScholesfor details of the module.- >>> from pfhedge.instruments import BrownianStock >>> from pfhedge.instruments import EuropeanOption >>> from pfhedge.nn import BlackScholes >>> from pfhedge.nn import Hedger ... >>> derivative = EuropeanOption(BrownianStock(cost=1e-4)) >>> model = BlackScholes(derivative) >>> hedger = Hedger(model, model.inputs()) >>> hedger Hedger( inputs=['log_moneyness', 'time_to_maturity', 'volatility'] (model): BSEuropeanOption(strike=1.) (criterion): EntropicRiskMeasure() ) - A hedger that uses Whalley-Wilmott’s no-transaction-band strategy. See - pfhedge.nn.WhalleyWilmottfor details of the module.- >>> from pfhedge.nn import WhalleyWilmott >>> >>> model = WhalleyWilmott(derivative) >>> hedger = Hedger(model, model.inputs()) >>> hedger Hedger( inputs=['log_moneyness', 'time_to_maturity', 'volatility', 'prev_hedge'] (model): WhalleyWilmott( (bs): BSEuropeanOption(strike=1.) ) (criterion): EntropicRiskMeasure() ) - A hedger that takes naked positions (never hedge at all). See - pfhedge.nn.Nakedfor details of the module.- >>> from pfhedge.nn import Naked >>> >>> hedger = Hedger(Naked(), ["empty"]) - A hedger represented by a neural network (Deep Hedging). See - pfhedge.nn.MultiLayerPerceptronfor details of the module.- >>> from pfhedge.nn import MultiLayerPerceptron >>> >>> model = MultiLayerPerceptron() >>> hedger = Hedger(model, ["moneyness", "time_to_maturity", "volatility"]) >>> derivative.simulate(n_paths=1) >>> _ = hedger.compute_pl(derivative) # Lazily materialize >>> hedger Hedger( inputs=['moneyness', 'time_to_maturity', 'volatility'] (model): MultiLayerPerceptron( (0): Linear(in_features=3, 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() ) >>> history = hedger.fit(derivative, n_paths=1, n_epochs=1, verbose=False) >>> hedger.price(derivative) tensor(...) - It is possible to hedge a derivative with another listed derivative by - list()method.- >>> from pfhedge.instruments import LookbackOption >>> from pfhedge.nn import BlackScholes >>> >>> pricer = lambda derivative: BlackScholes(derivative).price( ... log_moneyness=derivative.log_moneyness(), ... time_to_maturity=derivative.time_to_maturity(), ... volatility=derivative.ul().volatility) >>> >>> stock = BrownianStock() >>> hedging_instrument = EuropeanOption(stock, maturity=5/250) >>> hedging_instrument.list(pricer, cost=1e-4) >>> derivative = LookbackOption(stock) >>> >>> hedger = Hedger( ... MultiLayerPerceptron(), ... inputs=["moneyness", "time_to_maturity", "volatility"]) >>> _ = hedger.fit( ... derivative, ... hedge=[hedging_instrument], ... n_paths=1, ... n_epochs=1, ... verbose=False) >>> hedger.price(derivative) tensor(...) - Hedging a derivative with multiple instruments. - >>> from pfhedge.instruments import HestonStock >>> from pfhedge.instruments import EuropeanOption >>> from pfhedge.instruments import VarianceSwap >>> from pfhedge.nn import BlackScholes >>> >>> _ = torch.manual_seed(42) >>> stock = HestonStock(cost=1e-4) >>> option = EuropeanOption(stock) >>> varswap = VarianceSwap(stock) >>> pricer = lambda varswap: varswap.ul().variance - varswap.strike >>> varswap.list(pricer, cost=1e-4) >>> hedger = Hedger( ... MultiLayerPerceptron(3, 2), ... inputs=["moneyness", "time_to_maturity", "volatility"]) >>> hedger.price(option, hedge=[stock, varswap], n_paths=2) tensor(...) - compute_hedge(derivative, hedge=None)[source]¶
- Compute the hedge ratio at each time step. - This method assumes that the derivative is already simulated. - Parameters
- derivative (BaseDerivative) – The derivative to hedge. 
- hedge (list[BaseInstrument], optional) – The hedging instruments. If - None(default), use- derivative.underliers.
 
 - Shape:
- output: \((N, H, T)\) where \(N\) is the number of paths, \(H\) is the number of hedging instruments, and \(T\) is the number of time steps. 
 
 - Returns
- torch.Tensor 
 - Examples - >>> from pfhedge.instruments import BrownianStock >>> from pfhedge.instruments import EuropeanOption >>> from pfhedge.nn import BlackScholes ... >>> _ = torch.manual_seed(42) >>> derivative = EuropeanOption(BrownianStock(), maturity=5/250) >>> derivative.simulate(n_paths=2) >>> derivative.ul().spot tensor([[1.0000, 1.0016, 1.0044, 1.0073, 0.9930, 0.9906], [1.0000, 0.9919, 0.9976, 1.0009, 1.0076, 1.0179]]) >>> model = BlackScholes(derivative) >>> hedger = Hedger(model, model.inputs()) >>> hedger.compute_hedge(derivative).squeeze(1) tensor([[0.5056, 0.5295, 0.5845, 0.6610, 0.2918, 0.2918], [0.5056, 0.3785, 0.4609, 0.5239, 0.7281, 0.7281]]) 
 - compute_loss(derivative, hedge=None, n_paths=1000, n_times=1, init_state=None, enable_grad=True)[source]¶
- Returns the value of the criterion for the terminal portfolio value after hedging a given derivative. - This method basically computes - self.criterion(pl)where- plis given by- compute_pl().- Parameters
- derivative (BaseDerivative) – The derivative to hedge. 
- hedge (list[BaseInstrument], optional) – The hedging instruments. If - None(default), use- [derivative.underlier].
- n_paths (int, default=1000) – The number of simulated price paths of the underlying instrument. 
- n_times (int, default=1) – If - n_times > 1, returns the ensemble mean of the losses computed through multiple simulations.
- init_state (tuple, optional) – The initial price of the underlying instrument of the derivative. If - None(default), it uses the default value of the underlying instrument.
- enable_grad (bool, default=True) – Context-manager that sets gradient calculation to on or off. 
 
 - Shape:
- Output: \(()\) 
 
 - Returns
- torch.Tensor 
 - Examples - >>> from pfhedge.instruments import BrownianStock >>> from pfhedge.instruments import EuropeanOption >>> from pfhedge.nn import BlackScholes >>> from pfhedge.nn import Hedger ... >>> derivative = EuropeanOption(BrownianStock()) >>> model = BlackScholes(derivative) >>> hedger = Hedger(model, model.inputs()) >>> hedger.compute_loss(derivative, n_paths=2) tensor(...) - One can use PyTorch built-in loss functions, such as the mean squared loss - torch.nn.MSELoss, as criteria. Then the criterion measures the loss between the hedging portfolio (cf.- compute_portfolio()) as- inputand the payoff of the derivative as- target.- >>> from torch.nn import MSELoss ... >>> _ = torch.manual_seed(42) >>> derivative = EuropeanOption(BrownianStock()) >>> model = BlackScholes(derivative) >>> hedger = Hedger(model, model.inputs(), criterion=MSELoss()) >>> hedger.compute_loss(derivative, n_paths=10) tensor(...) 
 - compute_pl(derivative, hedge=None)[source]¶
- Returns the terminal portfolio value after hedging a given derivative. - This method assumes that the derivative is already simulated. - See - pfhedge.nn.functional.terminal_value()for the expression of the terminal portfolio value after hedging a derivative.- Parameters
- derivative (BaseDerivative) – The derivative to hedge. 
- hedge (list[BaseInstrument], optional) – The hedging instruments. If - None(default), use- [derivative.underlier].
- n_paths (int, default=1000) – The number of simulated price paths of the underlying instrument. 
- init_state (tuple[torch.Tensor | float], optional) – The initial state of the underlying instrument of the derivative. If - None(default), it uses the default value.
 
 - Shape:
- Output: \((N)\) where \(N\) is the number of paths. 
 
 - Returns
- torch.Tensor 
 - Examples - >>> from pfhedge.instruments import BrownianStock >>> from pfhedge.instruments import EuropeanOption >>> from pfhedge.nn import BlackScholes >>> from pfhedge.nn import Hedger ... >>> derivative = EuropeanOption(BrownianStock()) >>> derivative.simulate(n_paths=2) >>> model = BlackScholes(derivative) >>> hedger = Hedger(model, model.inputs()) >>> hedger.compute_pl(derivative) tensor([..., ...]) 
 - compute_pnl(derivative, hedge=None, n_paths=1000, init_state=None)[source]¶
- (deprecated) Simulates derivative and computes profit loss by - compute_pl().
 - compute_portfolio(derivative, hedge=None)[source]¶
- Compute terminal value of the hedging portfolio. - See - pfhedge.nn.functional.pl(), with \(Z\) being substituted with 0, for the expression of the terminal value of the hedging portfolio.- This method assumes that the derivative is already simulated. - Parameters
- derivative (BaseDerivative) – The derivative to hedge. 
- hedge (BaseInstrument, optional) – The hedging instrument. If - None(default), use- derivative.underlier.
 
 - Shape:
- output: \((N)\) where \(N\) is the number of paths. 
 
 - Returns
- torch.Tensor 
 
 - fit(derivative, hedge=None, n_epochs=100, n_paths=1000, n_times=1, optimizer=<class 'torch.optim.adam.Adam'>, init_state=None, verbose=True, validation=True, tqdm_kwargs={})[source]¶
- Fit the hedging model to hedge a given derivative. - The training is performed so that the hedger minimizes - criterion(pl)where- plis given by- compute_pl().- It returns the training history, that is, validation loss after each simulation. - Parameters
- derivative (BaseDerivative) – The derivative to hedge. 
- hedge (list[BaseInstrument], optional) – The hedging instruments. If - None(default), use- [derivative.underlier].
- n_epochs (int, default=100) – Number of Monte-Carlo simulations. 
- n_paths (int, default=1000) – The number of simulated price paths of the underlying instrument. 
- n_times (int, default=1) – If - n_times > 1, returns the ensemble mean of the losses computed through multiple simulations.
- optimizer (torch.optim.Optimizer, default=Adam) – The optimizer algorithm to use. It can be an instance or a class of - torch.optim.Optimizer.
- init_state (tuple, optional) – The initial price of the underlying instrument of the derivative. If - None(default), sensible default value is used.
- verbose (bool, default=True) – If - True, print progress of the training to standard output.
- validation (bool, default=True) – If - False, skip the computation of the validation loss and returns- None.
- tqdm_kwargs (dict, default={}) – Keyword argument passed to - tqdm.__init__to customize the progress bar.
 
- Returns
- list[float] 
 - Examples - >>> from pfhedge.instruments import BrownianStock >>> from pfhedge.instruments import EuropeanOption >>> from pfhedge.nn import MultiLayerPerceptron ... >>> derivative = EuropeanOption(BrownianStock()) >>> model = MultiLayerPerceptron() >>> hedger = Hedger(model, ["moneyness", "time_to_maturity", "volatility"]) >>> history = hedger.fit(derivative, n_paths=1, n_epochs=1, verbose=False) - One can use a custom optimizer as follows. - >>> from pfhedge.instruments import BrownianStock >>> from pfhedge.instruments import EuropeanOption >>> from pfhedge.nn import MultiLayerPerceptron >>> from torch.optim import SGD ... >>> derivative = EuropeanOption(BrownianStock()) >>> hedger = Hedger(MultiLayerPerceptron(), ["empty"]) >>> # Run a placeholder forward to initialize lazy parameters >>> _ = hedger.compute_pnl(derivative, n_paths=1) >>> _ = hedger.fit( ... derivative, ... optimizer=SGD(hedger.parameters(), lr=0.1), ... n_epochs=1, ... verbose=False) - One can also pass a class object of an optimizer. The optimizer will be initialized as - Adadelta(hedger.parameters()).- >>> from torch.optim import Adadelta ... >>> derivative = EuropeanOption(BrownianStock()) >>> hedger = Hedger(MultiLayerPerceptron(), ["empty"]) >>> _ = hedger.fit( ... derivative, ... optimizer=Adadelta, ... n_epochs=1, ... verbose=False) 
 - forward(input)[source]¶
- Returns the outout of - self.model.- The output represents the hedge ratio at the next time step. 
 - get_input(derivative, time_step)[source]¶
- Returns the input tensor to the model at the given time step. - Note - This method assumes that a derivative is already registered to the features. If self has not yet hedged a derivative, run a placeholder computation - _ = self.compute_pnl(derivative, n_paths=1)before calling this method.- Parameters
- derivative (BaseDerivative) – The derivative used for getting the input. 
- time_step (int, optional) – The time step to get the input tensor. If - Nonean input tensor for all time steps is returned.
 
 - Shape:
- Output: \((N, T, F)\) where \(N\) is the number of paths, \(T\) is the number of time steps, and \(F\) is the number of input features. If - time_stepis specified, \(T = 1\).
 
 - Returns
- torch.Tensor 
 - Examples - >>> from pfhedge.instruments import BrownianStock >>> from pfhedge.instruments import EuropeanOption >>> from pfhedge.nn import Naked ... >>> derivative = EuropeanOption(BrownianStock()) >>> derivative.simulate() >>> hedger = Hedger(Naked(), ["time_to_maturity", "volatility"]) >>> _ = hedger.compute_pnl(derivative, n_paths=1) # Materialize features >>> hedger.get_input(derivative, 0) tensor([[[0.0800, 0.2000]]]) 
 - price(derivative, hedge=None, n_paths=1000, n_times=1, init_state=None, enable_grad=False)[source]¶
- Evaluate the premium of the given derivative. - Parameters
- derivative (BaseDerivative) – The derivative to price. 
- hedge (list[BaseInstrument], optional) – The hedging instruments. If - None(default), use- [derivative.underlier].
- n_paths (int, default=1000) – The number of simulated price paths of the underlying instrument. 
- n_times (int, default=1) – If - n_times > 1, returns the ensemble mean of the losses computed through multiple simulations.
- init_state (tuple, optional) – The initial price of the underlying instrument of the derivative. If - None(default), it uses the default value of the underlying instrument.
- enable_grad (bool, default=False) – Context-manager that sets gradient calculation to on or off. 
 
 - Shape:
- Output: \(()\) 
 
 - Returns
- torch.Tensor 
 - Examples - >>> from pfhedge.instruments import BrownianStock >>> from pfhedge.instruments import EuropeanOption >>> from pfhedge.nn import BlackScholes >>> from pfhedge.nn import Hedger ... >>> derivative = EuropeanOption(BrownianStock()) >>> model = BlackScholes(derivative) >>> hedger = Hedger(model, model.inputs()) >>> hedger.price(derivative, n_paths=2) tensor(...)