package name.abuchen.portfolio.snapshot; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import java.math.BigDecimal; import java.time.LocalDate; import java.time.Month; import java.util.List; import org.junit.Test; import name.abuchen.portfolio.AccountBuilder; import name.abuchen.portfolio.PortfolioBuilder; import name.abuchen.portfolio.SecurityBuilder; import name.abuchen.portfolio.TestCurrencyConverter; import name.abuchen.portfolio.model.Account; import name.abuchen.portfolio.model.AccountTransaction; import name.abuchen.portfolio.model.AccountTransferEntry; import name.abuchen.portfolio.model.Client; import name.abuchen.portfolio.model.Portfolio; import name.abuchen.portfolio.model.PortfolioTransaction; import name.abuchen.portfolio.model.Security; import name.abuchen.portfolio.model.SecurityPrice; import name.abuchen.portfolio.model.Transaction; import name.abuchen.portfolio.model.Transaction.Unit; import name.abuchen.portfolio.money.CurrencyConverter; import name.abuchen.portfolio.money.CurrencyUnit; import name.abuchen.portfolio.money.Money; import name.abuchen.portfolio.money.MutableMoney; import name.abuchen.portfolio.money.Values; import name.abuchen.portfolio.snapshot.ClientPerformanceSnapshot.Category; import name.abuchen.portfolio.snapshot.ClientPerformanceSnapshot.CategoryType; @SuppressWarnings("nls") public class ClientPerformanceSnapshotTest { private final LocalDate startDate = LocalDate.of(2010, Month.DECEMBER, 31); private final LocalDate endDate = LocalDate.of(2011, Month.DECEMBER, 31); @Test public void testDepositPlusInterest() { Client client = new Client(); Account account = new Account(); account.addTransaction(new AccountTransaction(LocalDate.of(2010, Month.JANUARY, 1), CurrencyUnit.EUR, 1000_00, null, AccountTransaction.Type.DEPOSIT)); account.addTransaction(new AccountTransaction(LocalDate.of(2011, Month.JUNE, 1), CurrencyUnit.EUR, 50_00, null, AccountTransaction.Type.INTEREST)); client.addAccount(account); CurrencyConverter converter = new TestCurrencyConverter(); ClientPerformanceSnapshot snapshot = new ClientPerformanceSnapshot(client, converter, startDate, endDate); assertNotNull(snapshot); assertNotNull(snapshot.getStartClientSnapshot()); assertEquals(startDate, snapshot.getStartClientSnapshot().getTime()); assertNotNull(snapshot.getEndClientSnapshot()); assertEquals(endDate, snapshot.getEndClientSnapshot().getTime()); List<Category> categories = snapshot.getCategories(); assertNotNull(categories); assertThat(categories.size(), is(ClientPerformanceSnapshot.CategoryType.values().length)); assertThat(snapshot.getValue(CategoryType.INITIAL_VALUE), is(Money.of(CurrencyUnit.EUR, 1000_00))); assertThat(snapshot.getValue(CategoryType.EARNINGS), is(Money.of(CurrencyUnit.EUR, 50_00))); assertThat(snapshot.getValue(CategoryType.FINAL_VALUE), is(Money.of(CurrencyUnit.EUR, 1050_00))); assertThat(snapshot.getAbsoluteDelta(), is(snapshot.getValue(CategoryType.FINAL_VALUE) .subtract(snapshot.getValue(CategoryType.TRANSFERS)) .subtract(snapshot.getValue(CategoryType.INITIAL_VALUE)))); } @Test public void testDepositPlusInterestFirstDay() { Client client = new Client(); Account account = new Account(); account.addTransaction(new AccountTransaction(LocalDate.of(2010, Month.JANUARY, 1), CurrencyUnit.EUR, 1000_00, null, AccountTransaction.Type.DEPOSIT)); account.addTransaction(new AccountTransaction(LocalDate.of(2010, Month.DECEMBER, 31), CurrencyUnit.EUR, 50_00, null, AccountTransaction.Type.INTEREST)); client.addAccount(account); CurrencyConverter converter = new TestCurrencyConverter(); ClientPerformanceSnapshot snapshot = new ClientPerformanceSnapshot(client, converter, startDate, endDate); assertThat(snapshot.getValue(CategoryType.INITIAL_VALUE), is(Money.of(CurrencyUnit.EUR, 1050_00))); assertThat(snapshot.getValue(CategoryType.EARNINGS), is(Money.of(CurrencyUnit.EUR, 0))); assertThat(snapshot.getValue(CategoryType.FINAL_VALUE), is(Money.of(CurrencyUnit.EUR, 1050_00))); assertThat(snapshot.getAbsoluteDelta(), is(snapshot.getValue(CategoryType.FINAL_VALUE) .subtract(snapshot.getValue(CategoryType.TRANSFERS)) .subtract(snapshot.getValue(CategoryType.INITIAL_VALUE)))); } @Test public void testDepositPlusInterestLastDay() { Client client = new Client(); Account account = new Account(); account.addTransaction(new AccountTransaction(LocalDate.of(2010, Month.JANUARY, 1), CurrencyUnit.EUR, 1000_00, null, AccountTransaction.Type.DEPOSIT)); account.addTransaction(new AccountTransaction(LocalDate.of(2011, Month.DECEMBER, 31), CurrencyUnit.EUR, 50_00, null, AccountTransaction.Type.INTEREST)); client.addAccount(account); CurrencyConverter converter = new TestCurrencyConverter(); ClientPerformanceSnapshot snapshot = new ClientPerformanceSnapshot(client, converter, startDate, endDate); assertThat(snapshot.getValue(CategoryType.INITIAL_VALUE), is(Money.of(CurrencyUnit.EUR, 1000_00))); assertThat(snapshot.getValue(CategoryType.EARNINGS), is(Money.of(CurrencyUnit.EUR, 50_00))); assertThat(snapshot.getValue(CategoryType.CAPITAL_GAINS), is(Money.of(CurrencyUnit.EUR, 0))); assertThat(snapshot.getValue(CategoryType.FINAL_VALUE), is(Money.of(CurrencyUnit.EUR, 1050_00))); assertThat(snapshot.getAbsoluteDelta(), is(snapshot.getValue(CategoryType.FINAL_VALUE) .subtract(snapshot.getValue(CategoryType.TRANSFERS)) .subtract(snapshot.getValue(CategoryType.INITIAL_VALUE)))); } @Test public void testEarningsFromSecurity() { Client client = new Client(); Security security = new Security(); client.addSecurity(security); Portfolio portfolio = new Portfolio(); portfolio.setReferenceAccount(new Account()); portfolio.addTransaction(new PortfolioTransaction(LocalDate.of(2010, Month.JANUARY, 1), CurrencyUnit.EUR, 1_00, security, Values.Share.factorize(10), PortfolioTransaction.Type.BUY, 0, 0)); client.addPortfolio(portfolio); Account account = new Account(); account.addTransaction(new AccountTransaction(LocalDate.of(2011, Month.JANUARY, 31), CurrencyUnit.EUR, 50_00, security, AccountTransaction.Type.INTEREST)); client.addAccount(account); CurrencyConverter converter = new TestCurrencyConverter(); ClientPerformanceSnapshot snapshot = new ClientPerformanceSnapshot(client, converter, startDate, endDate); assertThat(snapshot.getValue(CategoryType.EARNINGS), is(Money.of(CurrencyUnit.EUR, 50_00))); assertThat(snapshot.getCategoryByType(CategoryType.EARNINGS).getPositions().size(), is(1)); assertThat(snapshot.getAbsoluteDelta(), is(snapshot.getValue(CategoryType.FINAL_VALUE) .subtract(snapshot.getValue(CategoryType.TRANSFERS)) .subtract(snapshot.getValue(CategoryType.INITIAL_VALUE)))); } @Test public void testCapitalGains() { Client client = new Client(); Security security = new Security(); security.addPrice(new SecurityPrice(LocalDate.of(2010, Month.JANUARY, 1), Values.Quote.factorize(100))); security.addPrice(new SecurityPrice(LocalDate.of(2011, Month.JUNE, 1), Values.Quote.factorize(110))); client.addSecurity(security); Portfolio portfolio = new Portfolio(); portfolio.setReferenceAccount(new Account()); portfolio.addTransaction(new PortfolioTransaction(LocalDate.of(2010, Month.JANUARY, 1), CurrencyUnit.EUR, 1_00, security, Values.Share.factorize(10), PortfolioTransaction.Type.BUY, 0, 0)); client.addPortfolio(portfolio); CurrencyConverter converter = new TestCurrencyConverter(); ClientPerformanceSnapshot snapshot = new ClientPerformanceSnapshot(client, converter, startDate, endDate); assertThat(snapshot.getValue(CategoryType.INITIAL_VALUE), is(Money.of(CurrencyUnit.EUR, 1000_00))); assertThat(snapshot.getValue(CategoryType.EARNINGS), is(Money.of(CurrencyUnit.EUR, 0))); assertThat(snapshot.getValue(CategoryType.CAPITAL_GAINS), is(Money.of(CurrencyUnit.EUR, 100_00))); assertThat(snapshot.getValue(CategoryType.FINAL_VALUE), is(Money.of(CurrencyUnit.EUR, 1100_00))); assertThat(snapshot.getAbsoluteDelta(), is(snapshot.getValue(CategoryType.FINAL_VALUE) .subtract(snapshot.getValue(CategoryType.TRANSFERS)) .subtract(snapshot.getValue(CategoryType.INITIAL_VALUE)))); } @Test public void testCapitalGainsWithBuyDuringReportPeriod() { Client client = new Client(); Security security = new Security(); security.addPrice(new SecurityPrice(LocalDate.of(2010, Month.JANUARY, 1), Values.Quote.factorize(100))); security.addPrice(new SecurityPrice(LocalDate.of(2011, Month.JUNE, 1), Values.Quote.factorize(110))); client.addSecurity(security); Portfolio portfolio = new Portfolio(); portfolio.setReferenceAccount(new Account()); portfolio.addTransaction(new PortfolioTransaction("2010-01-01", CurrencyUnit.EUR, 1_00, security, Values.Share.factorize(10), PortfolioTransaction.Type.BUY, 0, 0)); portfolio.addTransaction(new PortfolioTransaction("2011-01-15", CurrencyUnit.EUR, 99_00, security, Values.Share.factorize(1), PortfolioTransaction.Type.DELIVERY_INBOUND, 0, 0)); client.addPortfolio(portfolio); CurrencyConverter converter = new TestCurrencyConverter(); ClientPerformanceSnapshot snapshot = new ClientPerformanceSnapshot(client, converter, startDate, endDate); assertThat(snapshot.getValue(CategoryType.INITIAL_VALUE), is(Money.of(CurrencyUnit.EUR, 1000_00))); assertThat(snapshot.getValue(CategoryType.EARNINGS), is(Money.of(CurrencyUnit.EUR, 0))); assertThat(snapshot.getValue(CategoryType.CAPITAL_GAINS), is(Money.of(CurrencyUnit.EUR, 100_00 + (110_00 - 99_00)))); assertThat(snapshot.getValue(CategoryType.FINAL_VALUE), is(Money.of(CurrencyUnit.EUR, 1210_00))); assertThat(snapshot.getAbsoluteDelta(), is(snapshot.getValue(CategoryType.FINAL_VALUE) .subtract(snapshot.getValue(CategoryType.TRANSFERS)) .subtract(snapshot.getValue(CategoryType.INITIAL_VALUE)))); } @Test public void testCapitalGainsWithPartialSellDuringReportPeriod() { Client client = new Client(); Security security = new SecurityBuilder() // .addPrice("2010-01-01", Values.Quote.factorize(100)) // .addPrice("2011-06-01", Values.Quote.factorize(110)) // .addTo(client); Account account = new AccountBuilder() // .deposit_("2010-01-01", 1_00) // .withdraw("2011-01-15", 99_00) // .addTo(client); new PortfolioBuilder(account) // .buy(security, "2010-01-01", Values.Share.factorize(10), 1_00) // .sell(security, "2011-01-15", Values.Share.factorize(1), 99_00) // .addTo(client); CurrencyConverter converter = new TestCurrencyConverter(); ClientPerformanceSnapshot snapshot = new ClientPerformanceSnapshot(client, converter, startDate, endDate); assertThat(snapshot.getValue(CategoryType.INITIAL_VALUE), is(Money.of(CurrencyUnit.EUR, 1000_00))); assertThat(snapshot.getValue(CategoryType.EARNINGS), is(Money.of(CurrencyUnit.EUR, 0))); assertThat(snapshot.getValue(CategoryType.CAPITAL_GAINS), is(Money.of(CurrencyUnit.EUR, 10_00 * 9 + (99_00 - 100_00)))); assertThat(snapshot.getValue(CategoryType.FINAL_VALUE), is(Money.of(CurrencyUnit.EUR, 110_00 * 9))); assertThat(snapshot.getAbsoluteDelta(), is(snapshot.getValue(CategoryType.FINAL_VALUE) .subtract(snapshot.getValue(CategoryType.TRANSFERS)) .subtract(snapshot.getValue(CategoryType.INITIAL_VALUE)))); } @Test public void testCapitalGainsWithPartialSellDuringReportPeriodWithFees() { Client client = new Client(); Security security = new SecurityBuilder() // .addPrice("2010-01-01", Values.Quote.factorize(100)) // .addPrice("2011-06-01", Values.Quote.factorize(110)) // .addTo(client); Account account = new AccountBuilder() // .deposit_("2010-01-01", 1_00) // .withdraw("2011-01-15", 99_00) // .addTo(client); new PortfolioBuilder(account) // .buy(security, "2010-01-01", Values.Share.factorize(10), 1_00) // .sell(security, "2011-01-15", Values.Share.factorize(1), 99_00, 1) // .addTo(client); CurrencyConverter converter = new TestCurrencyConverter(); ClientPerformanceSnapshot snapshot = new ClientPerformanceSnapshot(client, converter, startDate, endDate); assertThat(snapshot.getValue(CategoryType.CAPITAL_GAINS), is(Money.of(CurrencyUnit.EUR, 1000 * 9 + (9900 - 10000) + 1))); assertThat(snapshot.getValue(CategoryType.FEES), is(Money.of(CurrencyUnit.EUR, 1))); assertThat(snapshot.getAbsoluteDelta(), is(snapshot.getValue(CategoryType.FINAL_VALUE) .subtract(snapshot.getValue(CategoryType.TRANSFERS)) .subtract(snapshot.getValue(CategoryType.INITIAL_VALUE)))); } @Test public void testCurrencyGainsWithIntermediateTransactions() { Client client = new Client(); new AccountBuilder("USD") // .deposit_("2015-01-01", 1000_00) // .deposit_("2015-01-05", 200_00) // .withdraw("2015-01-10", 500_00) // .fees____("2015-01-12", 20_00) // .addTo(client); CurrencyConverter converter = new TestCurrencyConverter(); ClientPerformanceSnapshot snapshot = new ClientPerformanceSnapshot(client, converter, // LocalDate.parse("2015-01-02"), LocalDate.parse("2015-01-15")); assertThat(snapshot.getValue(CategoryType.INITIAL_VALUE), is(Money.of(CurrencyUnit.EUR, Math.round(1000_00 * (1 / 1.2043))))); assertThat(snapshot.getValue(CategoryType.FINAL_VALUE), is(Money.of(CurrencyUnit.EUR, Math.round(680_00 * (1 / 1.1708))))); assertThat(snapshot.getAbsoluteDelta(), is(snapshot.getValue(CategoryType.FINAL_VALUE) .subtract(snapshot.getValue(CategoryType.TRANSFERS)) .subtract(snapshot.getValue(CategoryType.INITIAL_VALUE)))); } @Test public void testCurrencyGainsWithTransferalInOtherCurrencies() { Client client = new Client(); Account usd = new AccountBuilder("USD") // .deposit_("2015-01-01", 1000_00) // .addTo(client); Account cad = new AccountBuilder("CAD") // .deposit_("2015-01-01", 1000_00) // .addTo(client); // insert account transfer AccountTransferEntry entry = new AccountTransferEntry(usd, cad); entry.setDate(LocalDate.parse("2015-01-10")); AccountTransaction source = entry.getSourceTransaction(); AccountTransaction target = entry.getTargetTransaction(); source.setMonetaryAmount(Money.of("USD", 500_00)); target.setMonetaryAmount(Money.of("CAD", 1000_00)); source.addUnit(new Unit(Unit.Type.GROSS_VALUE, source.getMonetaryAmount(), target.getMonetaryAmount(), BigDecimal.valueOf(.5))); entry.insert(); // check currency gain calculation of client performance snapshot CurrencyConverter converter = new TestCurrencyConverter(); ClientPerformanceSnapshot snapshot = new ClientPerformanceSnapshot(client, converter, // LocalDate.parse("2015-01-01"), LocalDate.parse("2015-01-15")); MutableMoney currencyGains = MutableMoney.of(converter.getTermCurrency()); currencyGains.subtract(snapshot.getValue(CategoryType.INITIAL_VALUE)); currencyGains.subtract(snapshot.getValue(CategoryType.CAPITAL_GAINS)); currencyGains.subtract(snapshot.getValue(CategoryType.EARNINGS)); currencyGains.add(snapshot.getValue(CategoryType.FEES)); currencyGains.add(snapshot.getValue(CategoryType.TAXES)); currencyGains.add(snapshot.getValue(CategoryType.TRANSFERS)); currencyGains.add(snapshot.getValue(CategoryType.FINAL_VALUE)); assertThat(snapshot.getCategoryByType(CategoryType.CURRENCY_GAINS).getValuation(), is(currencyGains.toMoney())); } @Test public void testDividendTransactionWithTaxes() { Client client = new Client(); Security security = new SecurityBuilder().addTo(client); Account account = new AccountBuilder().addTo(client); AccountTransaction dividend = new AccountTransaction(); dividend.setDate(LocalDate.parse("2011-03-01")); dividend.setType(AccountTransaction.Type.DIVIDENDS); dividend.setSecurity(security); dividend.setMonetaryAmount(Money.of(CurrencyUnit.EUR, 100_00)); dividend.addUnit(new Transaction.Unit(Transaction.Unit.Type.TAX, Money.of(CurrencyUnit.EUR, 10_00))); assertThat(dividend.getGrossValue(), is(Money.of(CurrencyUnit.EUR, 110_00))); account.addTransaction(dividend); CurrencyConverter converter = new TestCurrencyConverter(); ClientPerformanceSnapshot snapshot = new ClientPerformanceSnapshot(client, converter, startDate, endDate); assertThat(snapshot.getValue(CategoryType.EARNINGS), is(Money.of(CurrencyUnit.EUR, 110_00))); assertThat(snapshot.getValue(CategoryType.TAXES), is(Money.of(CurrencyUnit.EUR, 10_00))); assertThat(snapshot.getEarnings().size(), is(1)); assertThat(snapshot.getCategoryByType(CategoryType.EARNINGS).getPositions().get(0).getValuation(), is(Money.of(CurrencyUnit.EUR, 110_00))); GroupEarningsByAccount.Item item = new GroupEarningsByAccount(snapshot).getItems().get(0); assertThat(item.getSum(), is(Money.of(CurrencyUnit.EUR, 110_00))); assertThatCalculationWorksOut(snapshot, converter); } @Test public void testInboundDeliveryWithFees() { Client client = new Client(); Security security = new SecurityBuilder().addTo(client); Portfolio portfolio = new PortfolioBuilder().addTo(client); PortfolioTransaction delivery = new PortfolioTransaction(); delivery.setDate(LocalDate.parse("2011-03-01")); delivery.setType(PortfolioTransaction.Type.DELIVERY_INBOUND); delivery.setSecurity(security); delivery.setMonetaryAmount(Money.of(CurrencyUnit.EUR, 100_00)); delivery.addUnit(new Transaction.Unit(Transaction.Unit.Type.TAX, Money.of(CurrencyUnit.EUR, 10_00))); delivery.addUnit(new Transaction.Unit(Transaction.Unit.Type.FEE, Money.of(CurrencyUnit.EUR, 10_00))); assertThat(delivery.getGrossValue(), is(Money.of(CurrencyUnit.EUR, 80_00))); portfolio.addTransaction(delivery); CurrencyConverter converter = new TestCurrencyConverter(); ClientPerformanceSnapshot snapshot = new ClientPerformanceSnapshot(client, converter, startDate, endDate); assertThat(snapshot.getValue(CategoryType.EARNINGS), is(Money.of(CurrencyUnit.EUR, 0))); assertThat(snapshot.getValue(CategoryType.FEES), is(Money.of(CurrencyUnit.EUR, 10_00))); assertThat(snapshot.getValue(CategoryType.TAXES), is(Money.of(CurrencyUnit.EUR, 10_00))); assertThat(snapshot.getValue(CategoryType.TRANSFERS), is(Money.of(CurrencyUnit.EUR, 100_00))); assertThatCalculationWorksOut(snapshot, converter); } @Test public void testInterestCharge() { Client client = new Client(); Account account = new AccountBuilder() // .interest_charge("2011-01-01", Values.Amount.factorize(100)) // .addTo(client); CurrencyConverter converter = new TestCurrencyConverter(); ClientPerformanceSnapshot snapshot = new ClientPerformanceSnapshot(client, converter, startDate, endDate); assertThat(snapshot.getValue(CategoryType.EARNINGS), is(Money.of(CurrencyUnit.EUR, Values.Amount.factorize(-100)))); assertThat(snapshot.getCategoryByType(CategoryType.EARNINGS).getPositions().size(), is(1)); assertThat(snapshot.getCategoryByType(CategoryType.EARNINGS).getPositions().get(0).getValuation(), is(Money.of(CurrencyUnit.EUR, Values.Amount.factorize(-100)))); assertThat(snapshot.getEarnings().size(), is(1)); assertThat(snapshot.getEarnings().get(0).getTransaction().getMonetaryAmount(), is(Money.of(CurrencyUnit.EUR, Values.Amount.factorize(100)))); GroupEarningsByAccount grouping = new GroupEarningsByAccount(snapshot); assertThat(grouping.getItems().size(), is(1)); assertThat(grouping.getItems().get(0).getAccount(), is(account)); assertThat(grouping.getItems().get(0).getSum(), is(Money.of(CurrencyUnit.EUR, Values.Amount.factorize(-100)))); assertThatCalculationWorksOut(snapshot, converter); } @Test public void testFeesRefund() { Client client = new Client(); new AccountBuilder() // .fees_refund("2011-01-01", Values.Amount.factorize(100)) // .addTo(client); CurrencyConverter converter = new TestCurrencyConverter(); ClientPerformanceSnapshot snapshot = new ClientPerformanceSnapshot(client, converter, startDate, endDate); assertThat(snapshot.getValue(CategoryType.FEES), is(Money.of(CurrencyUnit.EUR, Values.Amount.factorize(-100)))); assertThat(snapshot.getFees().size(), is(1)); assertThat(snapshot.getFees().get(0).getTransaction().getMonetaryAmount(), is(Money.of(CurrencyUnit.EUR, Values.Amount.factorize(100)))); assertThatCalculationWorksOut(snapshot, converter); } private void assertThatCalculationWorksOut(ClientPerformanceSnapshot snapshot, CurrencyConverter converter) { MutableMoney valueAtEndOfPeriod = MutableMoney.of(converter.getTermCurrency()); valueAtEndOfPeriod.add(snapshot.getValue(CategoryType.INITIAL_VALUE)); valueAtEndOfPeriod.add(snapshot.getValue(CategoryType.CAPITAL_GAINS)); valueAtEndOfPeriod.add(snapshot.getValue(CategoryType.EARNINGS)); valueAtEndOfPeriod.subtract(snapshot.getValue(CategoryType.FEES)); valueAtEndOfPeriod.subtract(snapshot.getValue(CategoryType.TAXES)); valueAtEndOfPeriod.add(snapshot.getValue(CategoryType.CURRENCY_GAINS)); valueAtEndOfPeriod.add(snapshot.getValue(CategoryType.TRANSFERS)); assertThat(valueAtEndOfPeriod.toMoney(), is(snapshot.getValue(CategoryType.FINAL_VALUE))); } }