//------------------------------------------------------------------------- /// <summary> /// Calculates the price of the foreign exchange vanilla option product. /// <para> /// The price of the product is the value on the valuation date for one unit of the base currency /// and is expressed in the counter currency. The price does not take into account the long/short flag. /// See <seealso cref="#presentValue"/> for scaling and currency. /// /// </para> /// </summary> /// <param name="option"> the option product </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="volatilities"> the Black volatility provider </param> /// <returns> the price of the product </returns> public virtual double price(ResolvedFxVanillaOption option, RatesProvider ratesProvider, BlackFxOptionSmileVolatilities volatilities) { validate(ratesProvider, volatilities); double timeToExpiry = volatilities.relativeTime(option.Expiry); if (timeToExpiry <= 0d) { return(0d); } ResolvedFxSingle underlyingFx = option.Underlying; Currency ccyCounter = option.CounterCurrency; double df = ratesProvider.discountFactor(ccyCounter, underlyingFx.PaymentDate); FxRate forward = fxPricer.forwardFxRate(underlyingFx, ratesProvider); CurrencyPair currencyPair = underlyingFx.CurrencyPair; double forwardRate = forward.fxRate(currencyPair); double strikeRate = option.Strike; bool isCall = option.PutCall.Call; SmileDeltaParameters smileAtTime = volatilities.Smile.smileForExpiry(timeToExpiry); double[] strikes = smileAtTime.strike(forwardRate).toArray(); double[] vols = smileAtTime.Volatility.toArray(); double volAtm = vols[1]; double[] x = vannaVolgaWeights(forwardRate, strikeRate, timeToExpiry, volAtm, strikes); double priceFwd = BlackFormulaRepository.price(forwardRate, strikeRate, timeToExpiry, volAtm, isCall); for (int i = 0; i < 3; i += 2) { double priceFwdAtm = BlackFormulaRepository.price(forwardRate, strikes[i], timeToExpiry, volAtm, isCall); double priceFwdSmile = BlackFormulaRepository.price(forwardRate, strikes[i], timeToExpiry, vols[i], isCall); priceFwd += x[i] * (priceFwdSmile - priceFwdAtm); } return(df * priceFwd); }
public SwapTrade trade(double quantity, MarketData marketData, ReferenceData refData) { double marketQuote = marketData.getValue(spreadId) + additionalSpread; FxRate fxRate = marketData.getValue(fxRateId); double rate = fxRate.fxRate(template.CurrencyPair); BuySell buySell = quantity > 0 ? BuySell.SELL : BuySell.BUY; return(template.createTrade(marketData.ValuationDate, buySell, Math.Abs(quantity), rate, marketQuote, refData)); }
public FxSwapTrade trade(double quantity, MarketData marketData, ReferenceData refData) { FxRate fxRate = marketData.getValue(fxRateId); double rate = fxRate.fxRate(template.CurrencyPair); double fxPts = marketData.getValue(farForwardPointsId); BuySell buySell = quantity > 0 ? BuySell.BUY : BuySell.SELL; return(template.createTrade(marketData.ValuationDate, buySell, Math.Abs(quantity), rate, fxPts, refData)); }
public virtual void test_trade() { FxSwapCurveNode node = FxSwapCurveNode.of(TEMPLATE, QUOTE_ID_PTS); FxSwapTrade trade = node.trade(1d, MARKET_DATA, REF_DATA); double rate = FX_RATE_NEAR.fxRate(EUR_USD); FxSwapTrade expected = TEMPLATE.createTrade(VAL_DATE, BuySell.BUY, 1.0, rate, FX_RATE_PTS, REF_DATA); assertEquals(trade, expected); assertEquals(node.resolvedTrade(1d, MARKET_DATA, REF_DATA), trade.resolve(REF_DATA)); }
public virtual void test_trade() { XCcyIborIborSwapCurveNode node = XCcyIborIborSwapCurveNode.of(TEMPLATE, SPREAD_ID, SPREAD_ADJ); double quantity = -1234.56; SwapTrade trade = node.trade(quantity, MARKET_DATA, REF_DATA); double rate = FX_EUR_USD.fxRate(Currency.EUR, Currency.USD); SwapTrade expected = TEMPLATE.createTrade(VAL_DATE, BUY, -quantity, rate, SPREAD_XCS + SPREAD_ADJ, REF_DATA); assertEquals(trade, expected); assertEquals(node.resolvedTrade(quantity, MARKET_DATA, REF_DATA), trade.resolve(REF_DATA)); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the implied Black volatility of the foreign exchange vanilla option product. /// </summary> /// <param name="option"> the option product </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="volatilities"> the Black volatility provider </param> /// <returns> the implied volatility of the product </returns> /// <exception cref="IllegalArgumentException"> if the option has expired </exception> public virtual double impliedVolatility(ResolvedFxVanillaOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities) { double timeToExpiry = volatilities.relativeTime(option.Expiry); if (timeToExpiry <= 0d) { throw new System.ArgumentException("valuation is after option's expiry."); } FxRate forward = fxPricer.forwardFxRate(option.Underlying, ratesProvider); CurrencyPair strikePair = option.Underlying.CurrencyPair; return(volatilities.volatility(strikePair, option.Expiry, option.Strike, forward.fxRate(strikePair))); }
/// <summary> /// Calculates the currency exposure of the foreign exchange vanilla option product. /// </summary> /// <param name="option"> the option product </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="volatilities"> the Black volatility provider </param> /// <returns> the currency exposure </returns> public virtual MultiCurrencyAmount currencyExposure(ResolvedFxVanillaOption option, RatesProvider ratesProvider, BlackFxOptionSmileVolatilities volatilities) { validate(ratesProvider, volatilities); double timeToExpiry = volatilities.relativeTime(option.Expiry); if (timeToExpiry <= 0d) { return(MultiCurrencyAmount.empty()); } ResolvedFxSingle underlyingFx = option.Underlying; Currency ccyCounter = option.CounterCurrency; double df = ratesProvider.discountFactor(ccyCounter, underlyingFx.PaymentDate); FxRate forward = fxPricer.forwardFxRate(underlyingFx, ratesProvider); CurrencyPair currencyPair = underlyingFx.CurrencyPair; double spot = ratesProvider.fxRate(currencyPair); double forwardRate = forward.fxRate(currencyPair); double fwdRateSpotSensitivity = fxPricer.forwardFxRateSpotSensitivity(option.PutCall.Call ? underlyingFx : underlyingFx.inverse(), ratesProvider); double strikeRate = option.Strike; bool isCall = option.PutCall.Call; SmileDeltaParameters smileAtTime = volatilities.Smile.smileForExpiry(timeToExpiry); double[] strikes = smileAtTime.strike(forwardRate).toArray(); double[] vols = smileAtTime.Volatility.toArray(); double volAtm = vols[1]; double[] x = vannaVolgaWeights(forwardRate, strikeRate, timeToExpiry, volAtm, strikes); double priceFwd = BlackFormulaRepository.price(forwardRate, strikeRate, timeToExpiry, volAtm, isCall); double deltaFwd = BlackFormulaRepository.delta(forwardRate, strikeRate, timeToExpiry, volAtm, isCall); for (int i = 0; i < 3; i += 2) { double priceFwdAtm = BlackFormulaRepository.price(forwardRate, strikes[i], timeToExpiry, volAtm, isCall); double priceFwdSmile = BlackFormulaRepository.price(forwardRate, strikes[i], timeToExpiry, vols[i], isCall); priceFwd += x[i] * (priceFwdSmile - priceFwdAtm); double deltaFwdAtm = BlackFormulaRepository.delta(forwardRate, strikes[i], timeToExpiry, volAtm, isCall); double deltaFwdSmile = BlackFormulaRepository.delta(forwardRate, strikes[i], timeToExpiry, vols[i], isCall); deltaFwd += x[i] * (deltaFwdSmile - deltaFwdAtm); } double price = df * priceFwd; double delta = df * deltaFwd * fwdRateSpotSensitivity; double signedNotional = this.signedNotional(option); CurrencyAmount domestic = CurrencyAmount.of(currencyPair.Counter, (price - delta * spot) * signedNotional); CurrencyAmount foreign = CurrencyAmount.of(currencyPair.Base, delta * signedNotional); return(MultiCurrencyAmount.of(domestic, foreign)); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the Black theta of the foreign exchange vanilla option product. /// <para> /// The theta is the negative of the first derivative of <seealso cref="#price"/> with respect to time parameter /// in Black formula (the discounted driftless theta). /// /// </para> /// </summary> /// <param name="option"> the option product </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="volatilities"> the Black volatility provider </param> /// <returns> the theta of the product </returns> public virtual double theta(ResolvedFxVanillaOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities) { double timeToExpiry = volatilities.relativeTime(option.Expiry); if (timeToExpiry <= 0d) { return(0d); } ResolvedFxSingle underlying = option.Underlying; FxRate forward = fxPricer.forwardFxRate(underlying, ratesProvider); CurrencyPair strikePair = underlying.CurrencyPair; double forwardRate = forward.fxRate(strikePair); double strikeRate = option.Strike; double volatility = volatilities.volatility(strikePair, option.Expiry, strikeRate, forwardRate); double fwdTheta = BlackFormulaRepository.driftlessTheta(forwardRate, strikeRate, timeToExpiry, volatility); double discountFactor = ratesProvider.discountFactor(option.CounterCurrency, underlying.PaymentDate); return(discountFactor * fwdTheta); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the present value sensitivity of the foreign exchange vanilla option product. /// <para> /// The present value sensitivity of the product is the sensitivity of <seealso cref="#presentValue"/> to /// the underlying curves. /// </para> /// <para> /// The implied strikes and weights are fixed in this sensitivity computation. /// /// </para> /// </summary> /// <param name="option"> the option product </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="volatilities"> the Black volatility provider </param> /// <returns> the present value curve sensitivity of the product </returns> public virtual PointSensitivityBuilder presentValueSensitivityRatesStickyStrike(ResolvedFxVanillaOption option, RatesProvider ratesProvider, BlackFxOptionSmileVolatilities volatilities) { validate(ratesProvider, volatilities); double timeToExpiry = volatilities.relativeTime(option.Expiry); if (timeToExpiry <= 0d) { return(PointSensitivityBuilder.none()); } ResolvedFxSingle underlyingFx = option.Underlying; Currency ccyCounter = option.CounterCurrency; double df = ratesProvider.discountFactor(ccyCounter, underlyingFx.PaymentDate); FxRate forward = fxPricer.forwardFxRate(underlyingFx, ratesProvider); CurrencyPair currencyPair = underlyingFx.CurrencyPair; double forwardRate = forward.fxRate(currencyPair); double strikeRate = option.Strike; bool isCall = option.PutCall.Call; SmileDeltaParameters smileAtTime = volatilities.Smile.smileForExpiry(timeToExpiry); double[] strikes = smileAtTime.strike(forwardRate).toArray(); double[] vols = smileAtTime.Volatility.toArray(); double volAtm = vols[1]; double[] x = vannaVolgaWeights(forwardRate, strikeRate, timeToExpiry, volAtm, strikes); double priceFwd = BlackFormulaRepository.price(forwardRate, strikeRate, timeToExpiry, volAtm, isCall); double deltaFwd = BlackFormulaRepository.delta(forwardRate, strikeRate, timeToExpiry, volAtm, isCall); for (int i = 0; i < 3; i += 2) { double priceFwdAtm = BlackFormulaRepository.price(forwardRate, strikes[i], timeToExpiry, volAtm, isCall); double priceFwdSmile = BlackFormulaRepository.price(forwardRate, strikes[i], timeToExpiry, vols[i], isCall); priceFwd += x[i] * (priceFwdSmile - priceFwdAtm); double deltaFwdAtm = BlackFormulaRepository.delta(forwardRate, strikes[i], timeToExpiry, volAtm, isCall); double deltaFwdSmile = BlackFormulaRepository.delta(forwardRate, strikes[i], timeToExpiry, vols[i], isCall); deltaFwd += x[i] * (deltaFwdSmile - deltaFwdAtm); } double signedNotional = this.signedNotional(option); PointSensitivityBuilder dfSensi = ratesProvider.discountFactors(ccyCounter).zeroRatePointSensitivity(underlyingFx.PaymentDate).multipliedBy(priceFwd * signedNotional); PointSensitivityBuilder fwdSensi = fxPricer.forwardFxRatePointSensitivity(option.PutCall.Call ? underlyingFx : underlyingFx.inverse(), ratesProvider).multipliedBy(df * deltaFwd * signedNotional); return(dfSensi.combinedWith(fwdSensi)); }
/// <summary> /// Computes the present value sensitivity to the black volatilities used in the pricing. /// <para> /// The implied strikes and weights are fixed in this sensitivity computation. /// /// </para> /// </summary> /// <param name="option"> the option product </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="volatilities"> the Black volatility provider </param> /// <returns> the present value sensitivity </returns> public virtual PointSensitivityBuilder presentValueSensitivityModelParamsVolatility(ResolvedFxVanillaOption option, RatesProvider ratesProvider, BlackFxOptionSmileVolatilities volatilities) { validate(ratesProvider, volatilities); double timeToExpiry = volatilities.relativeTime(option.Expiry); if (timeToExpiry <= 0d) { return(PointSensitivityBuilder.none()); } ResolvedFxSingle underlyingFx = option.Underlying; Currency ccyCounter = option.CounterCurrency; double df = ratesProvider.discountFactor(ccyCounter, underlyingFx.PaymentDate); FxRate forward = fxPricer.forwardFxRate(underlyingFx, ratesProvider); CurrencyPair currencyPair = underlyingFx.CurrencyPair; double forwardRate = forward.fxRate(currencyPair); double strikeRate = option.Strike; SmileDeltaParameters smileAtTime = volatilities.Smile.smileForExpiry(timeToExpiry); double[] strikes = smileAtTime.strike(forwardRate).toArray(); double[] vols = smileAtTime.Volatility.toArray(); double volAtm = vols[1]; double[] x = vannaVolgaWeights(forwardRate, strikeRate, timeToExpiry, volAtm, strikes); double vegaAtm = BlackFormulaRepository.vega(forwardRate, strikeRate, timeToExpiry, volAtm); double signedNotional = this.signedNotional(option); PointSensitivityBuilder sensiSmile = PointSensitivityBuilder.none(); for (int i = 0; i < 3; i += 2) { double vegaFwdAtm = BlackFormulaRepository.vega(forwardRate, strikes[i], timeToExpiry, volAtm); vegaAtm -= x[i] * vegaFwdAtm; double vegaFwdSmile = BlackFormulaRepository.vega(forwardRate, strikes[i], timeToExpiry, vols[i]); sensiSmile = sensiSmile.combinedWith(FxOptionSensitivity.of(volatilities.Name, currencyPair, timeToExpiry, strikes[i], forwardRate, ccyCounter, df * signedNotional * x[i] * vegaFwdSmile)); } FxOptionSensitivity sensiAtm = FxOptionSensitivity.of(volatilities.Name, currencyPair, timeToExpiry, strikes[1], forwardRate, ccyCounter, df * signedNotional * vegaAtm); return(sensiAtm.combinedWith(sensiSmile)); }
// the delta without discounting private double undiscountedDelta(ResolvedFxVanillaOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities) { double timeToExpiry = volatilities.relativeTime(option.Expiry); if (timeToExpiry < 0d) { return(0d); } ResolvedFxSingle underlying = option.Underlying; FxRate forward = fxPricer.forwardFxRate(underlying, ratesProvider); CurrencyPair strikePair = underlying.CurrencyPair; double forwardRate = forward.fxRate(strikePair); double strikeRate = option.Strike; bool isCall = option.PutCall.Call; if (timeToExpiry == 0d) { return(isCall ? (forwardRate > strikeRate ? 1d : 0d) : (strikeRate > forwardRate ? -1d : 0d)); } double volatility = volatilities.volatility(strikePair, option.Expiry, strikeRate, forwardRate); return(BlackFormulaRepository.delta(forwardRate, strikeRate, timeToExpiry, volatility, isCall)); }
/// <summary> /// Computes the present value sensitivity to the black volatility used in the pricing. /// <para> /// The result is a single sensitivity to the volatility used. /// /// </para> /// </summary> /// <param name="option"> the option product </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="volatilities"> the Black volatility provider </param> /// <returns> the present value sensitivity </returns> public virtual PointSensitivityBuilder presentValueSensitivityModelParamsVolatility(ResolvedFxVanillaOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities) { if (volatilities.relativeTime(option.Expiry) <= 0d) { return(PointSensitivityBuilder.none()); } ResolvedFxSingle underlying = option.Underlying; FxRate forward = fxPricer.forwardFxRate(underlying, ratesProvider); CurrencyPair strikePair = underlying.CurrencyPair; CurrencyAmount valueVega = presentValueVega(option, ratesProvider, volatilities); return(FxOptionSensitivity.of(volatilities.Name, strikePair, volatilities.relativeTime(option.Expiry), option.Strike, forward.fxRate(strikePair), valueVega.Currency, valueVega.Amount)); }
/// <summary> /// Creates an {@code FxSwap} using decimal forward points, specifying a date adjustment. /// <para> /// The FX rate at the near date is specified as {@code nearRate}. /// The FX rate at the far date is equal to {@code nearRate + forwardPoints} /// Thus "FX forward spread" might be a better name for the concept. /// </para> /// <para> /// The two currencies are specified by the near FX rate. /// The amount must be specified using one of the currencies of the near FX rate. /// The near date must be before the far date. /// Conventions will be used to determine the base and counter currency. /// /// </para> /// </summary> /// <param name="amount"> the amount being exchanged, positive if being received in the near leg, negative if being paid </param> /// <param name="nearRate"> the near FX rate </param> /// <param name="decimalForwardPoints"> the decimal forward points, where the far FX rate is {@code (nearRate + forwardPoints)} </param> /// <param name="nearDate"> the near value date </param> /// <param name="farDate"> the far value date </param> /// <param name="paymentDateAdjustment"> the adjustment to apply to the payment dates </param> /// <returns> the FX swap </returns> /// <exception cref="IllegalArgumentException"> if the FX rate and amount do not have a currency in common </exception> public static FxSwap ofForwardPoints(CurrencyAmount amount, FxRate nearRate, double decimalForwardPoints, LocalDate nearDate, LocalDate farDate, BusinessDayAdjustment paymentDateAdjustment) { FxRate farRate = FxRate.of(nearRate.Pair, nearRate.fxRate(nearRate.Pair) + decimalForwardPoints); return(of(amount, nearRate, nearDate, farRate, farDate, paymentDateAdjustment)); }
/// <summary> /// Creates an {@code FxSwap} using decimal forward points. /// <para> /// The FX rate at the near date is specified as {@code nearRate}. /// The FX rate at the far date is equal to {@code nearRate + forwardPoints}. /// Thus "FX forward spread" might be a better name for the concept. /// </para> /// <para> /// The two currencies are specified by the near FX rate. /// The amount must be specified using one of the currencies of the near FX rate. /// The near date must be before the far date. /// Conventions will be used to determine the base and counter currency. /// /// </para> /// </summary> /// <param name="amount"> the amount being exchanged, positive if being received in the near leg, negative if being paid </param> /// <param name="nearRate"> the near FX rate </param> /// <param name="decimalForwardPoints"> the decimal forward points, where the far FX rate is {@code (nearRate + forwardPoints)} </param> /// <param name="nearDate"> the near value date </param> /// <param name="farDate"> the far value date </param> /// <returns> the FX swap </returns> /// <exception cref="IllegalArgumentException"> if the FX rate and amount do not have a currency in common </exception> public static FxSwap ofForwardPoints(CurrencyAmount amount, FxRate nearRate, double decimalForwardPoints, LocalDate nearDate, LocalDate farDate) { FxRate farRate = FxRate.of(nearRate.Pair, nearRate.fxRate(nearRate.Pair) + decimalForwardPoints); return(of(amount, nearRate, nearDate, farRate, farDate)); }