/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.sesame;
import java.util.ArrayList;
import java.util.Collection;
import org.threeten.bp.LocalDate;
import org.threeten.bp.Period;
import com.opengamma.core.historicaltimeseries.HistoricalTimeSeries;
import com.opengamma.core.value.MarketDataRequirementNames;
import com.opengamma.financial.analytics.timeseries.HistoricalTimeSeriesBundle;
import com.opengamma.financial.convention.frequency.PeriodFrequency;
import com.opengamma.financial.security.FinancialSecurity;
import com.opengamma.financial.security.FinancialSecurityVisitorAdapter;
import com.opengamma.financial.security.fra.ForwardRateAgreementSecurity;
import com.opengamma.financial.security.future.BondFutureSecurity;
import com.opengamma.financial.security.future.DeliverableSwapFutureSecurity;
import com.opengamma.financial.security.future.FederalFundsFutureSecurity;
import com.opengamma.financial.security.future.InterestRateFutureSecurity;
import com.opengamma.financial.security.irs.FloatingInterestRateSwapLeg;
import com.opengamma.financial.security.irs.InterestRateSwapSecurity;
import com.opengamma.financial.security.option.BondFutureOptionSecurity;
import com.opengamma.financial.security.option.IRFutureOptionSecurity;
import com.opengamma.financial.security.option.SwaptionSecurity;
import com.opengamma.financial.security.swap.InflationIndexSwapLeg;
import com.opengamma.financial.security.swap.SwapLeg;
import com.opengamma.financial.security.swap.ZeroCouponInflationSwapSecurity;
import com.opengamma.id.ExternalId;
import com.opengamma.id.ExternalIdBundle;
import com.opengamma.id.UniqueId;
import com.opengamma.sesame.marketdata.HistoricalMarketDataFn;
import com.opengamma.timeseries.date.localdate.LocalDateDoubleTimeSeries;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.money.Currency;
import com.opengamma.util.result.FailureStatus;
import com.opengamma.util.result.Result;
import com.opengamma.util.time.LocalDateRange;
/**
* Function implementation that provides a historical time-series bundle.
*/
public class DefaultFixingsFn implements FixingsFn {
private static final HistoricalTimeSeriesBundle EMPTY_TIME_SERIES_BUNDLE = new HistoricalTimeSeriesBundle();
private static final Period ONE_MONTH = Period.ofMonths(1);
private final HistoricalMarketDataFn _historicalMarketDataFn;
public DefaultFixingsFn(HistoricalMarketDataFn historicalMarketDataFn) {
_historicalMarketDataFn = ArgumentChecker.notNull(historicalMarketDataFn, "historicalMarketDataFn");
}
@Override
public Result<HistoricalTimeSeriesBundle> getFixingsForSecurity(Environment env, FinancialSecurity security) {
FixingRetriever retriever = new FixingRetriever(env, env.getValuationDate());
try {
return security.accept(retriever);
} catch (Exception ex) {
return Result.failure(ex);
}
}
/**
* Class that returns a timeseries bundle of the fixing timeseries required by a security.
*/
private final class FixingRetriever extends FinancialSecurityVisitorAdapter<Result<HistoricalTimeSeriesBundle>> {
private final Environment _env;
/**
* end date of the fixing series required
*/
private final LocalDate _valuationDate;
/**
* @param env the environment used for the calculations
* @param valuationDate the valuation date
*/
private FixingRetriever(Environment env, LocalDate valuationDate) {
_env = env;
_valuationDate = valuationDate;
}
/**
* Returns a time series bundle of the previous month's market values for the specified security.
*
* @param dataField the data field, usually Market_Value
* @param period the period of time to return data for
* @param ids the externalIdBundles to get series for
* @return a historical time series bundle
*/
private Result<HistoricalTimeSeriesBundle> getTimeSeriesBundle(String dataField,
LocalDate start,
Period period,
ExternalIdBundle... ids) {
HistoricalTimeSeriesBundle bundle = new HistoricalTimeSeriesBundle();
Result<?> result = Result.success(true);
for (ExternalIdBundle id : ids) {
Result<HistoricalTimeSeries> series = getPreviousPeriodValues(id, period, start);
if (series.isSuccess()) {
bundle.add(dataField, id, series.getValue());
} else {
result = Result.failure(result, series);
}
}
if (result.isSuccess()) {
return Result.success(bundle);
}
return Result.failure(result);
}
/**
* Returns a time series bundle of the previous month's market values for the specified security.
*
* @param dataField the data field, usually Market_Value
* @param period the period of time to return data for
* @param ids the externalIdBundles to get series for
* @return a historical time series bundle
*/
private Result<HistoricalTimeSeriesBundle> getTimeSeriesBundle(String dataField,
Period period,
ExternalIdBundle... ids) {
return getTimeSeriesBundle(dataField, _valuationDate, period, ids);
}
/**
* Returns a time series of the previous periods values for the specified external id
*
* @param id the external id of used to lookup the field values.
* @param length the length of time to get values for.
* @param date the date to get values for.
* @return the time series result
*/
private Result<HistoricalTimeSeries> getPreviousPeriodValues(ExternalIdBundle id,
Period length,
LocalDate date) {
LocalDate periodStartDate = (date.isBefore(_valuationDate) ? date : _valuationDate).minus(length);
LocalDate periodEndDate = date.isAfter(_valuationDate) ? date : _valuationDate;
return getHistoricalTimeSeriesResult(_env, id, periodStartDate, periodEndDate);
}
@Override
public Result<HistoricalTimeSeriesBundle> visitFederalFundsFutureSecurity(FederalFundsFutureSecurity security) {
return getTimeSeriesBundle(MarketDataRequirementNames.MARKET_VALUE,
ONE_MONTH,
security.getExternalIdBundle(),
security.getUnderlyingId().toBundle());
}
@Override
public Result<HistoricalTimeSeriesBundle> visitInterestRateFutureSecurity(InterestRateFutureSecurity security) {
return getTimeSeriesBundle(MarketDataRequirementNames.MARKET_VALUE, ONE_MONTH, security.getExternalIdBundle());
}
@Override
public Result<HistoricalTimeSeriesBundle> visitIRFutureOptionSecurity(IRFutureOptionSecurity security) {
return getTimeSeriesBundle(MarketDataRequirementNames.MARKET_VALUE, ONE_MONTH, security.getExternalIdBundle());
}
@Override
public Result<HistoricalTimeSeriesBundle> visitSwaptionSecurity(SwaptionSecurity security) {
if (security.getCurrency().equals(Currency.BRL)) {
throw new UnsupportedOperationException("Fixing series for Brazilian swaptions not yet implemented");
}
return Result.success(EMPTY_TIME_SERIES_BUNDLE);
}
@Override
public Result<HistoricalTimeSeriesBundle> visitBondFutureSecurity(BondFutureSecurity security) {
return getTimeSeriesBundle(MarketDataRequirementNames.MARKET_VALUE, ONE_MONTH, security.getExternalIdBundle());
}
@Override
public Result<HistoricalTimeSeriesBundle> visitDeliverableSwapFutureSecurity(DeliverableSwapFutureSecurity security) {
return getTimeSeriesBundle(MarketDataRequirementNames.MARKET_VALUE, ONE_MONTH, security.getExternalIdBundle());
}
@Override
public Result<HistoricalTimeSeriesBundle> visitBondFutureOptionSecurity(BondFutureOptionSecurity security) {
return getTimeSeriesBundle(MarketDataRequirementNames.MARKET_VALUE, ONE_MONTH, security.getExternalIdBundle());
}
@Override
public Result<HistoricalTimeSeriesBundle> visitInterestRateSwapSecurity(InterestRateSwapSecurity security) {
Collection<ExternalIdBundle> ids = new ArrayList<>();
for (FloatingInterestRateSwapLeg leg : security.getLegs(FloatingInterestRateSwapLeg.class)) {
ExternalId id = leg.getFloatingReferenceRateId();
ids.add(id.toBundle());
}
return getTimeSeriesBundle(MarketDataRequirementNames.MARKET_VALUE,
security.getEffectiveDate(),
Period.ofYears(1),
ids.toArray(new ExternalIdBundle[ids.size()]));
}
@Override
public Result<HistoricalTimeSeriesBundle> visitZeroCouponInflationSwapSecurity(ZeroCouponInflationSwapSecurity security) {
Collection<ExternalIdBundle> ids = new ArrayList<>();
SwapLeg payLeg = security.getPayLeg();
SwapLeg receiveLeg = security.getReceiveLeg();
if (payLeg instanceof InflationIndexSwapLeg) {
ExternalId id = ((InflationIndexSwapLeg) payLeg).getIndexId();
ids.add(id.toBundle());
}
if (receiveLeg instanceof InflationIndexSwapLeg) {
ExternalId id = ((InflationIndexSwapLeg) receiveLeg).getIndexId();
ids.add(id.toBundle());
}
return getTimeSeriesBundle(MarketDataRequirementNames.MARKET_VALUE,
security.getEffectiveDate().toLocalDate(),
Period.ofYears(1),
ids.toArray(new ExternalIdBundle[ids.size()]));
}
@Override
public Result<HistoricalTimeSeriesBundle> visitForwardRateAgreementSecurity(ForwardRateAgreementSecurity security) {
ExternalIdBundle id = security.getUnderlyingId().toBundle();
PeriodFrequency period = PeriodFrequency.convertToPeriodFrequency(security.getIndexFrequency());
return getTimeSeriesBundle(MarketDataRequirementNames.MARKET_VALUE, period.getPeriod(), id);
}
}
/**
* Wraps the timeseries call and return a Result
* @param id the id
* @param startDate the start date
* @param endDate the end date
* @return the time series Result
*/
private Result<HistoricalTimeSeries> getHistoricalTimeSeriesResult(Environment env,
ExternalIdBundle id,
LocalDate startDate,
LocalDate endDate) {
LocalDateRange dateRange = LocalDateRange.of(startDate, endDate, true);
Result<LocalDateDoubleTimeSeries> seriesResult = _historicalMarketDataFn.getMarketValues(env, id, dateRange);
if (!seriesResult.isSuccess()) {
return Result.failure(seriesResult);
}
LocalDateDoubleTimeSeries timeSeries = seriesResult.getValue();
if (timeSeries.isEmpty()) {
return Result.failure(FailureStatus.MISSING_DATA, "Time series for {} is empty", id);
} else {
HistoricalTimeSeries historicalTimeSeries = new LegacyHistoricalTimeSeries(timeSeries);
return Result.success(historicalTimeSeries);
}
}
private static class LegacyHistoricalTimeSeries implements HistoricalTimeSeries {
private LocalDateDoubleTimeSeries _timeSeries;
private LegacyHistoricalTimeSeries(LocalDateDoubleTimeSeries timeSeries) {
_timeSeries = timeSeries;
}
@Override
public UniqueId getUniqueId() {
throw new UnsupportedOperationException("getUniqueId not supported");
}
@Override
public LocalDateDoubleTimeSeries getTimeSeries() {
return _timeSeries;
}
}
}