package org.marketcetera.core.position.impl;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import java.math.BigDecimal;
import org.junit.Before;
import org.junit.Test;
import org.marketcetera.core.position.*;
import org.marketcetera.core.position.MarketDataSupport.InstrumentMarketDataEvent;
import org.marketcetera.core.position.MarketDataSupport.InstrumentMarketDataListener;
import org.marketcetera.module.ExpectedFailure;
import org.marketcetera.trade.*;
import org.marketcetera.util.misc.ClassVersion;
import ca.odell.glazedlists.BasicEventList;
/* $License$ */
/**
* Test {@link PositionRowUpdater}.
*
* @author <a href="mailto:will@marketcetera.com">Will Horn</a>
* @version $Id: PositionRowUpdaterTest.java 16688 2013-09-19 03:18:45Z colin $
* @since 1.5.0
*/
@ClassVersion("$Id: PositionRowUpdaterTest.java 16688 2013-09-19 03:18:45Z colin $")
public class PositionRowUpdaterTest
{
private static final Instrument EQUITY = new Equity("METC");
private static final Instrument OPTION = new Option("METC", "20091010",
BigDecimal.TEN, OptionType.Put);
private static final Instrument FUTURE = new Future("METC", FutureExpirationMonth.DECEMBER,2014);
private static final Instrument CONVERTIBLE_BOND = new ConvertibleBond("US013817AT86");
private static final String ACCOUNT = "A1";
private static final String TRADER = "1";
private InstrumentMarketDataListener mListener;
private PositionRowUpdater mFixture;
private PositionRowImpl mRow;
private BasicEventList<Trade<?>> mTrades;
@Before
public void before() {
mTrades = new BasicEventList<Trade<?>>();
mRow = new PositionRowImpl(EQUITY, "METC", ACCOUNT, TRADER, new BigDecimal(100));
mFixture = new PositionRowUpdater(mRow, mTrades, new MockMarketData(
EQUITY));
}
@Test
public void testNulls() throws Exception {
new ExpectedFailure<IllegalArgumentException>() {
@Override
protected void run() throws Exception {
new PositionRowUpdater(null, mTrades,
new MockMarketData(EQUITY));
}
};
new ExpectedFailure<IllegalArgumentException>() {
@Override
protected void run() throws Exception {
new PositionRowUpdater(mRow, mTrades, null);
}
};
}
@Test
public void testGetPosition() {
assertPosition(mFixture.getPosition(), EQUITY, "100", null, null, null,
null, null);
}
/**
* Tests positions for convertible bonds.
*
* @throws Exception if an unexpected error occurs
*/
@Test
public void testGetConvertibleBondPosition()
throws Exception
{
mRow = new PositionRowImpl(CONVERTIBLE_BOND,
"METC",
ACCOUNT,
TRADER,
new BigDecimal(100));
mFixture = new PositionRowUpdater(mRow,
mTrades,
new MockMarketData(CONVERTIBLE_BOND));
assertPosition(mFixture.getPosition(),
CONVERTIBLE_BOND,
"100",
null,
null,
null,
null,
null);
}
@Test
public void testClosingPrice() throws Exception {
mTrades.add(createTrade("10", "1"));
// only position is updated
assertPosition(mFixture.getPosition(), EQUITY, "110", null, null, null,
null, null);
// set closing price
setClosePrice("1.50");
Thread.sleep(500);
// now realizedPL is valid
assertPosition(mFixture.getPosition(), EQUITY, "110", null, null, "0",
null, null);
// after a tick, everything is good to go
tick("2");
Thread.sleep(500);
assertPosition(mFixture.getPosition(), EQUITY, "110", "50", "10", "0",
"60", "60");
// set closing price to null
setClosePrice(null);
// nothing valid anymore
Thread.sleep(500);
assertPosition(mFixture.getPosition(), EQUITY, "110", null, null, null,
null, null);
}
private void tick(String price) {
mListener.symbolTraded(new InstrumentMarketDataEvent(this,
new BigDecimal(price)));
}
private void setClosePrice(String closePrice) {
mListener.closePriceChanged(new InstrumentMarketDataEvent(this,
closePrice == null ? null : new BigDecimal(closePrice)));
}
@Test
public void testTradesOutOfOrder() throws Exception {
setClosePrice("3");
tick("5");
Thread.sleep(500);
mTrades.add(createTrade("-100", "5"));
// closes incoming position for 5
assertPosition(mFixture.getPosition(), EQUITY, "0", "200", "0", "200",
"0", "200");
mTrades.add(0, createTrade("-100", "3"));
// recalculate with incoming position closed for 3
assertPosition(mFixture.getPosition(), EQUITY, "-100", "200", "-200",
"0", "0", "0");
}
@Test
public void testDelayedConnect() throws Exception {
// new fixture and row for this test since the main one initializes with
// trades
mFixture = new PositionRowUpdater(new PositionRowImpl(EQUITY, "METC",
ACCOUNT, TRADER, new BigDecimal(100)), null, new MockMarketData(EQUITY));
setClosePrice("1.50");
tick("2");
Thread.sleep(1000);
assertPosition(mFixture.getPosition(), EQUITY, "100", "50", "0", "0",
"50", "50");
mTrades.add(createTrade("-100", "2"));
assertPosition(mFixture.getPosition(), EQUITY, "100", "50", "0", "0",
"50", "50");
mFixture.connect(mTrades);
assertPosition(mFixture.getPosition(), EQUITY, "0", "50", "0", "50",
"0", "50");
}
@Test
public void testOptionMultiplier() throws Exception {
mFixture = new PositionRowUpdater(new PositionRowImpl(OPTION, "METC",
ACCOUNT, TRADER, new BigDecimal(100)), mTrades, new MockMarketData(
OPTION));
assertPosition(mFixture.getPosition(), OPTION, "100", null, null, null,
null, null);
mTrades.add(createOptionTrade("-100", "5"));
assertPosition(mFixture.getPosition(), OPTION, "0", null, null, null,
null, null);
tick("5");
setClosePrice("4");
Thread.sleep(1000);
assertPosition(mFixture.getPosition(), OPTION, "0", null, null, null,
null, null);
setMultiplier(BigDecimal.TEN);
Thread.sleep(1000);
assertPosition(mFixture.getPosition(), OPTION, "0", "1000", "0",
"1000", "0", "1000");
/*
* Not likely that multiplier will change, but test since the API allows
* it.
*/
setMultiplier(BigDecimal.ONE);
Thread.sleep(1000);
assertPosition(mFixture.getPosition(), OPTION, "0", "100", "0", "100",
"0", "100");
setMultiplier(null);
Thread.sleep(1000);
assertPosition(mFixture.getPosition(), OPTION, "0", null, null, null,
null, null);
}
@Test
public void testFutureMultiplier() throws Exception {
mFixture = new PositionRowUpdater(new PositionRowImpl(FUTURE, "METC",
ACCOUNT, TRADER, new BigDecimal(100)), mTrades, new MockMarketData(
FUTURE));
assertPosition(mFixture.getPosition(), FUTURE, "100", null, null, null,
null, null);
mTrades.add(createFutureTrade("-100", "5"));
assertPosition(mFixture.getPosition(), FUTURE, "0", null, null, null,
null, null);
tick("5");
setClosePrice("4");
Thread.sleep(1000);
assertPosition(mFixture.getPosition(), FUTURE, "0", null, null, null,
null, null);
setFutureMultiplier(BigDecimal.TEN);
Thread.sleep(1000);
assertPosition(mFixture.getPosition(), FUTURE, "0", "1000", "0",
"1000", "0", "1000");
/*
* Not likely that multiplier will change, but test since the API allows
* it.
*/
setFutureMultiplier(BigDecimal.ONE);
Thread.sleep(1000);
assertPosition(mFixture.getPosition(), FUTURE, "0", "100", "0", "100",
"0", "100");
setFutureMultiplier(null);
Thread.sleep(1000);
assertPosition(mFixture.getPosition(), FUTURE, "0", null, null, null,
null, null);
}
private void setMultiplier(BigDecimal multiplier) {
mListener.optionMultiplierChanged(new InstrumentMarketDataEvent(this,
multiplier));
}
private void setFutureMultiplier(BigDecimal multiplier) {
mListener.futureMultiplierChanged(new InstrumentMarketDataEvent(this,
multiplier));
}
private Trade<?> createTrade(String quantity, String price) {
return MockTrade.createEquityTrade("METC", ACCOUNT, TRADER, quantity,
price);
}
private Trade<?> createOptionTrade(String quantity, String price) {
return MockTrade.createTrade(OPTION, ACCOUNT, TRADER, quantity, price);
}
private Trade<?> createFutureTrade(String quantity, String price) {
return MockTrade.createTrade(FUTURE, ACCOUNT, TRADER, quantity, price);
}
private void assertPosition(PositionRow row, Instrument instrument,
String position, String positional, String trading,
String realized, String unrealized, String total) {
assertThat(row.getInstrument(), is(instrument));
assertThat(row.getAccount(), is(ACCOUNT));
assertThat(row.getTraderId(), is(TRADER));
PositionMetricsImplTest.assertPositionMetrics(row.getPositionMetrics(),
"100", position, positional, trading, realized, unrealized,
total);
}
class MockMarketData implements MarketDataSupport {
private final Instrument mInstrument;
public MockMarketData(Instrument instrument) {
mInstrument = instrument;
}
@Override
public void addInstrumentMarketDataListener(Instrument instrument,
InstrumentMarketDataListener listener) {
assertThat(instrument, is(mInstrument));
mListener = listener;
}
@Override
public BigDecimal getClosingPrice(Instrument instrument) {
return null;
}
@Override
public BigDecimal getLastTradePrice(Instrument instrument) {
return null;
}
@Override
public BigDecimal getOptionMultiplier(Option option) {
return null;
}
@Override
public BigDecimal getFutureMultiplier(Future future) {
return null;
}
@Override
public void removeInstrumentMarketDataListener(Instrument instrument,
InstrumentMarketDataListener listener) {
if (listener == mListener) {
mListener = null;
}
}
@Override
public void dispose() {
}
}
}