package org.marketcetera.options;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.marketcetera.trade.OptionType.Call;
import static org.marketcetera.trade.OptionType.Put;
import static org.marketcetera.trade.OptionType.Unknown;
import java.math.BigDecimal;
import java.text.DecimalFormatSymbols;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import org.junit.Test;
import org.marketcetera.module.ExpectedFailure;
import org.marketcetera.trade.Option;
import org.marketcetera.trade.OptionType;
/* $License$ */
/**
* Tests {@link OptionUtils}.
*
* @author <a href="mailto:will@marketcetera.com">Will Horn</a>
* @version $Id: OptionUtilsTest.java 16154 2012-07-14 16:34:05Z colin $
* @since 2.0.0
*/
public class OptionUtilsTest {
/**
* Verifies {@link OptionUtils#normalizeEquityOptionExpiry(String)} &
* {@link OptionUtils#normalizeUSEquityOptionExpiry(String)}.
*
* @throws Exception if there were unexpected errors
*/
@Test
public void testNormalizeOptionExpiry() throws Exception {
assertNormalized("200911", "20091121");
assertNormalized("201001", "20100116");
assertNormalized("201005", "20100522");
assertNormalized("203712", "20371219");
assertNormalized("20371212", null);
assertNormalized("abc", null);
assertNormalized("", null);
assertNormalized(" ", null);
assertNormalized(" ", null);
assertNormalized("2009xx", null);
assertNormalized("xxxx05", null);
assertNormalized("200900", null);
assertNormalized("200913", null);
new ExpectedFailure<NullPointerException>() {
@Override
protected void run() throws Exception {
OptionUtils.normalizeUSEquityOptionExpiry(null);
}
};
new ExpectedFailure<NullPointerException>() {
@Override
protected void run() throws Exception {
OptionUtils.normalizeEquityOptionExpiry(null);
}
};
}
private void assertNormalized(String expiry, String expected) {
assertThat(OptionUtils.normalizeUSEquityOptionExpiry(expiry), is(expected));
assertThat(OptionUtils.normalizeEquityOptionExpiry(expiry), is(expected));
}
/**
* Tests {@link OptionUtils#getOsiOptionFromString(String)}.
*
* @throws Exception if an unexpected error occurs
*/
@Test
public void testOsiOptionFromString()
throws Exception
{
new ExpectedFailure<NullPointerException>() {
@Override
protected void run()
throws Exception
{
OptionUtils.getOsiOptionFromString(null);
}
};
new ExpectedFailure<IllegalArgumentException>() {
@Override
protected void run()
throws Exception
{
OptionUtils.getOsiOptionFromString("");
}
};
new ExpectedFailure<IllegalArgumentException>() {
@Override
protected void run()
throws Exception
{
OptionUtils.getOsiOptionFromString("this-is-not-a-valid-osi-option");
}
};
new ExpectedFailure<IllegalArgumentException>() {
@Override
protected void run()
throws Exception
{
OptionUtils.getOsiOptionFromString("21-characters-not-osi");
}
};
new ExpectedFailure<IllegalArgumentException>() {
@Override
protected void run()
throws Exception
{
OptionUtils.getOsiOptionFromString("MSFT 991122p12345123"); // valid except 'Put' indicator is lower-case
}
};
new ExpectedFailure<IllegalArgumentException>() {
@Override
protected void run()
throws Exception
{
OptionUtils.getOsiOptionFromString("MSFT 991122c12345123"); // valid except 'Call' indicator is lower-case
}
};
new ExpectedFailure<IllegalArgumentException>() {
@Override
protected void run()
throws Exception
{
OptionUtils.getOsiOptionFromString("MSFT 991122 12345123"); // option-type missing
}
};
new ExpectedFailure<IllegalArgumentException>() {
@Override
protected void run()
throws Exception
{
OptionUtils.getOsiOptionFromString("MSFT 991122 12345123"); // option-type missing
}
};
verifyOption(OptionUtils.getOsiOptionFromString("123456301122P12345123"),
"123456",
OptionType.Put,
"20301122",
new BigDecimal("12345.123"));
verifyOption(OptionUtils.getOsiOptionFromString("MSFT 001122P12345123"),
"MSFT",
OptionType.Put,
"21001122",
new BigDecimal("12345.123"));
verifyOption(OptionUtils.getOsiOptionFromString("MSFT 000122P12345123"),
"MSFT",
OptionType.Put,
"21000122",
new BigDecimal("12345.123"));
verifyOption(OptionUtils.getOsiOptionFromString("MSFT 000922P12345123"),
"MSFT",
OptionType.Put,
"21000922",
new BigDecimal("12345.123"));
verifyOption(OptionUtils.getOsiOptionFromString("MSFT 001022P12345123"),
"MSFT",
OptionType.Put,
"21001022",
new BigDecimal("12345.123"));
verifyOption(OptionUtils.getOsiOptionFromString("MSFT 001222P12345123"),
"MSFT",
OptionType.Put,
"21001222",
new BigDecimal("12345.123"));
verifyOption(OptionUtils.getOsiOptionFromString("MSFT 001201P12345123"),
"MSFT",
OptionType.Put,
"21001201",
new BigDecimal("12345.123"));
verifyOption(OptionUtils.getOsiOptionFromString("MSFT 201209P12345123"),
"MSFT",
OptionType.Put,
"20201209",
new BigDecimal("12345.123"));
verifyOption(OptionUtils.getOsiOptionFromString("MSFT 201210P12345123"),
"MSFT",
OptionType.Put,
"20201210",
new BigDecimal("12345.123"));
verifyOption(OptionUtils.getOsiOptionFromString("MSFT 201219P12345123"),
"MSFT",
OptionType.Put,
"20201219",
new BigDecimal("12345.123"));
verifyOption(OptionUtils.getOsiOptionFromString("MSFT 001220P12345123"),
"MSFT",
OptionType.Put,
"21001220",
new BigDecimal("12345.123"));
verifyOption(OptionUtils.getOsiOptionFromString("123456001220P12345123"),
"123456",
OptionType.Put,
"21001220",
new BigDecimal("12345.123"));
verifyOption(OptionUtils.getOsiOptionFromString("MSFT 001229P12345123"),
"MSFT",
OptionType.Put,
"21001229",
new BigDecimal("12345.123"));
verifyOption(OptionUtils.getOsiOptionFromString("MSFT 201230P12345123"),
"MSFT",
OptionType.Put,
"20201230",
new BigDecimal("12345.123"));
verifyOption(OptionUtils.getOsiOptionFromString("MSFT 501231P12345123"),
"MSFT",
OptionType.Put,
"20501231",
new BigDecimal("12345.123"));
verifyOption(OptionUtils.getOsiOptionFromString("x 301122P12345123"),
"x",
OptionType.Put,
"20301122",
new BigDecimal("12345.123"));
verifyOption(OptionUtils.getOsiOptionFromString("xx 301122P12345123"),
"xx",
OptionType.Put,
"20301122",
new BigDecimal("12345.123"));
verifyOption(OptionUtils.getOsiOptionFromString("ABCDEF301122C12345123"),
"ABCDEF",
OptionType.Call,
"20301122",
new BigDecimal("12345.123"));
verifyOption(OptionUtils.getOsiOptionFromString("ABCDEF000431C12345123"), // invalid date
"ABCDEF",
OptionType.Call,
"21000431",
new BigDecimal("12345.123"));
verifyOption(OptionUtils.getOsiOptionFromString("ABCDEF301122C00000000"),
"ABCDEF",
OptionType.Call,
"20301122",
new BigDecimal("0.0"));
verifyOption(OptionUtils.getOsiOptionFromString("ABCDEF301122C99999999"),
"ABCDEF",
OptionType.Call,
"20301122",
new BigDecimal("99999.999"));
Locale currentLocale = Locale.getDefault();
try {
Locale.setDefault(Locale.FRANCE);
assertEquals(',',
DecimalFormatSymbols.getInstance().getDecimalSeparator());
verifyOption(OptionUtils.getOsiOptionFromString("ABCDEF301122C12345123"),
"ABCDEF",
OptionType.Call,
"20301122",
new BigDecimal("12345.123"));
} finally {
Locale.setDefault(currentLocale);
}
}
/**
* Tests {@link OptionUtils#getOsiSymbolFromOption(Option)}.
*
* @throws Exception if an unexpected error occurs
*/
@Test
public void testOsiSymbolFromOption()
throws Exception
{
new ExpectedFailure<NullPointerException>() {
@Override
protected void run()
throws Exception
{
OptionUtils.getOsiSymbolFromOption(null);
}
};
// symbol too long
verifyGetOsiSymbolFromOptionFails("1234567",
"20091010",
BigDecimal.ONE,
OptionType.Call);
// expiry too long
verifyGetOsiSymbolFromOptionFails("123456",
"200910100",
BigDecimal.ONE,
OptionType.Call);
// expiry without day
verifyGetOsiSymbolFromOptionFails("123456",
"200910",
BigDecimal.ONE,
OptionType.Call);
// expiry with week
verifyGetOsiSymbolFromOptionFails("123456",
"200910w1",
BigDecimal.ONE,
OptionType.Call);
// negative strike
verifyGetOsiSymbolFromOptionFails("123456",
"20091010",
new BigDecimal(-1),
OptionType.Put);
// strike with too many fractional digits
verifyGetOsiSymbolFromOptionFails("123456",
"20091010",
new BigDecimal("0.1234"),
OptionType.Put);
// strike with too many non-fractional digits
verifyGetOsiSymbolFromOptionFails("123456",
"20091010",
new BigDecimal("123456"),
OptionType.Put);
// unknown type
verifyGetOsiSymbolFromOptionFails("123456",
"20091010",
BigDecimal.ONE,
OptionType.Unknown);
verifyOsiSymbolFromOption("123456301122P12345123",
"123456",
OptionType.Put,
"20301122",
new BigDecimal("12345.123"));
verifyOsiSymbolFromOption("MSFT 001122P12345123",
"MSFT",
OptionType.Put,
"21001122",
new BigDecimal("12345.123"));
verifyOsiSymbolFromOption("MSFT 000122P12345123",
"MSFT",
OptionType.Put,
"21000122",
new BigDecimal("12345.123"));
verifyOsiSymbolFromOption("MSFT 000922P12345123",
"MSFT",
OptionType.Put,
"21000922",
new BigDecimal("12345.123"));
verifyOsiSymbolFromOption("MSFT 001022P12345123",
"MSFT",
OptionType.Put,
"21001022",
new BigDecimal("12345.123"));
verifyOsiSymbolFromOption("MSFT 001222P12345123",
"MSFT",
OptionType.Put,
"21001222",
new BigDecimal("12345.123"));
verifyOsiSymbolFromOption("MSFT 001201P12345123",
"MSFT",
OptionType.Put,
"21001201",
new BigDecimal("12345.123"));
verifyOsiSymbolFromOption("MSFT 201209P12345123",
"MSFT",
OptionType.Put,
"20201209",
new BigDecimal("12345.123"));
verifyOsiSymbolFromOption("MSFT 201210P12345123",
"MSFT",
OptionType.Put,
"20201210",
new BigDecimal("12345.123"));
verifyOsiSymbolFromOption("MSFT 201219P12345123",
"MSFT",
OptionType.Put,
"20201219",
new BigDecimal("12345.123"));
verifyOsiSymbolFromOption("MSFT 001220P12345123",
"MSFT",
OptionType.Put,
"21001220",
new BigDecimal("12345.123"));
verifyOsiSymbolFromOption("123456001220P12345123",
"123456",
OptionType.Put,
"21001220",
new BigDecimal("12345.123"));
verifyOsiSymbolFromOption("MSFT 001229P12345123",
"MSFT",
OptionType.Put,
"21001229",
new BigDecimal("12345.123"));
verifyOsiSymbolFromOption("MSFT 201230P12345123",
"MSFT",
OptionType.Put,
"20201230",
new BigDecimal("12345.123"));
verifyOsiSymbolFromOption("MSFT 501231P12345123",
"MSFT",
OptionType.Put,
"20501231",
new BigDecimal("12345.123"));
verifyOsiSymbolFromOption("x 301122P12345123",
"x",
OptionType.Put,
"20301122",
new BigDecimal("12345.123"));
verifyOsiSymbolFromOption("xx 301122P12345123",
"xx",
OptionType.Put,
"20301122",
new BigDecimal("12345.123"));
verifyOsiSymbolFromOption("ABCDEF301122C12345123",
"ABCDEF",
OptionType.Call,
"20301122",
new BigDecimal("12345.123"));
verifyOsiSymbolFromOption("ABCDEF000431C12345123", // invalid date
"ABCDEF",
OptionType.Call,
"21000431",
new BigDecimal("12345.123"));
verifyOsiSymbolFromOption("ABCDEF301122C00000000",
"ABCDEF",
OptionType.Call,
"20301122",
new BigDecimal("0.0"));
verifyOsiSymbolFromOption("ABCDEF301122C99999999",
"ABCDEF",
OptionType.Call,
"20301122",
new BigDecimal("99999.999"));
}
/**
* Tests {@link OptionUtils#getOptionTypeForOSICharacter(char)}.
*
* @throws Exception if an unexpected error occurs
*/
@Test
public void instanceForOSIValue()
throws Exception
{
for(char badOptionType : new char[] { ' ', 'x', 'c', 'p' }) {
assertEquals(Unknown,
OptionUtils.getOptionTypeForOSICharacter(badOptionType));
}
assertEquals(Put,
OptionUtils.getOptionTypeForOSICharacter('P'));
assertEquals(Call,
OptionUtils.getOptionTypeForOSICharacter('C'));
}
/**
* Verifies that the given <code>Option</code> matches the given expected attributes.
*
* @param inOption inOption an <code>Option</code> value
* @param inExpectedSymbol inSymbol a <code>String</code> value
* @param inExpectedType inOptionType an <code>OptionType</code> value
* @param inExpectedExpiry inExpiry a <code>String</code> value
* @param inExpectedStrike inStrike a <code>BigDecimal</code> value
* @throws Exception if an unexpected error occurs
*/
private static void verifyOption(Option inOption,
String inExpectedSymbol,
OptionType inExpectedType,
String inExpectedExpiry,
BigDecimal inExpectedStrike)
throws Exception
{
assertNotNull(inOption);
assertEquals(inExpectedSymbol,
inOption.getSymbol());
assertEquals(inExpectedType,
inOption.getType());
assertEquals(inExpectedExpiry,
inOption.getExpiry());
assertEquals(inExpectedStrike,
inOption.getStrikePrice());
}
/**
* Verifies that {@link OptionUtils#getOsiSymbolFromOption(Option)} returns
* the expected symbol given an <code>Option</code> with the provided
* attributes.
*
* @param inExpectedSymbol the expected OSI symbol
* @param inRootSymbol the <code>Option</code> root symbol
* @param inOptionType the <code>Option</code> type
* @param inExpiry the <code>Option</code> expiry
* @param inStrike the <code>Option</code> strike
* @throws Exception if an unexpected error occurs
*/
private static void verifyOsiSymbolFromOption(String inExpectedSymbol,
String inRootSymbol,
OptionType inOptionType,
String inExpiry,
BigDecimal inStrike)
throws Exception
{
assertEquals(inExpectedSymbol, OptionUtils
.getOsiSymbolFromOption(new Option(inRootSymbol, inExpiry,
inStrike, inOptionType)));
}
/**
* Verifies that {@link OptionUtils#getOsiSymbolFromOption(Option)} fails
* with IllegalArgumentException for the given option tuple.
*
* @param inRootSymbol the <code>Option</code> root symbol
* @param inExpiry the <code>Option</code> expiry
* @param inStrike the <code>Option</code> strike
* @param inOptionType the <code>Option</code> type
* @throws Exception if an unexpected error occurs
*/
private static void verifyGetOsiSymbolFromOptionFails(final String inRootSymbol,
final String inExpiry,
final BigDecimal inStrike,
final OptionType inOptionType)
throws Exception
{
new ExpectedFailure<IllegalArgumentException>() {
@Override
protected void run()
throws Exception
{
OptionUtils.getOsiSymbolFromOption(new Option(inRootSymbol,
inExpiry, inStrike, inOptionType));
}
};
}
}