+ * This class provides a series of static methods that delegate to {@link InheritableThreadLocalReferenceDataHolderStrategy}. + * This strategy uses an {@link InheritableThreadLocal} to store {@code ReferenceData} against a thread. + * + *
+ */ +public class ReferenceDataHolder { + private ReferenceDataHolder() { + // prevent instantiation + } + + private static final InheritableThreadLocalReferenceDataHolderStrategy STRATEGY = new InheritableThreadLocalReferenceDataHolderStrategy(); + + /** + * Sets the current reference data for this thread. + * + * @param refData the new value to hold (should never be null). + * @throws IllegalArgumentException if {@code refData} is null. + */ + public static void setReferenceData(ReferenceData refData) { + ArgChecker.notNull(refData, "refData"); + STRATEGY.setReferenceData(refData); + } + + /** + * Gets the current reference data for this thread. + * If it has not previously been set for this thread then it will be null. + * + * @return current reference data + */ + public static ReferenceData getReferenceData() { + return STRATEGY.getReferenceData(); + } + + /** + * Gets the current reference data for this thread and, if it is not set, return a fallback value instead. + * + * @param other the fallback value to return if the reference data has not been set for this thread (should never be null). + * @return the reference data for this thread, or the provided fallback value + * @throws IllegalArgumentException if {@code other} is null. + */ + public static ReferenceData getReferenceDataWithFallback(ReferenceData other) { + ArgChecker.notNull(other, "other"); + return STRATEGY.getReferenceDataWithFallback(other); + } + + /** + * Executes a function to return a value with reference data set for the duration of the function and then + * cleared afterwards. + * + * @param referenceData the new value to hold (should never be null). + * @param fn the function to call to return a value + * @return the value returned from the function + * @param+ * If {@link ReferenceDataHolder} has been populated for this thread, it will be used to obtain the calendar + * for calculating the spot date from the valuation date. Otherwise the {@link ReferenceData#standard() standard} + * calendar will be used instead. + * + * @param event the event + * @param provider the rates provider + * @return the currency exposure + */ @Override public MultiCurrencyAmount currencyExposure(FxResetNotionalExchange event, RatesProvider provider) { double dfCounterMaturity = provider.discountFactor(event.getCurrency(), event.getPaymentDate()); @@ -120,21 +130,17 @@ public MultiCurrencyAmount currencyExposure(FxResetNotionalExchange event, Rates return MultiCurrencyAmount.of(CurrencyAmount.of(event.getCurrency(), event.getNotional() * dfCounterMaturity * fxRate)); } + ReferenceData refData = ReferenceDataHolder.getReferenceDataWithFallback(ReferenceData.standard()); Currency baseCurrency = event.getReferenceCurrency(); Currency counterCurrency = event.getCurrency(); - LocalDate valuationDate = provider.getValuationDate(); - LocalDate spotDate = FxSwapConvention.of(CurrencyPair.of(baseCurrency, counterCurrency)).calculateSpotDateFromTradeDate(valuationDate, - ReferenceData.standard()); - - double dfCounterSpot = provider.discountFactor(counterCurrency, spotDate); - double dfReferenceSpot = provider.discountFactor(baseCurrency, spotDate); + double dfScaled = FxDfScaler.scaledDf(provider, refData, baseCurrency, counterCurrency, dfCounterMaturity); LocalDate maturityDate = event.getObservation().getMaturityDate(); double fxRateSpotSensitivity = rates.getFxForwardRates().rateFxSpotSensitivity(event.getReferenceCurrency(), maturityDate); return MultiCurrencyAmount.of( CurrencyAmount.of(event.getReferenceCurrency(), event.getNotional() * - fxRateSpotSensitivity * (dfCounterMaturity / dfCounterSpot) * dfReferenceSpot)); + fxRateSpotSensitivity * dfScaled)); } @Override diff --git a/modules/pricer/src/main/java/com/opengamma/strata/pricer/impl/swap/DiscountingRatePaymentPeriodPricer.java b/modules/pricer/src/main/java/com/opengamma/strata/pricer/impl/swap/DiscountingRatePaymentPeriodPricer.java index db24bdb712..ad378091c5 100644 --- a/modules/pricer/src/main/java/com/opengamma/strata/pricer/impl/swap/DiscountingRatePaymentPeriodPricer.java +++ b/modules/pricer/src/main/java/com/opengamma/strata/pricer/impl/swap/DiscountingRatePaymentPeriodPricer.java @@ -11,9 +11,9 @@ import com.google.common.collect.ImmutableList; import com.opengamma.strata.basics.ReferenceData; +import com.opengamma.strata.basics.ReferenceDataHolder; import com.opengamma.strata.basics.currency.Currency; import com.opengamma.strata.basics.currency.CurrencyAmount; -import com.opengamma.strata.basics.currency.CurrencyPair; import com.opengamma.strata.basics.currency.MultiCurrencyAmount; import com.opengamma.strata.basics.date.DayCount; import com.opengamma.strata.collect.ArgChecker; @@ -25,7 +25,6 @@ import com.opengamma.strata.pricer.rate.RateComputationFn; import com.opengamma.strata.pricer.rate.RatesProvider; import com.opengamma.strata.pricer.swap.SwapPaymentPeriodPricer; -import com.opengamma.strata.product.fx.type.FxSwapConvention; import com.opengamma.strata.product.rate.RateComputation; import com.opengamma.strata.product.swap.CompoundingMethod; import com.opengamma.strata.product.swap.FxReset; @@ -461,6 +460,18 @@ private void explainPresentValue( } //------------------------------------------------------------------------- + + /** + * Calculates the currency exposure of a single payment period. + *
+ * If {@link ReferenceDataHolder} has been populated for this thread, it will be used to obtain the calendar
+ * for calculating the spot date from the valuation date. Otherwise the {@link ReferenceData#standard() standard}
+ * calendar will be used instead.
+ *
+ * @param period the period
+ * @param provider the rates provider
+ * @return the currency exposure
+ */
@Override
public MultiCurrencyAmount currencyExposure(RatePaymentPeriod period, RatesProvider provider) {
double dfCounterMaturity = provider.discountFactor(period.getCurrency(), period.getPaymentDate());
@@ -474,20 +485,16 @@ public MultiCurrencyAmount currencyExposure(RatePaymentPeriod period, RatesProvi
return MultiCurrencyAmount.of(period.getCurrency(),
accrualWithNotional(period, period.getNotional() * fxRate * dfCounterMaturity, provider));
}
+ ReferenceData refData = ReferenceDataHolder.getReferenceDataWithFallback(ReferenceData.standard());
Currency baseCurrency = fxReset.getReferenceCurrency();
Currency counterCurrency = period.getCurrency();
- LocalDate valuationDate = provider.getValuationDate();
- LocalDate spotDate = FxSwapConvention.of(CurrencyPair.of(baseCurrency, counterCurrency)).calculateSpotDateFromTradeDate(valuationDate,
- ReferenceData.standard());
-
- double dfCounterSpot = provider.discountFactor(counterCurrency, spotDate);
- double dfReferenceSpot = provider.discountFactor(baseCurrency, spotDate);
+ double dfScaled = FxDfScaler.scaledDf(provider, refData, baseCurrency, counterCurrency, dfCounterMaturity);
double fxRateSpotSensitivity = rates.getFxForwardRates()
.rateFxSpotSensitivity(fxReset.getReferenceCurrency(), fxReset.getObservation().getMaturityDate());
return MultiCurrencyAmount.of(fxReset.getReferenceCurrency(),
accrualWithNotional(period, period.getNotional() * fxRateSpotSensitivity *
- (dfCounterMaturity / dfCounterSpot) * dfReferenceSpot, provider));
+ dfScaled, provider));
}
return MultiCurrencyAmount.of(period.getCurrency(), accrualWithNotional(period,
period.getNotional() * dfCounterMaturity, provider));
diff --git a/modules/pricer/src/main/java/com/opengamma/strata/pricer/impl/swap/FxDfScaler.java b/modules/pricer/src/main/java/com/opengamma/strata/pricer/impl/swap/FxDfScaler.java
new file mode 100644
index 0000000000..d4173c340b
--- /dev/null
+++ b/modules/pricer/src/main/java/com/opengamma/strata/pricer/impl/swap/FxDfScaler.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 - present by OpenGamma Inc. and the OpenGamma group of companies
+ *
+ * Please see distribution for license.
+ */
+package com.opengamma.strata.pricer.impl.swap;
+
+import java.time.LocalDate;
+
+import com.opengamma.strata.basics.ReferenceData;
+import com.opengamma.strata.basics.currency.Currency;
+import com.opengamma.strata.basics.currency.CurrencyPair;
+import com.opengamma.strata.pricer.rate.RatesProvider;
+import com.opengamma.strata.product.fx.type.FxSwapConvention;
+
+/**
+ * Utility method to scale a discount factor, used by both {@link DiscountingFxResetNotionalExchangePricer} and
+ * {@link DiscountingRatePaymentPeriodPricer}.
+ */
+public final class FxDfScaler {
+
+ private FxDfScaler() {
+ // prevent instantiation
+ }
+
+ /**
+ * Scale a discount factor by the ratio between base and counter currencies at the spot date.
+ *
+ * @param provider the rates provider
+ * @param refData the reference data, used to resolve the spot date from the valuation date
+ * @param baseCurrency the base currency
+ * @param counterCurrency the counter currency
+ * @param df the unscaled discount factor
+ * @return the discount factor scaled by the ratio between base and counter currency discount factors at the spot date
+ */
+ static double scaledDf(RatesProvider provider, ReferenceData refData, Currency baseCurrency, Currency counterCurrency, double df) {
+ LocalDate valuationDate = provider.getValuationDate();
+ LocalDate spotDate = FxSwapConvention.of(CurrencyPair.of(baseCurrency, counterCurrency)).calculateSpotDateFromTradeDate(valuationDate,
+ refData);
+
+ double dfCounterSpot = provider.discountFactor(counterCurrency, spotDate);
+ double dfReferenceSpot = provider.discountFactor(baseCurrency, spotDate);
+ double dfScaled = (df / dfCounterSpot) * dfReferenceSpot;
+ return dfScaled;
+ }
+}
diff --git a/modules/product/pom.xml b/modules/product/pom.xml
index c6438bfadf..3f7c1fe0f1 100644
--- a/modules/product/pom.xml
+++ b/modules/product/pom.xml
@@ -5,7 +5,7 @@