/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.model.option.definition;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.Validate;
import org.threeten.bp.ZoneOffset;
import org.threeten.bp.ZonedDateTime;
import com.opengamma.timeseries.DoubleTimeSeries;
import com.opengamma.timeseries.precise.PreciseDoubleTimeSeries;
import com.opengamma.timeseries.precise.zdt.ImmutableZonedDateTimeDoubleTimeSeries;
import com.opengamma.util.time.DateUtils;
import com.opengamma.util.time.Expiry;
/**
*
*/
public class ExtremeSpreadOptionDefinition extends OptionDefinition {
private final OptionExerciseFunction<StandardOptionWithSpotTimeSeriesDataBundle> _exerciseFunction = new EuropeanExerciseFunction<>();
private final OptionPayoffFunction<StandardOptionWithSpotTimeSeriesDataBundle> _payoffFunction = new OptionPayoffFunction<StandardOptionWithSpotTimeSeriesDataBundle>() {
@Override
public double getPayoff(final StandardOptionWithSpotTimeSeriesDataBundle data, final Double optionPrice) {
Validate.notNull(data, "data");
final DoubleTimeSeries<ZonedDateTime> ts = ImmutableZonedDateTimeDoubleTimeSeries.of((PreciseDoubleTimeSeries<?>) data.getSpotTimeSeries(), ZoneOffset.UTC);
final ZonedDateTime periodEnd = getPeriodEnd().getExpiry();
final DoubleTimeSeries<ZonedDateTime> firstPeriod = ts.subSeries(data.getDate(), true, periodEnd, true);
final DoubleTimeSeries<ZonedDateTime> secondPeriod = ts.subSeries(periodEnd, false, ts.getLatestTime(), true);
if (isCall()) {
return isReverse() ? Math.abs(secondPeriod.minValue() - firstPeriod.minValue()) : Math.abs(secondPeriod.maxValue() - firstPeriod.maxValue());
}
return isReverse() ? Math.abs(secondPeriod.maxValue() - firstPeriod.maxValue()) : Math.abs(secondPeriod.minValue() - firstPeriod.minValue());
}
};
private final Expiry _periodEnd;
private final boolean _isReverse;
public ExtremeSpreadOptionDefinition(final Expiry expiry, final boolean isCall, final Expiry periodEnd, final boolean isReverse) {
super(null, expiry, isCall);
Validate.notNull(periodEnd, "period end");
if (expiry.getExpiry().isBefore(periodEnd.getExpiry())) {
throw new IllegalArgumentException("Period end must be before option expiry");
}
_periodEnd = periodEnd;
_isReverse = isReverse;
}
@SuppressWarnings("unchecked")
@Override
public OptionExerciseFunction<StandardOptionWithSpotTimeSeriesDataBundle> getExerciseFunction() {
return _exerciseFunction;
}
@SuppressWarnings("unchecked")
@Override
public OptionPayoffFunction<StandardOptionWithSpotTimeSeriesDataBundle> getPayoffFunction() {
return _payoffFunction;
}
public double getTimeFromPeriodEnd(final ZonedDateTime date) {
Validate.notNull(date, "date");
return DateUtils.getDifferenceInYears(_periodEnd.getExpiry(), date);
}
public Expiry getPeriodEnd() {
return _periodEnd;
}
public boolean isReverse() {
return _isReverse;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + (_isReverse ? 1231 : 1237);
result = prime * result + ((_periodEnd == null) ? 0 : _periodEnd.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 ExtremeSpreadOptionDefinition other = (ExtremeSpreadOptionDefinition) obj;
return ObjectUtils.equals(_periodEnd, other._periodEnd) && _isReverse == other._isReverse;
}
}