/** * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.covariance; import java.util.Iterator; import org.apache.commons.lang.ObjectUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.opengamma.analytics.financial.timeseries.returns.ContinuouslyCompoundedRelativeTimeSeriesReturnCalculator; import com.opengamma.analytics.financial.timeseries.returns.RelativeTimeSeriesReturnCalculator; import com.opengamma.analytics.financial.timeseries.returns.TimeSeriesReturnCalculator; import com.opengamma.timeseries.date.localdate.LocalDateDoubleTimeSeries; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.CalculationMode; /** * The historical high-low-close volatility of a price time series can be * calculated using: * $$ * \begin{eqnarray*} * \sigma = \frac{1}{n}\sum\limits_{i=1}^n \frac{rr_i}{2} - \frac{1}{n}\sum\limits_{i=1}^n (2\ln{2} - 1) r_i^2 * \end{eqnarray*} * $$ * where $rr_i$ is the $i^\text{th}$ period *relative* return of the high and * low prices, $rr_i$ is the $i^\text{th}$ period return of the close price and * $n$ is the number of data points in the price series. * <p> * Although any relative return calculator can be used, to get correct results * the calculator should be a {@link ContinuouslyCompoundedRelativeTimeSeriesReturnCalculator}. */ public class HistoricalVolatilityHighLowCloseCalculator extends HistoricalVolatilityCalculator { /** The logger */ private static final Logger s_logger = LoggerFactory.getLogger(HistoricalVolatilityHighLowCloseCalculator.class); /** The return calculator */ private final TimeSeriesReturnCalculator _returnCalculator; /** The relative return calculator */ private final RelativeTimeSeriesReturnCalculator _relativeReturnCalculator; /** * Creates a calculator with the given return and relative return calculation * method and default values for the calculation mode and allowable * percentage of bad data points * @param returnCalculator The return calculator, not null * @param relativeReturnCalculator The relative return calculator, not null */ public HistoricalVolatilityHighLowCloseCalculator(final TimeSeriesReturnCalculator returnCalculator, final RelativeTimeSeriesReturnCalculator relativeReturnCalculator) { super(); ArgumentChecker.notNull(returnCalculator, "return calculator"); ArgumentChecker.notNull(relativeReturnCalculator, "relative return calculator"); _returnCalculator = returnCalculator; _relativeReturnCalculator = relativeReturnCalculator; } /** * Creates a calculator with the given return and relative return calculation method and default values for the calculation mode and allowable percentage of bad data points * @param returnCalculator The return calculator, not null * @param relativeReturnCalculator The relative return calculator, not null * @param mode The calculation mode, not null */ public HistoricalVolatilityHighLowCloseCalculator(final TimeSeriesReturnCalculator returnCalculator, final RelativeTimeSeriesReturnCalculator relativeReturnCalculator, final CalculationMode mode) { super(mode); ArgumentChecker.notNull(returnCalculator, "return calculator"); ArgumentChecker.notNull(relativeReturnCalculator, "relative return calculator"); _returnCalculator = returnCalculator; _relativeReturnCalculator = relativeReturnCalculator; } /** * Creates a calculator with the given return and relative return calculation method and default values for the calculation mode and allowable percentage of bad data points * @param returnCalculator The return calculator, not null * @param relativeReturnCalculator The relative return calculator, not null * @param mode The calculation mode, not null * @param percentBadDataPoints The maximum allowable percentage of bad data points */ public HistoricalVolatilityHighLowCloseCalculator(final TimeSeriesReturnCalculator returnCalculator, final RelativeTimeSeriesReturnCalculator relativeReturnCalculator, final CalculationMode mode, final double percentBadDataPoints) { super(mode, percentBadDataPoints); ArgumentChecker.notNull(returnCalculator, "return calculator"); ArgumentChecker.notNull(relativeReturnCalculator, "relative return calculator"); _returnCalculator = returnCalculator; _relativeReturnCalculator = relativeReturnCalculator; } /** * The array of time series assumes that the first series is the high price series, the second series the low and the third the close. * @param x The array of price time series * @return The historical close volatility * @throws IllegalArgumentException If the array is null or empty; if the first element of the array is null; if the array does not contain three time series; * if the high, low and close time series do not satisfy the requirements (see {@link HistoricalVolatilityCalculator#testHighLowClose}); if the price series does not contain at * least two data points */ @Override public Double evaluate(final LocalDateDoubleTimeSeries... x) { testTimeSeries(x, 2); if (x.length < 3) { throw new IllegalArgumentException("Need high, low and close time series to calculate high-low-close volatility"); } if (x.length > 3) { s_logger.info("Time series array contained more than three series; only using the first three"); } final LocalDateDoubleTimeSeries high = x[0]; final LocalDateDoubleTimeSeries low = x[1]; final LocalDateDoubleTimeSeries close = x[2]; testHighLowClose(high, low, close); final LocalDateDoubleTimeSeries closeReturns = _returnCalculator.evaluate(close); final LocalDateDoubleTimeSeries highLowReturns = _relativeReturnCalculator.evaluate(new LocalDateDoubleTimeSeries[] {high, low}); final Iterator<Double> highLowIterator = highLowReturns.valuesIterator(); final Iterator<Double> closeReturnIterator = closeReturns.valuesIterator(); double value, highLowValue; double sumHL = 0; double sum = 0; highLowIterator.next(); while (closeReturnIterator.hasNext()) { value = closeReturnIterator.next(); highLowValue = highLowIterator.next(); sum += value * value; sumHL += highLowValue * highLowValue; } final int n = closeReturns.size(); return Math.sqrt((0.5 * sumHL - (2 * Math.log(2) - 1) * sum) / n); } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + ((_relativeReturnCalculator == null) ? 0 : _relativeReturnCalculator.hashCode()); result = prime * result + ((_returnCalculator == null) ? 0 : _returnCalculator.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (!super.equals(obj)) { return false; } if (getClass() != obj.getClass()) { return false; } final HistoricalVolatilityHighLowCloseCalculator other = (HistoricalVolatilityHighLowCloseCalculator) obj; if (!ObjectUtils.equals(_relativeReturnCalculator, other._relativeReturnCalculator)) { return false; } return ObjectUtils.equals(_returnCalculator, other._returnCalculator); } }