/** * 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.slf4j.Logger; import org.slf4j.LoggerFactory; import org.threeten.bp.ZonedDateTime; import com.opengamma.analytics.financial.model.option.Moneyness; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.time.DateUtils; import com.opengamma.util.time.Expiry; /** * */ public class ForwardStartOptionDefinition extends OptionDefinition { private static final Logger s_logger = LoggerFactory.getLogger(ForwardStartOptionDefinition.class); private final OptionExerciseFunction<StandardOptionDataBundle> _exerciseFunction = new EuropeanExerciseFunction<>(); private final OptionPayoffFunction<StandardOptionDataBundle> _payoffFunction = new OptionPayoffFunction<StandardOptionDataBundle>() { @Override public double getPayoff(final StandardOptionDataBundle data, final Double optionPrice) { final ZonedDateTime date = data.getDate(); if (date.isBefore(getStartTime().getExpiry())) { throw new IllegalArgumentException("Cannot get strike before start time: it has not been defined"); } final double spot = data.getSpot(); final double alpha = getAlpha(); final double strike = spot * alpha; return isCall() ? Math.max(0, spot - strike) : Math.max(0, strike - spot); } }; private final Expiry _startTime; private final double _percent; private final Moneyness _moneyness; private final double _alpha; public ForwardStartOptionDefinition(final Expiry expiry, final Boolean isCall, final Expiry startTime) { this(expiry, isCall, startTime, 1, Moneyness.ATM); } public ForwardStartOptionDefinition(final Expiry expiry, final Boolean isCall, final Expiry startTime, final double percent, final Moneyness moneyness) { super(null, expiry, isCall); Validate.notNull(startTime); ArgumentChecker.notNegative(percent, "percent"); Validate.notNull(moneyness, "moneyness"); if (expiry.getExpiry().isBefore(startTime.getExpiry())) { throw new IllegalArgumentException("The forward start time must be before the expiry of the option"); } if (moneyness == Moneyness.ATM && percent != 1) { s_logger.info("Option is ATM but percentage is not one; ignoring value for percent"); } _startTime = startTime; _percent = percent; _moneyness = moneyness; switch (moneyness) { case ITM: _alpha = isCall ? 1 - percent : 1 + percent; break; case OTM: _alpha = isCall ? percent + 1 : 1 - percent; break; case ATM: _alpha = 1; break; default: throw new IllegalArgumentException("Can only handle ITM, OTM and ATM"); } } public Expiry getStartTime() { return _startTime; } public double getAlpha() { return _alpha; } public double getPercent() { return _percent; } public Moneyness getMoneyness() { return _moneyness; } @Override public OptionExerciseFunction<StandardOptionDataBundle> getExerciseFunction() { return _exerciseFunction; } @Override public OptionPayoffFunction<StandardOptionDataBundle> getPayoffFunction() { return _payoffFunction; } public double getTimeToStart(final ZonedDateTime date) { if (date.isAfter(getStartTime().getExpiry())) { throw new IllegalArgumentException("Date " + date + " is after startTime " + getStartTime()); } return DateUtils.getDifferenceInYears(date, getStartTime().getExpiry()); } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + ((_moneyness == null) ? 0 : _moneyness.hashCode()); long temp; temp = Double.doubleToLongBits(_percent); result = prime * result + (int) (temp ^ (temp >>> 32)); result = prime * result + ((_startTime == null) ? 0 : _startTime.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 ForwardStartOptionDefinition other = (ForwardStartOptionDefinition) obj; if (!ObjectUtils.equals(_moneyness, other._moneyness)) { return false; } if (Double.doubleToLongBits(_percent) != Double.doubleToLongBits(other._percent)) { return false; } return ObjectUtils.equals(_startTime, other._startTime); } }