package org.marketcetera.core.position.impl; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; import static org.marketcetera.core.position.impl.BigDecimalMatchers.comparesEqualTo; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.math.BigDecimal; import java.util.Map; import org.junit.Ignore; import org.junit.Test; import org.marketcetera.core.instruments.UnderlyingSymbolSupport; import org.marketcetera.core.position.*; import org.marketcetera.messagehistory.ReportHolder; import org.marketcetera.trade.*; import ca.odell.glazedlists.BasicEventList; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.event.ListEvent; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; /* $License$ */ /** * Tests {@link PositionEngineImpl}. * * @author <a href="mailto:will@marketcetera.com">Will Horn</a> * @version $Id: PositionEngineImplTest.java 16154 2012-07-14 16:34:05Z colin $ * @since 1.5.0 */ public class PositionEngineImplTest { private abstract class PositionEngineTestTemplate implements Runnable { EventList<ReportHolder> reports = new BasicEventList<ReportHolder>(); long tradeCounter = 0; @Override public void run() { initReports(); PositionEngine engine = PositionEngineFactory.createFromReportHolders(reports, new ImmutablePositionSupport(getIncomingPositions()), mock(MarketDataSupport.class), createUnderlyingSymbolSupport()); EventList<PositionRow> positions = getPositionData(engine); positions.addListEventListener(new ExpectedListChanges<PositionRow>("Positions", getExpectedPositionListChanges())); validatePositions(positions); } protected UnderlyingSymbolSupport createUnderlyingSymbolSupport() { return new UnderlyingSymbolSupport() { @Override public String getUnderlying(Instrument instrument) { return instrument.getSymbol(); } }; } protected Map<? extends PositionKey<?>, BigDecimal> getIncomingPositions() { return Maps.newHashMap(); } protected EventList<PositionRow> getPositionData(PositionEngine engine) { return engine.getFlatData().getPositions(); } protected void initReports() { // default is no-op } protected void validatePositions(EventList<PositionRow> positions) { // default is no-op } protected int[] getExpectedPositionListChanges() { return new int[] {}; } protected void clearReports() { reports.clear(); } /** * Adds a trade with the given attributes. * * @param instrument an <code>Instrument</code> value * @param account a <code>String</code> value * @param traderId a <code>String</code> value * @param side a <code>Side</code> value * @param quantity a <code>String</code> value * @param price a <code>String</code> value * @param inOrderStatus an <code>OrderStatus</code> value * @param inExecutionType an <code>ExecutionType</code> value * @param inOriginator an <code>Originator</code> value */ protected void addTrade(Instrument instrument, String account, long traderId, Side side, String quantity, String price, long sequence, OrderStatus inOrderStatus, ExecutionType inExecutionType, Originator inOriginator) { add(new MockExecutionReport(account, instrument, traderId, side, price, quantity, sequence, inOrderStatus, inExecutionType, inOriginator)); } protected void addEquityTrade(String symbol, String account, Side side, String quantity, String price) { addEquityTrade(symbol, account, "1", side, quantity, price); } /** * Adds a trade with the given attributes. * * @param inSymbol a <code>String</code> value * @param inAccount a <code>String</code> value * @param inSide a <code>Side</code> value * @param inQuantity a <code>String</code> value * @param inPrice a <code>String</code> value * @param inOrderStatus an <code>OrderStatus</code> value * @param inExecutionType an <code>ExecutionType</code> value * @param inOriginator an <code>Originator</code> value */ protected void addEquityTrade(String inSymbol, String inAccount, Side inSide, String inQuantity, String inPrice, OrderStatus inOrderStatus, ExecutionType inExecutionType, Originator inOriginator) { addEquityTrade(inSymbol, inAccount, "1", inSide, inQuantity, inPrice, inOrderStatus, inExecutionType, inOriginator); } protected void addEquityTrade(String symbol, String account, String traderId, Side side, String quantity, String price) { addTrade(new Equity(symbol), account, traderId, side, quantity, price); } /** * Adds a trade with the given attributes. * * @param inSymbol a <code>String</code> value * @param inAccount a <code>String</code> value * @param inTraderId a <code>String</code> value * @param inSide a <code>Side</code> value * @param inQuantity a <code>String</code> value * @param inPrice a <code>String</code> value * @param inOrderStatus an <code>OrderStatus</code> value * @param inExecutionType an <code>ExecutionType</code> value * @param inOriginator an <code>Originator</code> value */ protected void addEquityTrade(String inSymbol, String inAccount, String inTraderId, Side inSide, String inQuantity, String inPrice, OrderStatus inOrderStatus, ExecutionType inExecutionType, Originator inOriginator) { addTrade(new Equity(inSymbol), inAccount, Long.valueOf(inTraderId), inSide, inQuantity, inPrice, ++tradeCounter, inOrderStatus, inExecutionType, inOriginator); } /** * Adds a trade with the given attributes. * * @param instrument an <code>Instrument</code> value * @param account a <code>String</code> value * @param traderId a <code>String</code> value * @param side a <code>Side</code> value * @param quantity a <code>String</code> value * @param price a <code>String</code> value */ protected void addTrade(Instrument instrument, String account, String traderId, Side side, String quantity, String price) { addTrade(instrument, account, Long.valueOf(traderId), side, quantity, price, ++tradeCounter, OrderStatus.Filled, ExecutionType.Fill, Originator.Broker); } private void add(ReportBase report) { reports.add(new ReportHolder(report, null)); } protected void assertEquityPosition(PositionRow position, String symbol, String account, String traderId, String amount) { assertPosition(position, new Equity(symbol), symbol, account, traderId, amount); } protected void assertPosition(PositionRow position, Instrument instrument, String underlying, String account, String traderId, String amount) { assertThat(position.getInstrument(), is(instrument)); assertThat(position.getUnderlying(), is(underlying)); assertThat(position.getAccount(), is(account)); assertThat(position.getTraderId(), is(traderId)); assertThat(position.getPositionMetrics().getPosition(), comparesEqualTo(amount)); } protected void assertPositions(EventList<PositionRow> positions, String[]... values) { assertThat(positions.size(), is(values.length)); for (int i = 0; i < values.length; i++) { String[] strings = values[i]; assertEquityPosition(positions.get(i), strings[0], strings[1], strings[2], strings[3]); } } } @Test public void simpleInit() { new PositionEngineTestTemplate() { @Override protected void initReports() { addEquityTrade("METC", "personal", Side.Buy, "1000", "500"); addEquityTrade("METC", "personal", Side.Buy, "500", "200", OrderStatus.PendingCancel, ExecutionType.Fill, Originator.Broker); addEquityTrade("METC", "personal", Side.Buy, "1000", "500", OrderStatus.Filled, ExecutionType.Fill, Originator.Server); addEquityTrade("METC", "personal", Side.Buy, "1000", "500", OrderStatus.Filled, ExecutionType.New, Originator.Broker); } @Override protected void validatePositions(EventList<PositionRow> positions) { assertThat(positions.size(), is(1)); assertEquityPosition(positions.get(0), "METC", "personal", "1", "1500"); } }.run(); } @Test public void complexInit() { new PositionEngineTestTemplate() { @Override protected void initReports() { addEquityTrade("METC", "personal", Side.Buy, "1000", "1"); addEquityTrade("METC", "personal", Side.Buy, "104", "1"); addEquityTrade("METC", "personal", Side.Buy, "104", "1", OrderStatus.Filled, ExecutionType.Fill, Originator.Server); addEquityTrade("METC", "personal", Side.Buy, "104", "1", OrderStatus.Filled, ExecutionType.New, Originator.Broker); addEquityTrade("METC", "work", Side.Buy, "70", "1"); addEquityTrade("METC", "work", Side.Buy, "70", "1", OrderStatus.PartiallyFilled, ExecutionType.PartialFill, Originator.Server); addEquityTrade("METC", "work", Side.Buy, "70", "1", OrderStatus.PartiallyFilled, ExecutionType.New, Originator.Broker); addEquityTrade("GOOG", "personal", Side.Buy, "45.5", "1"); addEquityTrade("GOOG", "personal", Side.Buy, "45.5", "1", OrderStatus.Filled, ExecutionType.Fill, Originator.Server); addEquityTrade("GOOG", "personal", Side.Buy, "45.5", "1", OrderStatus.Filled, ExecutionType.New, Originator.Broker); addEquityTrade("YHOO", "work", Side.Buy, "20", "1"); addEquityTrade("YHOO", "work", Side.Sell, "6", "1"); addEquityTrade("YHOO", "work", Side.Sell, "6", "1", OrderStatus.Filled, ExecutionType.Fill, Originator.Server); addEquityTrade("YHOO", "work", Side.Sell, "6", "1", OrderStatus.Filled, ExecutionType.New, Originator.Broker); addEquityTrade("ABC", "work", Side.Sell, "100", "1"); addEquityTrade("ABC", "work", Side.Buy, "20", "1"); addEquityTrade("ABC", "work", "2", Side.Buy, "20", "1"); } @Override protected void validatePositions(EventList<PositionRow> positions) { assertThat(positions.size(), is(6)); assertEquityPosition(positions.get(0), "ABC", "work", "1", "-80"); assertEquityPosition(positions.get(1), "GOOG", "personal", "1", "45.5"); assertEquityPosition(positions.get(2), "METC", "personal", "1", "1104"); assertEquityPosition(positions.get(3), "METC", "work", "1", "70"); assertEquityPosition(positions.get(4), "YHOO", "work", "1", "14"); assertEquityPosition(positions.get(5), "ABC", "work", "2", "20"); } }.run(); } @Test public void simpleLiveUpdate() { new PositionEngineTestTemplate() { @Override protected int[] getExpectedPositionListChanges() { return new int[] { ListEvent.INSERT, 0 }; } @Override protected void validatePositions(EventList<PositionRow> positions) { addEquityTrade("METC", "personal", Side.Buy, "1000", "1"); assertThat(positions.size(), is(1)); assertEquityPosition(positions.get(0), "METC", "personal", "1", "1000"); } }.run(); } @Test public void complexLiveUpdate() { new PositionEngineTestTemplate() { @Override protected int[] getExpectedPositionListChanges() { return new int[] { ListEvent.INSERT, 0, ListEvent.UPDATE, 0, ListEvent.INSERT, 1, ListEvent.INSERT, 0, ListEvent.INSERT, 3, ListEvent.UPDATE, 3, ListEvent.INSERT, 0, ListEvent.UPDATE, 0 }; } @Override protected void validatePositions(EventList<PositionRow> positions) { addEquityTrade("METC", "personal", Side.Buy, "1000", "1"); assertThat(positions.size(), is(1)); assertEquityPosition(positions.get(0), "METC", "personal", "1", "1000"); addEquityTrade("METC", "personal", Side.Buy, "104", "1"); assertThat(positions.size(), is(1)); assertEquityPosition(positions.get(0), "METC", "personal", "1", "1104"); addEquityTrade("METC", "work", Side.Buy, "70", "1"); assertThat(positions.size(), is(2)); assertEquityPosition(positions.get(0), "METC", "personal", "1", "1104"); assertEquityPosition(positions.get(1), "METC", "work", "1", "70"); addEquityTrade("GOOG", "personal", Side.Buy, "45.5", "1"); assertThat(positions.size(), is(3)); assertEquityPosition(positions.get(0), "GOOG", "personal", "1", "45.5"); assertEquityPosition(positions.get(1), "METC", "personal", "1", "1104"); assertEquityPosition(positions.get(2), "METC", "work", "1", "70"); addEquityTrade("YHOO", "work", Side.Buy, "20", "1"); assertThat(positions.size(), is(4)); assertEquityPosition(positions.get(0), "GOOG", "personal", "1", "45.5"); assertEquityPosition(positions.get(1), "METC", "personal", "1", "1104"); assertEquityPosition(positions.get(2), "METC", "work", "1", "70"); assertEquityPosition(positions.get(3), "YHOO", "work", "1", "20"); addEquityTrade("YHOO", "work", Side.Sell, "6", "1"); assertThat(positions.size(), is(4)); assertEquityPosition(positions.get(0), "GOOG", "personal", "1", "45.5"); assertEquityPosition(positions.get(1), "METC", "personal", "1", "1104"); assertEquityPosition(positions.get(2), "METC", "work", "1", "70"); assertEquityPosition(positions.get(3), "YHOO", "work", "1", "14"); addEquityTrade("ABC", "work", Side.Sell, "100", "1"); assertThat(positions.size(), is(5)); assertEquityPosition(positions.get(0), "ABC", "work", "1", "-100"); assertEquityPosition(positions.get(1), "GOOG", "personal", "1", "45.5"); assertEquityPosition(positions.get(2), "METC", "personal", "1", "1104"); assertEquityPosition(positions.get(3), "METC", "work", "1", "70"); assertEquityPosition(positions.get(4), "YHOO", "work", "1", "14"); addEquityTrade("ABC", "work", Side.Buy, "20", "1"); assertThat(positions.size(), is(5)); assertEquityPosition(positions.get(0), "ABC", "work", "1", "-80"); assertEquityPosition(positions.get(1), "GOOG", "personal", "1", "45.5"); assertEquityPosition(positions.get(2), "METC", "personal", "1", "1104"); assertEquityPosition(positions.get(3), "METC", "work", "1", "70"); assertEquityPosition(positions.get(4), "YHOO", "work", "1", "14"); } }.run(); } @Test public void unknowns() { new PositionEngineTestTemplate() { @Override protected void initReports() { addEquityTrade("METC", null, Side.Buy, "1000", "500"); addEquityTrade("METC", null, Side.Buy, "1000", "500"); addEquityTrade("METC", null, Side.Sell, "200", "500"); } @Override protected void validatePositions(EventList<PositionRow> positions) { assertThat(positions.size(), is(1)); assertEquityPosition(positions.get(0), "METC", null, "1", "1800"); } }.run(); } @Test @Ignore // clearing not currently supported public void clear() { new PositionEngineTestTemplate() { @Override protected void initReports() { addEquityTrade("METC", "personal", Side.Buy, "1000", "500"); } @Override protected int[] getExpectedPositionListChanges() { return new int[] { ListEvent.DELETE, 0, ListEvent.INSERT, 0 }; } @Override protected void validatePositions(EventList<PositionRow> positions) { clearReports(); assertThat(positions.size(), is(0)); addEquityTrade("METC", "personal", Side.Buy, "2000", "500"); assertThat(positions.size(), is(1)); assertEquityPosition(positions.get(0), "METC", "personal", "1", "2000"); } }.run(); } @Test @Ignore // clearing not currently supported public void complexClear() { new PositionEngineTestTemplate() { @Override protected void initReports() { addEquityTrade("METC", "personal", Side.Buy, "1000", "500"); addEquityTrade("METC", "personal", Side.Buy, "100", "500"); } @Override protected int[] getExpectedPositionListChanges() { return new int[] { ListEvent.UPDATE, 0, ListEvent.DELETE, 0, ListEvent.INSERT, 0, ListEvent.UPDATE, 0 }; } @Override protected void validatePositions(EventList<PositionRow> positions) { assertThat(positions.size(), is(1)); assertEquityPosition(positions.get(0), "METC", "personal", "1", "1100"); addEquityTrade("METC", "personal", Side.Buy, "200", "500"); assertEquityPosition(positions.get(0), "METC", "personal", "1", "1300"); clearReports(); assertThat(positions.size(), is(0)); addEquityTrade("METC", "personal", Side.Buy, "2000", "500"); addEquityTrade("METC", "personal", Side.Buy, "2000", "500"); assertThat(positions.size(), is(1)); assertEquityPosition(positions.get(0), "METC", "personal", "1", "4000"); } }.run(); } @Test public void validation() { new PositionEngineTestTemplate() { @Override protected void initReports() { addTrade(null, "personal", "1", Side.Buy, "1000", "500"); addEquityTrade("METC", "personal", Side.Buy, "1000", "0"); addEquityTrade("METC", "personal", Side.Buy, "0", "500"); } @Override protected void validatePositions(EventList<PositionRow> positions) { assertThat(positions.size(), is(0)); } }.run(); } @Test public void grouping() { new PositionEngineTestTemplate() { @Override protected EventList<PositionRow> getPositionData(PositionEngine engine) { return engine.getGroupedData(Grouping.Underlying, Grouping.Account).getPositions(); } @Override protected void initReports() { addEquityTrade("METC", "personal", Side.Buy, "1000", "500"); addEquityTrade("METC", "personal", Side.Buy, "100", "500"); addEquityTrade("METC", "business", Side.Buy, "1050", "500"); addEquityTrade("METC", "business", Side.Buy, "100", "500"); addEquityTrade("IBM", "business", Side.Buy, "300", "500"); addEquityTrade("IBM", "personal", Side.Buy, "200", "500"); } @Override protected void validatePositions(EventList<PositionRow> positions) { // root assertPositions(positions, new String[] { "IBM", "business", "1", "500" }, new String[] { "METC", "business", "1", "2250" }); // first level PositionRow ibm = positions.get(0); assertPositions(ibm.getChildren(), new String[] { "IBM", "business", "1", "300" }, new String[] { "IBM", "personal", "1", "200" }); PositionRow metc = positions.get(1); assertPositions(metc.getChildren(), new String[] { "METC", "business", "1", "1150" }, new String[] { "METC", "personal", "1", "1100" }); // second level PositionRow metcBusiness = metc.getChildren().get(0); assertPositions(metcBusiness.getChildren(), new String[] { "METC", "business", "1", "1150" }); PositionRow metcPersonal = metc.getChildren().get(1); assertPositions(metcPersonal.getChildren(), new String[] { "METC", "personal", "1", "1100" }); PositionRow ibmBusiness = ibm.getChildren().get(0); assertPositions(ibmBusiness.getChildren(), new String[] { "IBM", "business", "1", "300" }); PositionRow ibmPersonal = ibm.getChildren().get(1); assertPositions(ibmPersonal.getChildren(), new String[] { "IBM", "personal", "1", "200" }); // some new trades addEquityTrade("IBM", "business", Side.Sell, "45", "500"); addEquityTrade("GOOG", "abc", Side.Sell, "100", "500"); addEquityTrade("METC", "personal", Side.Sell, "90.3", "500"); // root assertPositions(positions, new String[] { "GOOG", "abc", "1", "-100" }, new String[] { "IBM", "business", "1", "455" }, new String[] { "METC", "business", "1", "2159.7" }); // first level PositionRow goog = positions.get(0); assertPositions(goog.getChildren(), new String[] { "GOOG", "abc", "1", "-100" }); ibm = positions.get(1); assertPositions(ibm.getChildren(), new String[] { "IBM", "business", "1", "255" }, new String[] { "IBM", "personal", "1", "200" }); metc = positions.get(2); assertPositions(metc.getChildren(), new String[] { "METC", "business", "1", "1150" }, new String[] { "METC", "personal", "1", "1009.7" }); // second level metcBusiness = metc.getChildren().get(0); assertPositions(metcBusiness.getChildren(), new String[] { "METC", "business", "1", "1150" }); metcPersonal = metc.getChildren().get(1); assertPositions(metcPersonal.getChildren(), new String[] { "METC", "personal", "1", "1009.7" }); ibmBusiness = ibm.getChildren().get(0); assertPositions(ibmBusiness.getChildren(), new String[] { "IBM", "business", "1", "255" }); ibmPersonal = ibm.getChildren().get(1); assertPositions(ibmPersonal.getChildren(), new String[] { "IBM", "personal", "1", "200" }); PositionRow googAbc = goog.getChildren().get(0); assertPositions(googAbc.getChildren(), new String[] { "GOOG", "abc", "1", "-100" }); } @Override protected int[] getExpectedPositionListChanges() { return new int[] { ListEvent.UPDATE, 0, ListEvent.INSERT, 0, ListEvent.UPDATE, 2 }; } }.run(); } @Test public void grouping2() { new PositionEngineTestTemplate() { @Override protected EventList<PositionRow> getPositionData(PositionEngine engine) { return engine.getGroupedData(Grouping.Trader, Grouping.Account).getPositions(); } @Override protected void initReports() { addEquityTrade("METC", "personal", "1", Side.Buy, "1000", "500"); addEquityTrade("METC", "personal", "2", Side.Buy, "100", "500"); addEquityTrade("IBM", "personal", "2", Side.Sell, "200", "500"); } @Override protected void validatePositions(EventList<PositionRow> positions) { // root assertPositions(positions, new String[] { "METC", "personal", "1", "1000" }, new String[] { "IBM", "personal", "2", "-100" }); // first level PositionRow t1 = positions.get(0); assertPositions(t1.getChildren(), new String[] { "METC", "personal", "1", "1000" }); PositionRow t2 = positions.get(1); assertPositions(t2.getChildren(), new String[] { "IBM", "personal", "2", "-100" }); // second level PositionRow t1Personal = t1.getChildren().get(0); assertPositions(t1Personal.getChildren(), new String[] { "METC", "personal", "1", "1000" }); PositionRow t2Personal = t2.getChildren().get(0); assertPositions(t2Personal.getChildren(), new String[] { "IBM", "personal", "2", "-200" }, new String[] { "METC", "personal", "2", "100" }); // some new trades addEquityTrade("IBM", "business", "2", Side.Sell, "45", "500"); addEquityTrade("GOOG", "abc", "1", Side.Sell, "100", "500"); // root assertPositions(positions, new String[] { "METC", "personal", "1", "900" }, new String[] { "IBM", "personal", "2", "-145" }); // first level t1 = positions.get(0); assertPositions(t1.getChildren(), new String[] { "GOOG", "abc", "1", "-100" }, new String[] { "METC", "personal", "1", "1000" }); t2 = positions.get(1); assertPositions(t2.getChildren(), new String[] { "IBM", "business", "2", "-45" }, new String[] { "IBM", "personal", "2", "-100" }); // second level PositionRow t1Abc = t1.getChildren().get(0); assertPositions(t1Abc.getChildren(), new String[] { "GOOG", "abc", "1", "-100" }); t1Personal = t1.getChildren().get(1); assertPositions(t1Personal.getChildren(), new String[] { "METC", "personal", "1", "1000" }); PositionRow t2Business = t2.getChildren().get(0); assertPositions(t2Business.getChildren(), new String[] { "IBM", "business", "2", "-45" }); t2Personal = t2.getChildren().get(1); assertPositions(t2Personal.getChildren(), new String[] { "IBM", "personal", "2", "-200" }, new String[] { "METC", "personal", "2", "100" }); } @Override protected int[] getExpectedPositionListChanges() { return new int[] { ListEvent.UPDATE, 1, ListEvent.UPDATE, 0 }; } }.run(); } @Test public void incomingPosition() { new PositionEngineTestTemplate() { @Override protected int[] getExpectedPositionListChanges() { return new int[] { ListEvent.UPDATE, 0 }; } @Override protected Map<? extends PositionKey<?>, BigDecimal> getIncomingPositions() { return ImmutableMap.of(PositionKeyFactory.createEquityKey("METC", "personal", "1"), new BigDecimal("100")); } @Override protected void validatePositions(EventList<PositionRow> positions) { assertThat(positions.size(), is(1)); assertEquityPosition(positions.get(0), "METC", "personal", "1", "100"); addEquityTrade("METC", "personal", Side.Buy, "1000", "1"); assertThat(positions.size(), is(1)); assertEquityPosition(positions.get(0), "METC", "personal", "1", "1100"); } }.run(); } @Test public void equityAndOptions() { new PositionEngineTestTemplate() { private UnderlyingSymbolSupport mUnderlyingSymbolSupport; private final Equity equity = new Equity("METC"); private final Option option1 = new Option("MTC", "20090910", BigDecimal.ONE, OptionType.Call); private final Option option2 = new Option("MEC", "20090910", BigDecimal.ONE, OptionType.Call); private final Option option3 = new Option("PXR", "20090910", BigDecimal.ONE, OptionType.Call); protected UnderlyingSymbolSupport createUnderlyingSymbolSupport() { mUnderlyingSymbolSupport = mock(UnderlyingSymbolSupport.class); when(mUnderlyingSymbolSupport.getUnderlying(equity)).thenReturn("METC"); when(mUnderlyingSymbolSupport.getUnderlying(option1)).thenReturn("METC"); when(mUnderlyingSymbolSupport.getUnderlying(option2)).thenReturn("METC"); when(mUnderlyingSymbolSupport.getUnderlying(option3)).thenReturn("YHOO"); return mUnderlyingSymbolSupport; }; @Override protected void initReports() { addTrade(option3, "personal", "2", Side.Buy, "20", "1"); addTrade(option1, "personal", "1", Side.Buy, "104", "1"); addTrade(option1, "personal", "1", Side.Sell, "104", "1"); addTrade(equity, "personal", "1", Side.Buy, "1000", "1"); addTrade(equity, "personal", "1", Side.Buy, "104", "1"); addTrade(option2, "personal", "1", Side.Buy, "20", "1"); } @Override protected void validatePositions(EventList<PositionRow> positions) { assertThat(positions.size(), is(4)); assertEquityPosition(positions.get(0), "METC", "personal", "1", "1104"); assertPosition(positions.get(1), option2, "METC", "personal", "1", "20"); assertPosition(positions.get(2), option1, "METC", "personal", "1", "0"); assertPosition(positions.get(3), option3, "YHOO", "personal", "2", "20"); } }.run(); } @Test public void groupingWithOptions() { new PositionEngineTestTemplate() { private UnderlyingSymbolSupport mUnderlyingSymbolSupport; private final Equity metcEquity = new Equity("METC"); private final Equity ibmEquity = new Equity("IBM"); private final Equity yhooEquity = new Equity("YHOO"); private final Option metcOption = new Option("MTC", "20090910", BigDecimal.ONE, OptionType.Call); private final Option yhooOption = new Option("PXR", "20090910", BigDecimal.ONE, OptionType.Call); protected UnderlyingSymbolSupport createUnderlyingSymbolSupport() { mUnderlyingSymbolSupport = mock(UnderlyingSymbolSupport.class); when(mUnderlyingSymbolSupport.getUnderlying(metcEquity)) .thenReturn("METC"); when(mUnderlyingSymbolSupport.getUnderlying(ibmEquity)) .thenReturn("IBM"); when(mUnderlyingSymbolSupport.getUnderlying(yhooEquity)) .thenReturn("YHOO"); when(mUnderlyingSymbolSupport.getUnderlying(metcOption)) .thenReturn("METC"); when(mUnderlyingSymbolSupport.getUnderlying(yhooOption)) .thenReturn("YHOO"); return mUnderlyingSymbolSupport; }; @Override protected EventList<PositionRow> getPositionData( PositionEngine engine) { return engine.getGroupedData(Grouping.Account, Grouping.Underlying).getPositions(); } @Override protected void initReports() { addEquityTrade("METC", "personal", Side.Buy, "1000", "500"); addEquityTrade("METC", "personal", Side.Buy, "100", "500"); addEquityTrade("METC", "business", Side.Buy, "1050", "500"); addEquityTrade("METC", "business", Side.Buy, "100", "500"); addEquityTrade("IBM", "business", Side.Buy, "300", "500"); addEquityTrade("YHOO", "personal", Side.Buy, "200", "500"); addEquityTrade("YHOO", "personal", Side.Buy, "200", "500"); addTrade(metcOption, "business", "1", Side.Buy, "300", "500"); addTrade(yhooOption, "personal", "1", Side.Buy, "400", "500"); } @Override protected void validatePositions(EventList<PositionRow> positions) { // root assertPositions(positions, new String[] { "IBM", "business", "1", "1750" }, new String[] { "METC", "personal", "1", "1900" }); // first level PositionRow business = positions.get(0); assertPositions(business.getChildren(), new String[] { "IBM", "business", "1", "300" }, new String[] { "METC", "business", "1", "1450" }); PositionRow personal = positions.get(1); assertPositions(personal.getChildren(), new String[] { "METC", "personal", "1", "1100" }, new String[] { "YHOO", "personal", "1", "800" }); // second level PositionRow ibmBusiness = business.getChildren().get(0); assertPositions(ibmBusiness.getChildren(), new String[] { "IBM", "business", "1", "300" }); PositionRow metcBusiness = business.getChildren().get(1); assertPositions(metcBusiness.getChildren(), new String[] { "METC", "business", "1", "1450" }); PositionRow metcPersonal = personal.getChildren().get(0); assertPositions(metcPersonal.getChildren(), new String[] { "METC", "personal", "1", "1100" }); PositionRow yhooPersonal = personal.getChildren().get(1); assertPositions(yhooPersonal.getChildren(), new String[] { "YHOO", "personal", "1", "800" }); // third level PositionRow ibmBusiness1 = ibmBusiness.getChildren().get(0); assertPositions(ibmBusiness1.getChildren(), new String[] { "IBM", "business", "1", "300" }); PositionRow metcBusiness1 = metcBusiness.getChildren().get(0); assertPosition(metcBusiness1.getChildren().get(0), metcEquity, "METC", "business", "1", "1150"); assertPosition(metcBusiness1.getChildren().get(1), metcOption, "METC", "business", "1", "300"); PositionRow metcPersonal1 = metcPersonal.getChildren().get(0); assertPositions(metcPersonal1.getChildren(), new String[] { "METC", "personal", "1", "1100" }); PositionRow yhooPersonal1 = yhooPersonal.getChildren().get(0); assertPosition(yhooPersonal1.getChildren().get(0), yhooEquity, "YHOO", "personal", "1", "400"); assertPosition(yhooPersonal1.getChildren().get(1), yhooOption, "YHOO", "personal", "1", "400"); } }.run(); } }