/**
* 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 java.util.Collections;
import java.util.Set;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.Validate;
import com.opengamma.analytics.financial.greeks.Greek;
import com.opengamma.analytics.financial.model.option.pricing.analytic.AnalyticOptionModel;
import com.opengamma.analytics.financial.model.option.pricing.analytic.BlackScholesMertonModel;
import com.opengamma.util.time.Expiry;
/**
* A simple chooser option gives the holder the right to choose whether the
* option is to be a standard call or put (both with the same expiry) after a
* certain time. The exercise style of the option, once the choice has been
* made, is European.
* <p>
* The payoff of this option is:
* $$
* \begin{align*}
* \mathrm{payoff} = \max(c_{BSM}(S, K, T_2), p_{BSM}(S, K, T_2))
* \end{align*}
* $$
* where $c_{BSM}$ is the general Black-Scholes Merton call price, $c_{BSM}$ is
* the general Black-Scholes Merton put price (see {@link BlackScholesMertonModel}),
* $K$ is the strike, $S$ is the spot and $T_2$ is the time to expiry of the
* underlying option.
*/
public class SimpleChooserOptionDefinition extends OptionDefinition {
/** The payoff function */
private final OptionPayoffFunction<StandardOptionDataBundle> _payoffFunction = new OptionPayoffFunction<StandardOptionDataBundle>() {
@SuppressWarnings("synthetic-access")
@Override
public double getPayoff(final StandardOptionDataBundle data, final Double optionPrice) {
Validate.notNull(data);
final double callPrice = BSM.getGreeks(getCallDefinition(), data, GREEKS).get(Greek.FAIR_PRICE);
final double putPrice = BSM.getGreeks(getPutDefinition(), data, GREEKS).get(Greek.FAIR_PRICE);
return Math.max(callPrice, putPrice);
}
};
/** The exercise function */
private final OptionExerciseFunction<StandardOptionDataBundle> _exerciseFunction = new EuropeanExerciseFunction<>();
/** The strike of the underlying option */
private final double _underlyingStrike;
/** The expiry of the underlying option */
private final Expiry _underlyingExpiry;
/** The underlying call */
private final OptionDefinition _callDefinition;
/** The underlying put */
private final OptionDefinition _putDefinition;
/** Black-Scholes Merton model */
private static final AnalyticOptionModel<OptionDefinition, StandardOptionDataBundle> BSM = new BlackScholesMertonModel();
/** The greeks that can be computed */
private static final Set<Greek> GREEKS = Collections.singleton(Greek.FAIR_PRICE);
/**
* @param chooseDate The date when the choice is to be made (i.e. the chooser option expiry)
* @param underlyingStrike The strike of the underlying option
* @param underlyingExpiry The expiry of the underlying European option
*/
public SimpleChooserOptionDefinition(final Expiry chooseDate, final double underlyingStrike, final Expiry underlyingExpiry) {
super(null, chooseDate, null);
Validate.notNull(underlyingExpiry);
Validate.isTrue(underlyingStrike > 0, "underlying strike");
if (underlyingExpiry.getExpiry().isBefore(chooseDate.getExpiry())) {
throw new IllegalArgumentException("Underlying option expiry must be after the choice date");
}
_underlyingStrike = underlyingStrike;
_underlyingExpiry = underlyingExpiry;
_callDefinition = new EuropeanVanillaOptionDefinition(underlyingStrike, underlyingExpiry, true);
_putDefinition = new EuropeanVanillaOptionDefinition(underlyingStrike, underlyingExpiry, false);
}
/**
* @return The underlying call definition
*/
public OptionDefinition getCallDefinition() {
return _callDefinition;
}
/**
* @return The underlying put definition
*/
public OptionDefinition getPutDefinition() {
return _putDefinition;
}
/**
* @return The strike of the underlying option
*/
public double getUnderlyingStrike() {
return _underlyingStrike;
}
/**
* @return The expiry of the underlying option
*/
public Expiry getUnderlyingExpiry() {
return _underlyingExpiry;
}
/**
* {@inheritDoc}
*/
@Override
public OptionExerciseFunction<StandardOptionDataBundle> getExerciseFunction() {
return _exerciseFunction;
}
/**
* {@inheritDoc}
*/
@Override
public OptionPayoffFunction<StandardOptionDataBundle> getPayoffFunction() {
return _payoffFunction;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((_underlyingExpiry == null) ? 0 : _underlyingExpiry.hashCode());
long temp;
temp = Double.doubleToLongBits(_underlyingStrike);
result = prime * result + (int) (temp ^ (temp >>> 32));
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 SimpleChooserOptionDefinition other = (SimpleChooserOptionDefinition) obj;
if (Double.doubleToLongBits(_underlyingStrike) != Double.doubleToLongBits(other._underlyingStrike)) {
return false;
}
return ObjectUtils.equals(_underlyingExpiry, other._underlyingExpiry);
}
}