/**
* Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.pricer.index;
import com.opengamma.strata.collect.ArgChecker;
import com.opengamma.strata.market.sensitivity.PointSensitivities;
import com.opengamma.strata.pricer.impl.option.NormalFormulaRepository;
import com.opengamma.strata.pricer.rate.RatesProvider;
import com.opengamma.strata.product.index.IborFutureOption;
import com.opengamma.strata.product.index.ResolvedIborFuture;
import com.opengamma.strata.product.index.ResolvedIborFutureOption;
import com.opengamma.strata.product.option.FutureOptionPremiumStyle;
/**
* Pricer of options on Ibor future with a normal model on the underlying future price.
* <p>
* This provides the ability to price an Ibor future option.
* The option must be based on {@linkplain FutureOptionPremiumStyle#DAILY_MARGIN daily margin}.
*
* <h4>Price</h4>
* The price of an Ibor future option is based on the price of the underlying future, the volatility
* and the time to expiry. The price of the at-the-money option tends to zero as expiry approaches.
* <p>
* Strata uses <i>decimal prices</i> for Ibor future options in the trade model, pricers and market data.
* The decimal price is based on the decimal rate equivalent to the percentage.
* For example, an option price of 0.2 is related to a futures price of 99.32 that implies an
* interest rate of 0.68%. Strata represents the price of the future as 0.9932 and thus
* represents the price of the option as 0.002.
*/
public class NormalIborFutureOptionMarginedProductPricer {
/**
* Default implementation.
*/
public static final NormalIborFutureOptionMarginedProductPricer DEFAULT =
new NormalIborFutureOptionMarginedProductPricer(DiscountingIborFutureProductPricer.DEFAULT);
/**
* The underlying future pricer.
* The pricer take only the curves as inputs, no model parameters.
*/
private final DiscountingIborFutureProductPricer futurePricer;
/**
* Creates an instance.
*
* @param futurePricer the pricer for {@link IborFutureOption}
*/
public NormalIborFutureOptionMarginedProductPricer(
DiscountingIborFutureProductPricer futurePricer) {
this.futurePricer = ArgChecker.notNull(futurePricer, "futurePricer");
}
//-------------------------------------------------------------------------
/**
* Returns the underlying future pricer function.
*
* @return the future pricer
*/
DiscountingIborFutureProductPricer getFuturePricer() {
return futurePricer;
}
//-------------------------------------------------------------------------
/**
* Calculates the number related to Ibor futures product on which the daily margin is computed.
* <p>
* For two consecutive settlement prices C1 and C2, the daily margin is computed as
* {@code marginIndex(future, C2) - marginIndex(future, C1)}.
*
* @param option the option product
* @param price the price of the product, in decimal form
* @return the index
*/
double marginIndex(ResolvedIborFutureOption option, double price) {
double notional = option.getUnderlyingFuture().getNotional();
double accrualFactor = option.getUnderlyingFuture().getAccrualFactor();
return price * notional * accrualFactor;
}
/**
* Calculates the margin index sensitivity of the Ibor future product.
* <p>
* The margin index sensitivity if the sensitivity of the margin index to the underlying curves.
* For two consecutive settlement prices C1 and C2, the daily margin is computed as
* {@code marginIndex(future, C2) - marginIndex(future, C1)}.
*
* @param option the option product
* @param priceSensitivity the price sensitivity of the product
* @return the index sensitivity
*/
PointSensitivities marginIndexSensitivity(
ResolvedIborFutureOption option,
PointSensitivities priceSensitivity) {
double notional = option.getUnderlyingFuture().getNotional();
double accrualFactor = option.getUnderlyingFuture().getAccrualFactor();
return priceSensitivity.multipliedBy(notional * accrualFactor);
}
//-------------------------------------------------------------------------
/**
* Calculates the price of the Ibor future option product.
* <p>
* The price of the option is the price on the valuation date.
* <p>
* This calculates the underlying future price using the future pricer.
*
* @param futureOption the option product
* @param ratesProvider the rates provider
* @param volatilities the volatilities
* @return the price of the product, in decimal form
*/
public double price(
ResolvedIborFutureOption futureOption,
RatesProvider ratesProvider,
NormalIborFutureOptionVolatilities volatilities) {
double futurePrice = futurePrice(futureOption, ratesProvider);
return price(futureOption, ratesProvider, volatilities, futurePrice);
}
/**
* Calculates the price of the Ibor future option product
* based on the price of the underlying future.
* <p>
* The price of the option is the price on the valuation date.
*
* @param futureOption the option product
* @param ratesProvider the rates provider
* @param volatilities the volatilities
* @param futurePrice the price of the underlying future, in decimal form
* @return the price of the product, in decimal form
*/
public double price(
ResolvedIborFutureOption futureOption,
RatesProvider ratesProvider,
NormalIborFutureOptionVolatilities volatilities,
double futurePrice) {
ArgChecker.isTrue(futureOption.getPremiumStyle().equals(FutureOptionPremiumStyle.DAILY_MARGIN),
"Premium style should be DAILY_MARGIN");
ArgChecker.isTrue(futureOption.getUnderlyingFuture().getIndex().equals(volatilities.getIndex()),
"Future index should be the same as data index");
double timeToExpiry = volatilities.relativeTime(futureOption.getExpiry());
double strike = futureOption.getStrikePrice();
ResolvedIborFuture future = futureOption.getUnderlyingFuture();
double volatility = volatilities.volatility(timeToExpiry, future.getLastTradeDate(), strike, futurePrice);
return NormalFormulaRepository.price(futurePrice, strike, timeToExpiry, volatility, futureOption.getPutCall());
}
//-------------------------------------------------------------------------
/**
* Calculates the delta of the Ibor future option product.
* <p>
* The delta of the product is the sensitivity of the option price to the future price.
* The volatility is unchanged for a fixed strike in the sensitivity computation, hence the "StickyStrike" name.
* <p>
* This calculates the underlying future price using the future pricer.
*
* @param futureOption the option product
* @param ratesProvider the rates provider
* @param volatilities the volatilities
* @return the price curve sensitivity of the product
*/
public double deltaStickyStrike(
ResolvedIborFutureOption futureOption,
RatesProvider ratesProvider,
NormalIborFutureOptionVolatilities volatilities) {
double futurePrice = futurePrice(futureOption, ratesProvider);
return deltaStickyStrike(futureOption, ratesProvider, volatilities, futurePrice);
}
/**
* Calculates the delta of the Ibor future option product
* based on the price of the underlying future.
* <p>
* The delta of the product is the sensitivity of the option price to the future price.
* The volatility is unchanged for a fixed strike in the sensitivity computation, hence the "StickyStrike" name.
*
* @param futureOption the option product
* @param ratesProvider the rates provider
* @param volatilities the volatilities
* @param futurePrice the price of the underlying future, in decimal form
* @return the price curve sensitivity of the product
*/
public double deltaStickyStrike(
ResolvedIborFutureOption futureOption,
RatesProvider ratesProvider,
NormalIborFutureOptionVolatilities volatilities,
double futurePrice) {
ArgChecker.isTrue(futureOption.getPremiumStyle().equals(FutureOptionPremiumStyle.DAILY_MARGIN),
"Premium style should be DAILY_MARGIN");
double timeToExpiry = volatilities.relativeTime(futureOption.getExpiry());
double strike = futureOption.getStrikePrice();
ResolvedIborFuture future = futureOption.getUnderlyingFuture();
double volatility = volatilities.volatility(timeToExpiry, future.getLastTradeDate(), strike, futurePrice);
return NormalFormulaRepository.delta(futurePrice, strike, timeToExpiry, volatility, futureOption.getPutCall());
}
//-------------------------------------------------------------------------
/**
* Calculates the price sensitivity of the Ibor future option product based on curves.
* <p>
* The price sensitivity of the product is the sensitivity of the price to the underlying curves.
* The volatility is unchanged for a fixed strike in the sensitivity computation, hence the "StickyStrike" name.
* <p>
* This calculates the underlying future price using the future pricer.
*
* @param futureOption the option product
* @param ratesProvider the rates provider
* @param volatilities the volatilities
* @return the price curve sensitivity of the product
*/
public PointSensitivities priceSensitivityRatesStickyStrike(
ResolvedIborFutureOption futureOption,
RatesProvider ratesProvider,
NormalIborFutureOptionVolatilities volatilities) {
ArgChecker.isTrue(futureOption.getPremiumStyle().equals(FutureOptionPremiumStyle.DAILY_MARGIN),
"Premium style should be DAILY_MARGIN");
double futurePrice = futurePrice(futureOption, ratesProvider);
return priceSensitivityRatesStickyStrike(futureOption, ratesProvider, volatilities, futurePrice);
}
/**
* Calculates the price sensitivity of the Ibor future option product
* based on the price of the underlying future.
* <p>
* The price sensitivity of the product is the sensitivity of the price to the underlying curves.
* The volatility is unchanged for a fixed strike in the sensitivity computation, hence the "StickyStrike" name.
*
* @param futureOption the option product
* @param ratesProvider the rates provider
* @param volatilities the volatilities
* @param futurePrice the price of the underlying future, in decimal form
* @return the price curve sensitivity of the product
*/
public PointSensitivities priceSensitivityRatesStickyStrike(
ResolvedIborFutureOption futureOption,
RatesProvider ratesProvider,
NormalIborFutureOptionVolatilities volatilities,
double futurePrice) {
double delta = deltaStickyStrike(futureOption, ratesProvider, volatilities, futurePrice);
PointSensitivities futurePriceSensitivity =
futurePricer.priceSensitivity(futureOption.getUnderlyingFuture(), ratesProvider);
return futurePriceSensitivity.multipliedBy(delta);
}
//-------------------------------------------------------------------------
/**
* Calculates the price sensitivity to the normal volatility used for the pricing of the Ibor future option.
* <p>
* This sensitivity is also called the <i>price normal vega</i>.
* <p>
* This calculates the underlying future price using the future pricer.
*
* @param futureOption the option product
* @param ratesProvider the rates provider
* @param volatilities the volatilities
* @return the sensitivity
*/
public IborFutureOptionSensitivity priceSensitivityModelParamsVolatility(
ResolvedIborFutureOption futureOption,
RatesProvider ratesProvider,
NormalIborFutureOptionVolatilities volatilities) {
double futurePrice = futurePrice(futureOption, ratesProvider);
return priceSensitivityModelParamsVolatility(futureOption, ratesProvider, volatilities, futurePrice);
}
/**
* Calculates the price sensitivity to the normal volatility used for the pricing of the Ibor future option
* based on the price of the underlying future.
* <p>
* This sensitivity is also called the <i>price normal vega</i>.
*
* @param futureOption the option product
* @param ratesProvider the rates provider
* @param volatilities the volatilities
* @param futurePrice the underlying future price, in decimal form
* @return the sensitivity
*/
public IborFutureOptionSensitivity priceSensitivityModelParamsVolatility(
ResolvedIborFutureOption futureOption,
RatesProvider ratesProvider,
NormalIborFutureOptionVolatilities volatilities,
double futurePrice) {
ArgChecker.isTrue(futureOption.getPremiumStyle().equals(FutureOptionPremiumStyle.DAILY_MARGIN),
"Premium style should be DAILY_MARGIN");
double timeToExpiry = volatilities.relativeTime(futureOption.getExpiry());
double strike = futureOption.getStrikePrice();
ResolvedIborFuture future = futureOption.getUnderlyingFuture();
double volatility = volatilities.volatility(timeToExpiry, future.getLastTradeDate(), strike, futurePrice);
double vega = NormalFormulaRepository.vega(futurePrice, strike, timeToExpiry, volatility, futureOption.getPutCall());
return IborFutureOptionSensitivity.of(
volatilities.getName(), timeToExpiry, future.getLastTradeDate(), strike, futurePrice, future.getCurrency(), vega);
}
//-------------------------------------------------------------------------
// calculate the price of the underlying future
private double futurePrice(ResolvedIborFutureOption futureOption, RatesProvider ratesProvider) {
ResolvedIborFuture future = futureOption.getUnderlyingFuture();
return futurePricer.price(future, ratesProvider);
}
}