/** * Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.timeseries.util; import com.opengamma.analytics.math.function.Function1D; import com.opengamma.timeseries.date.DateDoubleTimeSeries; import com.opengamma.timeseries.date.localdate.ImmutableLocalDateDoubleTimeSeries; import com.opengamma.util.ArgumentChecker; /** * Calculates a weighted volatility series from a series of absolute or relative returns. * A seed period can be defined. In that case, the non-weighted variance/volatility is computed for the * number of returns indicated in the seed length. That volatility is used as the starting point of the volatility * computation. The length of the output is reduced by the number of seed. * The length of the output time series is (Return TS length) - (seed length) + 1. */ public final class TimeSeriesWeightedVolatilityOperator extends Function1D<DateDoubleTimeSeries<?>, DateDoubleTimeSeries<?>> { /** Default seed length: 0*/ private static final int DEFAULT_SEED_LENGTH = 0; /** Return operator as relative return on one period. */ private static final TimeSeriesPercentageChangeOperator RELATIVE_CHANGE = new TimeSeriesPercentageChangeOperator(); /** Return operator as absolute return on one period. */ private static final TimeSeriesDifferenceOperator ABSOLUTE_CHANGE = new TimeSeriesDifferenceOperator(); private final Function1D<DateDoubleTimeSeries<?>, DateDoubleTimeSeries<?>> _changeOperator; /** The weight used for the Exponentially Weighted Moving Average computation. */ private final double _lambda; /** The length of the seed period. In the seed period, the variance is computed with equal weight. */ private final int _seedLength; /** * Constructor with a generic return operator and the weight. * @param returnOperator The return operator for time series. * @param lambda The weight of the exponentially weighted moving average. * @param seedLength The length of the seed period. In the seed period, the variance is computed with equal weight. */ public TimeSeriesWeightedVolatilityOperator( Function1D<DateDoubleTimeSeries<?>, DateDoubleTimeSeries<?>> returnOperator, double lambda, int seedLength) { _changeOperator = returnOperator; ArgumentChecker.isTrue(lambda > 0.0d, "lambda should be > 0"); ArgumentChecker.isTrue(lambda < 1.0d, "lambda should be < 1"); _lambda = lambda; _seedLength = seedLength; } @Override public DateDoubleTimeSeries<?> evaluate(DateDoubleTimeSeries<?> ts) { ArgumentChecker.notNull(ts, "time series"); ArgumentChecker.isTrue(ts.size() > _seedLength, "time series length must be > ", _seedLength); DateDoubleTimeSeries<?> returnSeries = _changeOperator.evaluate(ts); int n = returnSeries.size(); int[] returnTimes = returnSeries.timesArrayFast(); int seedLengthAdjusted = Math.max(_seedLength, 1); // When seed part is 0, the first variance is computed as the square of the first return. This is the same // as a seed part of length 1. // Seed part double seedVariance = 0.0; for (int i = 0; i < seedLengthAdjusted; i++) { double returnTs = returnSeries.getValueAtIndexFast(i); seedVariance += returnTs * returnTs; } seedVariance /= seedLengthAdjusted; // EWMA part int outputLength = n - seedLengthAdjusted + 1; double[] weightedVolatilities = new double[outputLength]; int[] volatilityTimes = new int[outputLength]; weightedVolatilities[0] = Math.sqrt(seedVariance); volatilityTimes[0] = returnTimes[seedLengthAdjusted - 1]; double ewmaVariance = seedVariance; for (int i = 0; i < outputLength - 1; i++) { double returnTs = returnSeries.getValueAtIndexFast(i + seedLengthAdjusted); ewmaVariance = _lambda * ewmaVariance + (1 - _lambda) * returnTs * returnTs; weightedVolatilities[i + 1] = Math.sqrt(ewmaVariance); volatilityTimes[i + 1] = returnTimes[i + seedLengthAdjusted]; } return ImmutableLocalDateDoubleTimeSeries.of(volatilityTimes, weightedVolatilities); } /** * Calculates weighted volatilities using the relative difference series and the default lag of 1 period in the * return computation and no seed period (seed length = 0). * @param lambda lambda value to apply * @return a TimeSeriesWeightedVolatilityOperator instance */ public static TimeSeriesWeightedVolatilityOperator relative(double lambda) { return new TimeSeriesWeightedVolatilityOperator(RELATIVE_CHANGE, lambda, DEFAULT_SEED_LENGTH); } /** * Calculates weighted volatilities using the absolute difference series and the default lag of 1 period in the * return computation and no seed period (seed length = 0). * @param lambda lambda value to apply * @return a TimeSeriesWeightedVolatilityOperator instance */ public static TimeSeriesWeightedVolatilityOperator absolute(double lambda) { return new TimeSeriesWeightedVolatilityOperator(ABSOLUTE_CHANGE, lambda, DEFAULT_SEED_LENGTH); } }