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.ConvertibleBond; import org.marketcetera.trade.Future; import org.marketcetera.trade.Instrument; import org.marketcetera.trade.Option; import ca.odell.glazedlists.BasicEventList; /* $License$ */ /** * Test {@link PositionRowUpdater}. * */ public class ConvertibleBondPositionRowUpdaterTest { 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(CONVERTIBLE_BOND, "METC", ACCOUNT, TRADER, new BigDecimal(100)); mFixture = new PositionRowUpdater(mRow, mTrades, new MockMarketData( CONVERTIBLE_BOND)); } @Test public void testNulls() throws Exception { new ExpectedFailure<IllegalArgumentException>() { @Override protected void run() throws Exception { new PositionRowUpdater(null, mTrades, new MockMarketData(CONVERTIBLE_BOND)); } }; new ExpectedFailure<IllegalArgumentException>() { @Override protected void run() throws Exception { new PositionRowUpdater(mRow, mTrades, null); } }; } @Test public void testGetPosition() { 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(), CONVERTIBLE_BOND, "110", null, null, null, null, null); // set closing price setClosePrice("1.50"); Thread.sleep(500); // now realizedPL is valid assertPosition(mFixture.getPosition(), CONVERTIBLE_BOND, "110", null, null, "0", null, null); // after a tick, everything is good to go tick("2"); Thread.sleep(500); assertPosition(mFixture.getPosition(), CONVERTIBLE_BOND, "110", "50", "10", "0", "60", "60"); // set closing price to null setClosePrice(null); // nothing valid anymore Thread.sleep(500); assertPosition(mFixture.getPosition(), CONVERTIBLE_BOND, "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(), CONVERTIBLE_BOND, "0", "200", "0", "200", "0", "200"); mTrades.add(0, createTrade("-100", "3")); // recalculate with incoming position closed for 3 assertPosition(mFixture.getPosition(), CONVERTIBLE_BOND, "-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(CONVERTIBLE_BOND, "METC", ACCOUNT, TRADER, new BigDecimal(100)), null, new MockMarketData(CONVERTIBLE_BOND)); setClosePrice("1.50"); tick("2"); Thread.sleep(1000); assertPosition(mFixture.getPosition(), CONVERTIBLE_BOND, "100", "50", "0", "0", "50", "50"); mTrades.add(createTrade("-100", "2")); assertPosition(mFixture.getPosition(), CONVERTIBLE_BOND, "100", "50", "0", "0", "50", "50"); mFixture.connect(mTrades); assertPosition(mFixture.getPosition(), CONVERTIBLE_BOND, "0", "50", "0", "50", "0", "50"); } private Trade<?> createTrade(String quantity, String price) { return MockTrade.createCurrencyTrade("USD","INR", 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() { } } }