package org.marketcetera.strategy.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import org.junit.Test;
import org.marketcetera.event.AskEvent;
import org.marketcetera.event.BidEvent;
import org.marketcetera.event.DividendEvent;
import org.marketcetera.event.DividendFrequency;
import org.marketcetera.event.DividendStatus;
import org.marketcetera.event.DividendType;
import org.marketcetera.event.EventTestBase;
import org.marketcetera.event.LogEvent;
import org.marketcetera.event.MarketstatEvent;
import org.marketcetera.event.OptionEvent;
import org.marketcetera.event.QuoteAction;
import org.marketcetera.event.TestMessages;
import org.marketcetera.event.TradeEvent;
import org.marketcetera.event.impl.*;
import org.marketcetera.marketdata.DateUtils;
import org.marketcetera.module.ExpectedFailure;
import org.marketcetera.options.ExpirationType;
import org.marketcetera.strategy.util.OptionContractPair.OptionContractPairKey;
import org.marketcetera.trade.Equity;
import org.marketcetera.trade.Instrument;
import org.marketcetera.trade.Option;
import org.marketcetera.trade.OptionType;
import org.marketcetera.util.test.EqualityAssert;
/* $License$ */
/**
* Tests {@link OptionChain}, [@link OptionContractPair}, and {@link OptionContract}.
*
* @author <a href="mailto:colin@marketcetera.com">Colin DuPlantis</a>
* @version $Id: OptionChainTest.java 16154 2012-07-14 16:34:05Z colin $
* @since 2.0.0
*/
public class OptionChainTest
implements TestMessages
{
/**
* Tests {@link OptionChain#OptionChain(org.marketcetera.trade.Instrument)}.
*
* @throws Exception
*/
@Test
public void optionChainConstructor()
throws Exception
{
new ExpectedFailure<NullPointerException>() {
@Override
protected void run()
throws Exception
{
new OptionChain(null);
}
};
new ExpectedFailure<UnsupportedOperationException>() {
@Override
protected void run()
throws Exception
{
Instrument other = EventTestBase.generateUnsupportedInstrument();
verifyOptionChain(new OptionChain(other),
other,
null,
null,
null,
null,
new ArrayList<DividendEvent>(),
new ArrayList<OptionContractPair>());
}
};
verifyOptionChain(new OptionChain(equity),
equity,
null,
null,
null,
null,
new ArrayList<DividendEvent>(),
new ArrayList<OptionContractPair>());
verifyOptionChain(new OptionChain(callOption),
callOption,
null,
null,
null,
null,
new ArrayList<DividendEvent>(),
new ArrayList<OptionContractPair>());
}
/**
* Tests {@link OptionChain#getOptionChain()}.
*
* @throws Exception if an unexpected error occurs
*/
@Test
public void getOptionChain()
throws Exception
{
OptionChain chain = new OptionChain(equity);
// empty option chain
verifyOptionChain(chain,
equity,
null,
null,
null,
null,
new ArrayList<DividendEvent>(),
new ArrayList<OptionContractPair>());
// add an element to the option chain
AskEvent callAsk = QuoteEventBuilder.optionAskEvent().withUnderlyingInstrument(equity)
.hasDeliverable(false)
.withExchange("B")
.withExpirationType(ExpirationType.AMERICAN)
.withInstrument(callOption)
.withMultiplier(BigDecimal.ZERO)
.withPrice(EventTestBase.generateDecimalValue())
.withQuoteDate(DateUtils.dateToString(new Date()))
.withSize(EventTestBase.generateDecimalValue()).create();
assertTrue(chain.process(callAsk));
// create expected result
OptionContractPair entry = new OptionContractPair((OptionEvent)callAsk);
assertTrue(entry.process((OptionEvent)callAsk));
// this test verifies only the contents of the option chain, doesn't yet dig into verifying the contents of the option chain entries
verifyOptionChain(chain,
equity,
null,
null,
null,
null,
new ArrayList<DividendEvent>(),
Arrays.asList(new OptionContractPair[] { entry }));
// add a bid for the call side of the same bid
BidEvent callBid = QuoteEventBuilder.optionBidEvent().withUnderlyingInstrument(equity)
.withExchange("B")
.withExpirationType(ExpirationType.AMERICAN)
.withInstrument(callOption)
.withPrice(EventTestBase.generateDecimalValue())
.withQuoteDate(DateUtils.dateToString(new Date()))
.withSize(EventTestBase.generateDecimalValue()).create();
assertTrue(chain.process(callBid));
// update expected result
assertTrue(entry.process((OptionEvent)callBid));
// verify again
verifyOptionChain(chain,
equity,
null,
null,
null,
null,
new ArrayList<DividendEvent>(),
Arrays.asList(new OptionContractPair[] { entry }));
// add something for the put side of the same contract
AskEvent putAsk = QuoteEventBuilder.optionAskEvent().withUnderlyingInstrument(equity)
.hasDeliverable(false)
.withExchange("X")
.withExpirationType(ExpirationType.AMERICAN)
.withInstrument(putOption)
.withMultiplier(BigDecimal.ZERO)
.withPrice(EventTestBase.generateDecimalValue())
.withQuoteDate(DateUtils.dateToString(new Date()))
.withSize(EventTestBase.generateDecimalValue()).create();
assertTrue(chain.process(putAsk));
// update expected result
assertTrue(entry.process((OptionEvent)putAsk));
// verify
verifyOptionChain(chain,
equity,
null,
null,
null,
null,
new ArrayList<DividendEvent>(),
Arrays.asList(new OptionContractPair[] { entry }));
// now, get and hold the option chain view and make sure it gets updated without having to get a new version of it
Collection<OptionContractPair> optionChain = chain.getOptionChain();
assertEquals(1,
optionChain.size());
assertEquals(entry,
optionChain.iterator().next());
// add a bid for the same contract on the put side
BidEvent putBid = QuoteEventBuilder.optionBidEvent().withUnderlyingInstrument(equity)
.withExchange("X")
.withExpirationType(ExpirationType.AMERICAN)
.withInstrument(putOption)
.withPrice(EventTestBase.generateDecimalValue())
.withQuoteDate(DateUtils.dateToString(new Date()))
.withSize(EventTestBase.generateDecimalValue()).create();
assertTrue(chain.process(putBid));
// update expected result
assertTrue(entry.process((OptionEvent)putBid));
// verify
verifyOptionChain(chain,
equity,
null,
null,
null,
null,
new ArrayList<DividendEvent>(),
Arrays.asList(new OptionContractPair[] { entry }));
// check the updated option chain view
assertEquals(1,
optionChain.size());
assertEquals(entry,
optionChain.iterator().next());
// add a trade for the put side
TradeEvent putTrade = TradeEventBuilder.optionTradeEvent().withUnderlyingInstrument(equity)
.withExchange("X")
.withExpirationType(ExpirationType.AMERICAN)
.withInstrument(putOption)
.withPrice(EventTestBase.generateDecimalValue())
.withTradeDate(DateUtils.dateToString(new Date()))
.withSize(EventTestBase.generateDecimalValue()).create();
assertTrue(chain.process(putTrade));
// update expected result
assertTrue(entry.process((OptionEvent)putTrade));
// verify
verifyOptionChain(chain,
equity,
null,
null,
null,
null,
new ArrayList<DividendEvent>(),
Arrays.asList(new OptionContractPair[] { entry }));
// check the updated option chain view (not using verifyOptionChain from here on in order to use the same optionChain collection,
// making sure it gets updated rather than getting a new view each time)
assertEquals(1,
optionChain.size());
assertEquals(entry,
optionChain.iterator().next());
// create an event for a new OptionContract (same symbol and expiry, different (greater) strike)
Option newCallOption = new Option(callOption.getSymbol(),
callOption.getExpiry(),
callOption.getStrikePrice().add(EventTestBase.generateDecimalValue()),
OptionType.Call);
callAsk = QuoteEventBuilder.optionAskEvent().withUnderlyingInstrument(equity)
.withExchange("Q")
.withExpirationType(ExpirationType.AMERICAN)
.withInstrument(newCallOption)
.withPrice(EventTestBase.generateDecimalValue())
.withQuoteDate(DateUtils.dateToString(new Date()))
.withSize(EventTestBase.generateDecimalValue()).create();
// process the event (creates the new pair in the chain)
assertTrue(chain.process(callAsk));
assertNotNull(optionChain.toString());
// make sure the new entry gets added
assertEquals(2,
optionChain.size());
OptionContractPair newEntry = new OptionContractPair((OptionEvent)callAsk);
Iterator<OptionContractPair> iterator = optionChain.iterator();
assertEquals(entry,
iterator.next());
assertEquals(newEntry,
iterator.next());
// create an event for yet another new OptionContract (same symbol and expiry, different (even greater) strike)
Option newerCallOption = new Option(newCallOption.getSymbol(),
newCallOption.getExpiry(),
newCallOption.getStrikePrice().add(EventTestBase.generateDecimalValue()),
OptionType.Call);
callBid = QuoteEventBuilder.optionBidEvent().withUnderlyingInstrument(equity)
.withExchange("Q")
.withExpirationType(ExpirationType.AMERICAN)
.withInstrument(newerCallOption)
.withPrice(EventTestBase.generateDecimalValue())
.withQuoteDate(DateUtils.dateToString(new Date()))
.withSize(EventTestBase.generateDecimalValue()).create();
// process the event (creates the new pair in the chain)
assertTrue(chain.process(callBid));
assertNotNull(chain.toString());
// add a trade for the same call
TradeEvent callTrade = TradeEventBuilder.optionTradeEvent().withUnderlyingInstrument(equity)
.withExchange("Q")
.withExpirationType(ExpirationType.AMERICAN)
.withInstrument(newerCallOption)
.withPrice(EventTestBase.generateDecimalValue())
.withTradeDate(DateUtils.dateToString(new Date()))
.withSize(EventTestBase.generateDecimalValue()).create();
// process the event (creates the new pair in the chain)
assertTrue(chain.process(callTrade));
assertNotNull(chain.toString());
// make sure the new entry gets added
assertEquals(3,
optionChain.size());
OptionContractPair newerEntry = new OptionContractPair((OptionEvent)callTrade);
iterator = optionChain.iterator();
assertEquals(entry,
iterator.next());
assertEquals(newEntry,
iterator.next());
assertEquals(newerEntry,
iterator.next());
}
/**
* Tests {@link OptionChain#getDividends()}.
*
* @throws Exception if an unexpected error occurs
*/
@Test
public void getDividends()
throws Exception
{
OptionChain chain = new OptionChain(equity);
// empty dividends
verifyOptionChain(chain,
equity,
null,
null,
null,
null,
new ArrayList<DividendEvent>(),
new ArrayList<OptionContractPair>());
// add a dividend
Date date = new Date();
DividendEventBuilder builder = DividendEventBuilder.dividend().withAmount(new BigDecimal("123.45"))
.withCurrency("US Dollars")
.withDeclareDate(DateUtils.dateToString(date))
.withEquity(equity)
.withExecutionDate(DateUtils.dateToString(date))
.withFrequency(DividendFrequency.ANNUALLY)
.withPaymentDate(DateUtils.dateToString(date))
.withRecordDate(DateUtils.dateToString(date))
.withStatus(DividendStatus.OFFICIAL)
.withType(DividendType.FUTURE);
DividendEvent event = builder.create();
assertTrue(chain.process(event));
verifyOptionChain(chain,
equity,
null,
null,
null,
null,
Arrays.asList(new DividendEvent[] { event }),
new ArrayList<OptionContractPair>());
// add a new dividend making sure the list gets updated
builder.withAmount(new BigDecimal("2345.67"));
DividendEvent event2 = builder.create();
assertFalse(event.equals(event2));
assertTrue(chain.process(event2));
verifyOptionChain(chain,
equity,
null,
null,
null,
null,
Arrays.asList(new DividendEvent[] { event, event2 }),
new ArrayList<OptionContractPair>());
}
/**
* Tests {@link OptionChain#getLatestUnderlyingAsk()}.
*
* @throws Exception if an unexpected error occurs
*/
@Test
public void getLatestUnderlyingAsk()
throws Exception
{
OptionChain chain = new OptionChain(equity);
// no latest ask yet
verifyOptionChain(chain,
equity,
null,
null,
null,
null,
new ArrayList<DividendEvent>(),
new ArrayList<OptionContractPair>());
// create an ask for the underlying symbol
AskEvent equityAsk = EventTestBase.generateEquityAskEvent(equity,
QuoteAction.ADD);
assertTrue(chain.process(equityAsk));
verifyOptionChain(chain,
equity,
null,
equityAsk,
null,
null,
new ArrayList<DividendEvent>(),
new ArrayList<OptionContractPair>());
// send in a new ask
AskEvent equityAsk2 = EventTestBase.generateEquityAskEvent(equity,
QuoteAction.ADD);
assertFalse(equityAsk.equals(equityAsk2));
assertTrue(chain.process(equityAsk2));
verifyOptionChain(chain,
equity,
null,
equityAsk2,
null,
null,
new ArrayList<DividendEvent>(),
new ArrayList<OptionContractPair>());
}
/**
* Tests {@link OptionChain#getLatestUnderlyingBid()}.
*
* @throws Exception if an unexpected error occurs
*/
@Test
public void getLatestUnderlyingBid()
throws Exception
{
OptionChain chain = new OptionChain(equity);
// no latest bid yet
verifyOptionChain(chain,
equity,
null,
null,
null,
null,
new ArrayList<DividendEvent>(),
new ArrayList<OptionContractPair>());
// create an ask for the underlying symbol
BidEvent equityBid = EventTestBase.generateEquityBidEvent(equity,
QuoteAction.ADD);
assertTrue(chain.process(equityBid));
verifyOptionChain(chain,
equity,
equityBid,
null,
null,
null,
new ArrayList<DividendEvent>(),
new ArrayList<OptionContractPair>());
// send in a new ask
BidEvent equityBid2 = EventTestBase.generateEquityBidEvent(equity,
QuoteAction.ADD);
assertFalse(equityBid.equals(equityBid2));
assertTrue(chain.process(equityBid2));
verifyOptionChain(chain,
equity,
equityBid2,
null,
null,
null,
new ArrayList<DividendEvent>(),
new ArrayList<OptionContractPair>());
}
/**
* Tests {@link OptionChain#getLatestUnderlyingTrade()}.
*
* @throws Exception if an unexpected error occurs
*/
@Test
public void getLatestUnderlyingTrade()
throws Exception
{
OptionChain chain = new OptionChain(equity);
// no latest trade yet
verifyOptionChain(chain,
equity,
null,
null,
null,
null,
new ArrayList<DividendEvent>(),
new ArrayList<OptionContractPair>());
// create an ask for the underlying symbol
TradeEvent equityTrade = EventTestBase.generateEquityTradeEvent(equity);
assertTrue(chain.process(equityTrade));
verifyOptionChain(chain,
equity,
null,
null,
equityTrade,
null,
new ArrayList<DividendEvent>(),
new ArrayList<OptionContractPair>());
// send in a new ask
TradeEvent equityTrade2 = EventTestBase.generateEquityTradeEvent(equity);
assertFalse(equityTrade.equals(equityTrade2));
assertTrue(chain.process(equityTrade2));
verifyOptionChain(chain,
equity,
null,
null,
equityTrade2,
null,
new ArrayList<DividendEvent>(),
new ArrayList<OptionContractPair>());
}
/**
* Tests {@link OptionChain#getLatestUnderlyingMarketstat()}.
*
* @throws Exception if an unexpected error occurs
*/
@Test
public void getLatestUnderlyingMarketstat()
throws Exception
{
OptionChain chain = new OptionChain(equity);
// no latest marketstat yet
verifyOptionChain(chain,
equity,
null,
null,
null,
null,
new ArrayList<DividendEvent>(),
new ArrayList<OptionContractPair>());
// create an ask for the underlying symbol
MarketstatEvent equityMarketstat = EventTestBase.generateEquityMarketstatEvent(equity);
assertTrue(chain.process(equityMarketstat));
verifyOptionChain(chain,
equity,
null,
null,
null,
equityMarketstat,
new ArrayList<DividendEvent>(),
new ArrayList<OptionContractPair>());
// send in a new ask
MarketstatEvent equityMarketstat2 = EventTestBase.generateEquityMarketstatEvent(equity);
assertFalse(equityMarketstat.equals(equityMarketstat2));
assertTrue(chain.process(equityMarketstat2));
verifyOptionChain(chain,
equity,
null,
null,
null,
equityMarketstat2,
new ArrayList<DividendEvent>(),
new ArrayList<OptionContractPair>());
// add an option event (creates an entry in the option chain)
MarketstatEvent optionMarketstat = EventTestBase.generateOptionMarketstatEvent(putOption,
equity);
assertTrue(chain.process(optionMarketstat));
OptionContractPair entry = new OptionContractPair((OptionEvent)optionMarketstat);
verifyOptionChain(chain,
equity,
null,
null,
null,
equityMarketstat2,
new ArrayList<DividendEvent>(),
Arrays.asList(new OptionContractPair[] { entry }));
}
/**
* Tests {@link OptionChain#process(org.marketcetera.event.Event)}.
*
* <p>Note that this test uses the process capability of {@link OptionContractPair}
* to prepare the expected data, which means that it merely tests {@link OptionContractPair}'s
* ability to pass events through. Another test is required to make sure that {@link OptionContract}
* does the right thing.
*
* @throws Exception if an unexpected error occurs
*/
@Test
public void process()
throws Exception
{
final OptionChain chain = new OptionChain(equity);
new ExpectedFailure<NullPointerException>() {
@Override
protected void run()
throws Exception
{
chain.process(null);
}
};
AskEvent ask = EventTestBase.generateEquityAskEvent(equity,
QuoteAction.ADD);
BidEvent bid = EventTestBase.generateEquityBidEvent(equity,
QuoteAction.ADD);
TradeEvent trade = EventTestBase.generateEquityTradeEvent(equity);
MarketstatEvent marketstat = EventTestBase.generateEquityMarketstatEvent(equity);
DividendEvent dividend = EventTestBase.generateDividendEvent();
LogEvent log = LogEventBuilder.error().withMessage(MESSAGE_0P).create();
assertTrue(chain.process(ask));
assertTrue(chain.process(bid));
assertTrue(chain.process(trade));
assertTrue(chain.process(marketstat));
assertTrue(chain.process(dividend));
assertFalse(chain.process(log));
verifyOptionChain(chain,
equity,
bid,
ask,
trade,
marketstat,
Arrays.asList(new DividendEvent[] { dividend }),
new ArrayList<OptionContractPair>());
}
/**
* Tests {@link OptionChain#process(org.marketcetera.event.Event)} with error conditions.
*
* @throws Exception if an unexpected error occurs
*/
@Test
public void processErrorCases()
throws Exception
{
OptionChain chain = new OptionChain(equity);
verifyOptionChain(chain,
equity,
null,
null,
null,
null,
new ArrayList<DividendEvent>(),
new ArrayList<OptionContractPair>());
// generate events for a different underlying instrument
Equity otherEquity = new Equity("GOOG");
assertFalse(otherEquity.equals(equity));
AskEvent ask = EventTestBase.generateEquityAskEvent(otherEquity,
QuoteAction.ADD);
BidEvent bid = EventTestBase.generateEquityBidEvent(otherEquity,
QuoteAction.ADD);
TradeEvent trade = EventTestBase.generateEquityTradeEvent(otherEquity);
MarketstatEvent marketstat = EventTestBase.generateEquityMarketstatEvent(otherEquity);
DividendEvent dividend = EventTestBase.generateDividendEvent(otherEquity);
assertFalse(chain.process(ask));
assertFalse(chain.process(bid));
assertFalse(chain.process(trade));
assertFalse(chain.process(marketstat));
assertFalse(chain.process(dividend));
verifyOptionChain(chain,
equity,
null,
null,
null,
null,
new ArrayList<DividendEvent>(),
new ArrayList<OptionContractPair>());
// same thing for option events (wrong symbol on events)
ask = EventTestBase.generateOptionAskEvent(callOption,
QuoteAction.ADD);
bid = EventTestBase.generateOptionBidEvent(callOption,
QuoteAction.ADD);
trade = EventTestBase.generateOptionTradeEvent(callOption,
otherEquity);
marketstat = EventTestBase.generateOptionMarketstatEvent(callOption,
otherEquity);
assertFalse(chain.process(ask));
assertFalse(chain.process(bid));
assertFalse(chain.process(trade));
assertFalse(chain.process(marketstat));
verifyOptionChain(chain,
equity,
null,
null,
null,
null,
new ArrayList<DividendEvent>(),
new ArrayList<OptionContractPair>());
}
/**
* This test verifies that the {@link OptionContractPair} correct handles
* events intended to modify the component {@link OptionContract} objects.
*
* <p>Note that this test uses the process capability of {@link OptionContract}
* to prepare the expected data, which means that it merely tests {@link OptionContractPair}'s
* ability to pass events through. Another test is required to make sure that {@link OptionContract}
* does the right thing.
*
* @throws Exception if an unexpected error occurs
*/
@Test
public void optionChainPairProcessing()
throws Exception
{
OptionChain chain = new OptionChain(equity);
Collection<OptionContractPair> optionChain = chain.getOptionChain();
// prepare expected contracts
OptionContract expectedCall = new OptionContract(equity,
callOption,
OptionType.Call,
ExpirationType.AMERICAN,
true,
BigDecimal.TEN,
callOption.getSymbol());
OptionContract expectedPut = new OptionContract(equity,
putOption,
OptionType.Put,
ExpirationType.AMERICAN,
true,
BigDecimal.TEN,
null);
// create a few events for a given option contract
assertTrue(optionChain.isEmpty());
QuoteEventBuilder<AskEvent> askBuilder = QuoteEventBuilder.optionAskEvent();
QuoteEventBuilder<BidEvent> bidBuilder = QuoteEventBuilder.optionBidEvent();
AskEvent ask = askBuilder.withExchange("Q")
.withExpirationType(ExpirationType.AMERICAN)
.withInstrument(callOption)
.withMultiplier(BigDecimal.TEN)
.hasDeliverable(true)
.withPrice(EventTestBase.generateDecimalValue())
.withQuoteDate(DateUtils.dateToString(new Date()))
.withSize(EventTestBase.generateDecimalValue())
.withProviderSymbol(callOption.getSymbol())
.withUnderlyingInstrument(equity).create();
BidEvent bid = bidBuilder.withExchange("X")
.withExpirationType(ExpirationType.AMERICAN)
.withInstrument(callOption)
.withMultiplier(BigDecimal.TEN)
.hasDeliverable(true)
.withPrice(EventTestBase.generateDecimalValue())
.withQuoteDate(DateUtils.dateToString(new Date()))
.withSize(EventTestBase.generateDecimalValue())
.withProviderSymbol(callOption.getSymbol())
.withUnderlyingInstrument(equity).create();
TradeEvent trade = EventTestBase.generateOptionTradeEvent(callOption,
equity);
MarketstatEvent marketstat = EventTestBase.generateOptionMarketstatEvent(callOption,
equity);
assertTrue(chain.process(ask));
OptionContractPair pair = optionChain.iterator().next();
// prepare expected result
expectedCall.process((OptionEvent)ask);
verifyOptionContractPair(pair,
null,
expectedCall);
assertTrue(chain.process(bid));
expectedCall.process((OptionEvent)bid);
verifyOptionContractPair(pair,
null,
expectedCall);
assertTrue(chain.process(trade));
expectedCall.process((OptionEvent)trade);
verifyOptionContractPair(pair,
null,
expectedCall);
assertTrue(chain.process(marketstat));
expectedCall.process((OptionEvent)marketstat);
verifyOptionContractPair(pair,
null,
expectedCall);
assertEquals(1,
optionChain.size());
// repeat tests for put side
ask = askBuilder.withExchange("Q")
.withExpirationType(ExpirationType.AMERICAN)
.withInstrument(putOption)
.withMultiplier(BigDecimal.TEN)
.hasDeliverable(true)
.withPrice(EventTestBase.generateDecimalValue())
.withQuoteDate(DateUtils.dateToString(new Date()))
.withSize(EventTestBase.generateDecimalValue())
.withProviderSymbol(putOption.getSymbol())
.withUnderlyingInstrument(equity).create();
bid = bidBuilder.withExchange("X")
.withExpirationType(ExpirationType.AMERICAN)
.withInstrument(putOption)
.withMultiplier(BigDecimal.TEN)
.hasDeliverable(true)
.withPrice(EventTestBase.generateDecimalValue())
.withQuoteDate(DateUtils.dateToString(new Date()))
.withSize(EventTestBase.generateDecimalValue())
.withProviderSymbol(putOption.getSymbol())
.withUnderlyingInstrument(equity).create();
trade = EventTestBase.generateOptionTradeEvent(putOption,
equity);
marketstat = EventTestBase.generateOptionMarketstatEvent(putOption,
equity);
assertTrue(chain.process(ask));
expectedPut.process((OptionEvent)ask);
verifyOptionContractPair(pair,
expectedPut,
expectedCall);
assertTrue(chain.process(bid));
expectedPut.process((OptionEvent)bid);
verifyOptionContractPair(pair,
expectedPut,
expectedCall);
assertTrue(chain.process(trade));
expectedPut.process((OptionEvent)trade);
verifyOptionContractPair(pair,
expectedPut,
expectedCall);
assertTrue(chain.process(marketstat));
expectedPut.process((OptionEvent)marketstat);
verifyOptionContractPair(pair,
expectedPut,
expectedCall);
assertEquals(1,
optionChain.size());
}
/**
* This test verifies that <code>OptionContractPair</code> objects are maintained
* in the correct sort order.
*
* @throws Exception if an error occurs
*/
@Test
public void optionContractOrder()
throws Exception
{
OptionChain chain = new OptionChain(equity);
Collection<OptionContractPair> optionChain = chain.getOptionChain();
assertTrue(optionChain.isEmpty());
// we're going to need several different options for this test, all for the same underlying symbol
Option o1 = new Option("ABC",
"20100101",
BigDecimal.ONE,
OptionType.Put);
// this value has a higher strike price than o1, all other details the same
Option o2 = new Option(o1.getSymbol(),
o1.getExpiry(),
o1.getStrikePrice().add(BigDecimal.ONE),
OptionType.Put);
// this value has a higher expiry than o1, all other details the same
Option o3 = new Option(o1.getSymbol(),
"20110101",
o1.getStrikePrice(),
OptionType.Put);
// this value has a lexicographically higher value than o1, all other details the same
Option o4 = new Option("ABCD",
o1.getExpiry(),
o1.getStrikePrice(),
OptionType.Put);
// verify the data is set up correctly (asserting that the above comments are true)
assertTrue(o1.getStrikePrice().compareTo(o2.getStrikePrice()) == -1);
assertTrue(o1.getExpiry().compareTo(o3.getExpiry()) == -1);
assertTrue(o1.getSymbol().compareTo(o4.getSymbol()) == -1);
// we expect the order to end up being: o1, o2, o3, o4. to prove that the contract pairs are sorted and not
// FIFO or FILO, we'll add them as follows: o2, o3, o1, o4. remember that to create contract pairs, we
// add events for them
// first, create one contract pair (we're just going to use a single event type to simplify the test.
// in this case, the type of event doesn't matter)
QuoteEventBuilder<AskEvent> builder = QuoteEventBuilder.optionAskEvent();
builder.withExchange("Q")
.withExpirationType(ExpirationType.AMERICAN)
.withPrice(EventTestBase.generateDecimalValue())
.withSize(EventTestBase.generateDecimalValue())
.withUnderlyingInstrument(equity)
.withQuoteDate(DateUtils.dateToString(new Date()));
assertTrue(chain.process(builder.withInstrument(o2).create()));
assertEquals(1,
optionChain.size());
assertEquals(o2,
optionChain.iterator().next().getPut().getInstrument());
// add the next contract
assertTrue(chain.process(builder.withInstrument(o3).create()));
assertEquals(2,
optionChain.size());
Iterator<OptionContractPair> iterator = optionChain.iterator();
assertEquals(o2,
iterator.next().getPut().getInstrument());
assertEquals(o3,
iterator.next().getPut().getInstrument());
// next
assertTrue(chain.process(builder.withInstrument(o1).create()));
assertEquals(3,
optionChain.size());
iterator = optionChain.iterator();
assertEquals(o1,
iterator.next().getPut().getInstrument());
assertEquals(o2,
iterator.next().getPut().getInstrument());
assertEquals(o3,
iterator.next().getPut().getInstrument());
// last
assertTrue(chain.process(builder.withInstrument(o4).create()));
assertEquals(4,
optionChain.size());
iterator = optionChain.iterator();
assertEquals(o1,
iterator.next().getPut().getInstrument());
assertEquals(o2,
iterator.next().getPut().getInstrument());
assertEquals(o3,
iterator.next().getPut().getInstrument());
assertEquals(o4,
iterator.next().getPut().getInstrument());
}
/**
* This test tests {@link OptionContractPair#equals(Object)} and {@link OptionContractPair#hashCode()}.
*
* <p>Most of the relevant testing of <code>equals(Object)</code> is done above. This method tests
* the boundary cases not exercised by the use of the <code>OptionChain</code> <code>Collection</code>.
*
* @throws Exception if an unexpected error occurs
*/
@Test
public void optionContractPairEqualsHashCode()
throws Exception
{
QuoteEventBuilder<AskEvent> builder = QuoteEventBuilder.optionAskEvent();
builder.withExchange("Q")
.withExpirationType(ExpirationType.AMERICAN)
.withPrice(EventTestBase.generateDecimalValue())
.withQuoteDate(DateUtils.dateToString(new Date()))
.withSize(EventTestBase.generateDecimalValue())
.withUnderlyingInstrument(equity);
OptionContractPair pair = new OptionContractPair((OptionEvent)builder.withInstrument(putOption).create());
OptionContractPair equalPair = new OptionContractPair((OptionEvent)builder.create());
OptionContractPair unequalPair = new OptionContractPair((OptionEvent)builder.withInstrument(new Option(putOption.getSymbol(),
putOption.getExpiry(),
putOption.getStrikePrice().add(BigDecimal.ONE),
OptionType.Put)).create());
EqualityAssert.assertEquality(pair,
equalPair,
unequalPair,
this,
null);
}
/**
* Tests {@link OptionContractPair#equals(Object)} and {@link OptionContractPair#hashCode()}.
*
* @throws Exception if an unexpected error occurs
*/
@Test
public void contractPairHashCodeAndEquals()
throws Exception
{
Option otherOptionStrike = new Option(callOption.getSymbol(),
callOption.getExpiry(),
callOption.getStrikePrice().add(BigDecimal.ONE),
OptionType.Call);
Option otherOptionExpiry = new Option(callOption.getSymbol(),
"20110319",
callOption.getStrikePrice(),
OptionType.Call);
Option otherOptionSymbol = new Option("COLIN",
callOption.getExpiry(),
callOption.getStrikePrice(),
OptionType.Call);
assertFalse(callOption.getSymbol().equals(otherOptionSymbol.getSymbol()));
assertFalse(callOption.getExpiry().equals(otherOptionExpiry.getExpiry()));
assertFalse(callOption.getStrikePrice().equals(otherOptionStrike.getStrikePrice()));
OptionContractPairKey key1 = OptionContractPair.getOptionContractPairKey(callOption);
OptionContractPairKey key2 = OptionContractPair.getOptionContractPairKey(callOption);
OptionContractPairKey key3 = OptionContractPair.getOptionContractPairKey(otherOptionStrike);
OptionContractPairKey key4 = OptionContractPair.getOptionContractPairKey(otherOptionExpiry);
OptionContractPairKey key5 = OptionContractPair.getOptionContractPairKey(otherOptionSymbol);
EqualityAssert.assertEquality(key1,
key2,
key3,
key4,
key5,
this,
null);
}
/**
* Verifies that the given <code>OptionContractPair</code> contains the
* given expected results.
*
* @param inActualContractPair an <code>OptionContractPair</code> value
* @param inExpectedPut an <code>OptionContract</code> value
* @param inExpectedCall an <code>OptionContract</code> value
* @throws Exception if an unexpected error occurs
*/
private static void verifyOptionContractPair(OptionContractPair inActualContractPair,
OptionContract inExpectedPut,
OptionContract inExpectedCall)
throws Exception
{
assertNotNull(inActualContractPair);
if(inExpectedPut == null) {
assertNull(inActualContractPair.getPut());
} else {
verifyOptionContract(inActualContractPair.getPut(),
inExpectedPut.getUnderlyingInstrument(),
inExpectedPut.getInstrument(),
inExpectedPut.getInstrument().getSymbol(),
inExpectedPut.getExpirationType(),
inExpectedPut.getMultiplier(),
inExpectedPut.hasDeliverable(),
inExpectedPut.getLatestBid(),
inExpectedPut.getLatestAsk(),
inExpectedPut.getLatestTrade(),
inExpectedPut.getLatestMarketstat());
}
if(inExpectedCall == null) {
assertNull(inActualContractPair.getCall());
} else {
verifyOptionContract(inActualContractPair.getCall(),
inExpectedCall.getUnderlyingInstrument(),
inExpectedCall.getInstrument(),
inExpectedCall.getInstrument().getSymbol(),
inExpectedCall.getExpirationType(),
inExpectedCall.getMultiplier(),
inExpectedCall.hasDeliverable(),
inExpectedCall.getLatestBid(),
inExpectedCall.getLatestAsk(),
inExpectedCall.getLatestTrade(),
inExpectedCall.getLatestMarketstat());
}
}
/**
* Verifies that the given <code>OptionContract</code> contains the given
* expected values.
*
* @param inActualContract an <code>OptionContract</code> value
* @param inExpectedUnderlyingInstrument an <code>Instrument</code> value
* @param inExpectedOption an <code>Option</code> value
* @param inExpectedProviderSymbol a <code>String</code> value
* @param inExpectedExpirationType an <code>ExpirationType</code> value
* @param inExpectedMultiplier a <code>BigDecimal</code> value
* @param inExpectedHasDeliverable a <code>boolean</code> value
* @param inExpectedLatestBid a <code>BidEvent</code> value
* @param inExpectedLatestAsk an <code>AskEvent</code> value
* @param inExpectedLatestTrade a <code>TradeEvent</code> value
* @param inExpectedLatestMarketstat a <code>MarketstatEvent</code> value
* @throws Exception if an unexpected error occurs
*/
private static void verifyOptionContract(OptionContract inActualContract,
Instrument inExpectedUnderlyingInstrument,
Option inExpectedOption,
String inExpectedProviderSymbol,
ExpirationType inExpectedExpirationType,
BigDecimal inExpectedMultiplier,
boolean inExpectedHasDeliverable,
BidEvent inExpectedLatestBid,
AskEvent inExpectedLatestAsk,
TradeEvent inExpectedLatestTrade,
MarketstatEvent inExpectedLatestMarketstat)
throws Exception
{
assertEquals(inExpectedUnderlyingInstrument,
inActualContract.getUnderlyingInstrument());
assertEquals(inExpectedOption,
inActualContract.getInstrument());
assertEquals(inExpectedExpirationType,
inActualContract.getExpirationType());
assertEquals(inExpectedMultiplier,
inActualContract.getMultiplier());
assertEquals(inExpectedHasDeliverable,
inActualContract.hasDeliverable());
assertEquals(inExpectedLatestBid,
inActualContract.getLatestBid());
assertEquals(inExpectedLatestAsk,
inActualContract.getLatestAsk());
assertEquals(inExpectedLatestTrade,
inActualContract.getLatestTrade());
assertEquals(inExpectedLatestMarketstat,
inActualContract.getLatestMarketstat());
assertEquals(inExpectedProviderSymbol,
inActualContract.getProviderSymbol());
}
/**
* Verifies that the given <code>OptionChain</code> object contains the given expected values.
*
* @param inActualOptionChain an <code>OptionChain</code> value
* @param inExpectedUnderlyingInstrument an <code>Instrument</code> value
* @param inExpectedLatestBid a <code>BidEvent</code> value
* @param inExpectedLatestAsk an <code>AskEvent</code> value
* @param inExpectedLatestTrade a <code>TradeEvent</code> value
* @param inExpectedLatestMarketstat a <code>Marketstat</code> value
* @param inExpectedDividends a <code>List<DividendEvent></code> value
* @param inExpectedOptionChainContents a <code>Collection<OptionContractPair></code> value
* @throws Exception if an unexpected error occurs
*/
private static void verifyOptionChain(OptionChain inActualOptionChain,
Instrument inExpectedUnderlyingInstrument,
BidEvent inExpectedLatestBid,
AskEvent inExpectedLatestAsk,
TradeEvent inExpectedLatestTrade,
MarketstatEvent inExpectedLatestMarketstat,
List<DividendEvent> inExpectedDividends,
Collection<OptionContractPair> inExpectedOptionChainContents)
throws Exception
{
assertNotNull(inActualOptionChain);
assertNotNull(inActualOptionChain.toString());
assertEquals(inExpectedUnderlyingInstrument,
inActualOptionChain.getUnderlyingInstrument());
assertEquals(inExpectedLatestBid,
inActualOptionChain.getLatestUnderlyingBid());
assertEquals(inExpectedLatestAsk,
inActualOptionChain.getLatestUnderlyingAsk());
assertEquals(inExpectedLatestTrade,
inActualOptionChain.getLatestUnderlyingTrade());
assertEquals(inExpectedLatestMarketstat,
inActualOptionChain.getLatestUnderlyingMarketstat());
// convert to arrays for the equality test because otherwise the type of the collection
// is included in the test, which isn't relevant (LinkedList vs. ArrayList or ImmutableCollection vs. List, that kind of thing - just test
// the contents)
assertTrue(Arrays.equals(inExpectedOptionChainContents.toArray(),
inActualOptionChain.getOptionChain().toArray()));
assertEquals(inExpectedDividends,
inActualOptionChain.getDividends());
}
/**
* test instrument
*/
private final Equity equity = new Equity("METC");
/**
* test option
*/
private final Option callOption = new Option("MSFT",
"20100319",
BigDecimal.ONE,
OptionType.Call);
/**
* test option
*/
private final Option putOption = new Option("MSFT",
"20100319",
BigDecimal.ONE,
OptionType.Put);
}