/**
* 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 static com.opengamma.analytics.financial.timeseries.util.TimeSeriesDataTestUtils.testNotNullOrEmpty;
import static com.opengamma.analytics.financial.timeseries.util.TimeSeriesDataTestUtils.testTimeSeriesDates;
import static com.opengamma.analytics.financial.timeseries.util.TimeSeriesDataTestUtils.testTimeSeriesSize;
import java.util.Iterator;
import com.opengamma.timeseries.DoubleTimeSeries;
import com.opengamma.timeseries.TimeSeriesException;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.CalculationMode;
/**
* Base class for historical volatility calculators.
*/
public abstract class HistoricalVolatilityCalculator implements VolatilityCalculator {
/** The default calculation mode */
private static final CalculationMode DEFAULT_CALCULATION_MODE = CalculationMode.STRICT;
/** The default percentage of bad data points allowed */
private static final double DEFAULT_PERCENT_BAD_DATA_POINTS = 0.0;
/** The calculation mode */
private final CalculationMode _mode;
/** The percentage of bad data points allowed */
private final double _percentBadDataPoints;
/**
* Sets the calculation mode and acceptable percentage of bad data points to the default value (strict and 0 respectively)
*/
public HistoricalVolatilityCalculator() {
this(DEFAULT_CALCULATION_MODE, DEFAULT_PERCENT_BAD_DATA_POINTS);
}
/**
* Sets the acceptable percentage of bad data points to the default value, 0
* @param mode The calculation mode
*/
public HistoricalVolatilityCalculator(final CalculationMode mode) {
this(mode, DEFAULT_PERCENT_BAD_DATA_POINTS);
}
/**
* @param mode The calculation mode, not null
* @param percentBadDataPoints The acceptable percentage of bad data points, must be >= 0 and <= 1
*/
public HistoricalVolatilityCalculator(final CalculationMode mode, final double percentBadDataPoints) {
ArgumentChecker.notNull(mode, "mode");
ArgumentChecker.isInRangeInclusive(0, 1, percentBadDataPoints);
_mode = mode;
_percentBadDataPoints = percentBadDataPoints;
}
/**
* Checks that the time series array is not null, empty and that the first entry is not null
* @param tsArray The time series array, not null or empty
* @param minLength The minimum of entries in the time series
*/
protected void testTimeSeries(final DoubleTimeSeries<?>[] tsArray, final int minLength) {
ArgumentChecker.notEmpty(tsArray, "array of time series");
testNotNullOrEmpty(tsArray[0]);
final DoubleTimeSeries<?> ts = tsArray[0];
testTimeSeriesSize(ts, minLength);
for (int i = 1; i < tsArray.length; i++) {
testTimeSeriesSize(tsArray[i], minLength);
testTimeSeriesDates(ts, tsArray[i]);
}
}
/**
* Tests that the high price for a date is greater than the low value for the same date.
* @param high The period high price time series
* @param low The period low price time series
* @throws IllegalArgumentException Strict calculation mode: if the low value for a date is greater than the high value. Lenient calculation mode: if the percentage of times
* that the low value for a date is greater than the high value is greater than the maximum allowed
*/
protected void testHighLow(final DoubleTimeSeries<?> high, final DoubleTimeSeries<?> low) {
final double size = high.size();
int count = 0;
final Iterator<Double> highIter = high.valuesIterator();
final Iterator<Double> lowIter = low.valuesIterator();
boolean compare;
while (highIter.hasNext()) {
compare = highIter.next() < lowIter.next();
if (compare) {
if (_mode == CalculationMode.STRICT) {
throw new TimeSeriesException("Not all values in the high series were greater than the values in the low series");
}
count++;
}
}
final double percent = count / size;
if (percent > _percentBadDataPoints) {
throw new TimeSeriesException("Percent " + percent + " of bad data points is greater than " + _percentBadDataPoints);
}
}
/**
* Tests that the high price for a date is greater than the low value for the same date and that the close price falls in this (inclusive) range
* @param high The period high price time series
* @param low The period low price time series
* @param close The period close price time series
* @throws IllegalArgumentException Strict calculation mode: if the low value for a date is greater than the high value or if the close value is not in the range bounded
* by the high and low prices. Lenient calculation mode: if the percentage of times that the low value for a date is greater than the high value or that the close value is
* not in the range bounded by the high and low values is greater than the maximum allowed
*/
protected void testHighLowClose(final DoubleTimeSeries<?> high, final DoubleTimeSeries<?> low, final DoubleTimeSeries<?> close) {
final double size = high.size();
int count = 0;
final Iterator<Double> highIter = high.valuesIterator();
final Iterator<Double> lowIter = low.valuesIterator();
final Iterator<Double> closeIter = close.valuesIterator();
boolean compare;
double highValue, lowValue, closeValue;
while (highIter.hasNext()) {
highValue = highIter.next();
lowValue = lowIter.next();
closeValue = closeIter.next();
compare = highValue < lowValue || closeValue > highValue || closeValue < lowValue;
if (compare) {
if (_mode == CalculationMode.STRICT) {
throw new TimeSeriesException("Not all values in the high series were greater than the values in the low series");
}
count++;
}
}
final double percent = count / size;
if (percent > _percentBadDataPoints) {
throw new TimeSeriesException("Percent " + percent + " of bad data points is greater than " + _percentBadDataPoints);
}
}
/**
*
* @return The default calculation mode
*/
protected static CalculationMode getDefaultCalculationMode() {
return DEFAULT_CALCULATION_MODE;
}
/**
*
* @return The default percentage of bad data points
*/
protected static double getDefaultBadDataPoints() {
return DEFAULT_PERCENT_BAD_DATA_POINTS;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((_mode == null) ? 0 : _mode.hashCode());
long temp;
temp = Double.doubleToLongBits(_percentBadDataPoints);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final HistoricalVolatilityCalculator other = (HistoricalVolatilityCalculator) obj;
if (_mode != other._mode) {
return false;
}
if (Double.doubleToLongBits(_percentBadDataPoints) != Double.doubleToLongBits(other._percentBadDataPoints)) {
return false;
}
return true;
}
}