package name.abuchen.portfolio.snapshot;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.time.LocalDate;
import java.time.Month;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.hamcrest.number.IsCloseTo;
import org.junit.Test;
import name.abuchen.portfolio.AccountBuilder;
import name.abuchen.portfolio.Messages;
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.Client;
import name.abuchen.portfolio.model.Security;
import name.abuchen.portfolio.money.CurrencyConverter;
import name.abuchen.portfolio.money.CurrencyUnit;
import name.abuchen.portfolio.money.Values;
import name.abuchen.portfolio.util.Dates;
@SuppressWarnings("nls")
public class ClientIndexTest
{
private static final double PRECISION = 0.000001d;
private Client createClient()
{
Client client = new Client();
new AccountBuilder() //
.deposit_("2011-12-31", 10000 * Values.Amount.factor()) //
.interest("2012-01-01", 230 * Values.Amount.factor()) //
.deposit_("2012-01-02", 200 * Values.Amount.factor()) //
.interest("2012-01-02", 200 * Values.Amount.factor()) //
.withdraw("2012-01-03", 400 * Values.Amount.factor()) //
.fees____("2012-01-03", 23441) //
.interest("2012-01-04", 29399) //
.interest("2012-01-05", 29399) //
.deposit_("2012-01-06", 5400 * Values.Amount.factor()) //
.interest("2012-01-06", 19599) //
.withdraw("2012-01-07", 369704) //
.fees____("2012-01-07", 88252) //
.fees____("2012-01-08", 100385) //
.addTo(client);
return client;
}
@Test
public void testExcelSample()
{
Client client = createClient();
ReportingPeriod.FromXtoY period = new ReportingPeriod.FromXtoY(LocalDate.of(2011, Month.DECEMBER, 31), //
LocalDate.of(2012, Month.JANUARY, 8));
CurrencyConverter converter = new TestCurrencyConverter();
ClientIndex index = PerformanceIndex.forClient(client, converter, period, new ArrayList<Exception>());
assertNotNull(index);
assertThat(period.toInterval(), is(index.getReportInterval().toInterval()));
assertThat(client, is(index.getClient()));
LocalDate[] dates = index.getDates();
assertThat(dates.length, is(Dates.daysBetween(period.getStartDate(), period.getEndDate()) + 1));
double[] delta = index.getDeltaPercentage();
assertThat(delta[0], is(0d));
assertThat(delta[1], IsCloseTo.closeTo(0.023d, PRECISION));
assertThat(delta[2], IsCloseTo.closeTo(0.0195503d, PRECISION));
assertThat(delta[3], IsCloseTo.closeTo(-0.0220517d, PRECISION));
assertThat(delta[4], IsCloseTo.closeTo(0.0294117647d, PRECISION));
assertThat(delta[5], IsCloseTo.closeTo(0.0285714286d, PRECISION));
assertThat(delta[6], IsCloseTo.closeTo(0.0185185185d, PRECISION));
assertThat(delta[7], IsCloseTo.closeTo(-0.0545454545d, PRECISION));
assertThat(delta[8], IsCloseTo.closeTo(-0.0865384615d, PRECISION));
double[] accumulated = index.getAccumulatedPercentage();
assertThat(accumulated[0], is(0d));
assertThat(accumulated[1], IsCloseTo.closeTo(0.023d, PRECISION));
assertThat(accumulated[2], IsCloseTo.closeTo(0.043d, PRECISION));
assertThat(accumulated[3], IsCloseTo.closeTo(0.02d, PRECISION));
assertThat(accumulated[4], IsCloseTo.closeTo(0.05d, PRECISION));
assertThat(accumulated[5], IsCloseTo.closeTo(0.08d, PRECISION));
assertThat(accumulated[6], IsCloseTo.closeTo(0.10d, PRECISION));
assertThat(accumulated[7], IsCloseTo.closeTo(0.04d, PRECISION));
assertThat(accumulated[8], IsCloseTo.closeTo(-0.05d, PRECISION));
}
private Client createClient(double[] delta, long[] transferals)
{
Client client = new Client();
AccountBuilder account = new AccountBuilder();
LocalDate time = LocalDate.parse("2012-01-01");
long valuation = 0;
double quote = 1;
for (int ii = 0; ii < delta.length; ii++)
{
long v = (long) Math.round((double) valuation * (delta[ii] + 1) / quote);
long d = v - valuation;
if (transferals[ii] > 0)
account.deposit_(time, transferals[ii]);
else if (transferals[ii] < 0)
account.withdraw(time, Math.abs(transferals[ii]));
if (v > 0)
account.interest(time, d);
else if (v < 0)
account.fees____(time, Math.abs(d));
valuation = v + transferals[ii];
quote = 1 + delta[ii];
time = time.plusDays(1);
}
account.addTo(client);
return client;
}
@Test
public void testThatTransferalsDoNotChangePerformance()
{
double[] delta = { 0d, 0.023d, 0.043d, 0.02d, 0.05d, 0.08d, 0.1d, 0.04d, -0.05d };
long[] transferals = { 1000000, 0, 20000, -40000, 0, 0, 540000, -369704, 0 };
long[] transferals2 = { 1000000, 0, 0, 0, 0, 0, 0, 0, 0 };
Client client = createClient(delta, transferals);
ReportingPeriod.FromXtoY period = new ReportingPeriod.FromXtoY(LocalDate.of(2012, Month.JANUARY, 1), //
LocalDate.of(2012, Month.JANUARY, 9));
CurrencyConverter converter = new TestCurrencyConverter();
ClientIndex index = PerformanceIndex.forClient(client, converter, period, new ArrayList<Exception>());
double[] accumulated = index.getAccumulatedPercentage();
for (int ii = 0; ii < accumulated.length; ii++)
assertThat(accumulated[ii], IsCloseTo.closeTo(delta[ii], PRECISION));
Client anotherClient = createClient(delta, transferals2);
index = PerformanceIndex.forClient(anotherClient, converter, period, new ArrayList<Exception>());
accumulated = index.getAccumulatedPercentage();
for (int ii = 0; ii < accumulated.length; ii++)
assertThat(accumulated[ii], IsCloseTo.closeTo(delta[ii], PRECISION));
}
@Test
public void testThatNoValuationResultsInZeroPerformance()
{
Client client = new Client();
ReportingPeriod.FromXtoY period = new ReportingPeriod.FromXtoY(LocalDate.of(2012, Month.JANUARY, 1), //
LocalDate.of(2012, Month.JANUARY, 9));
CurrencyConverter converter = new TestCurrencyConverter();
ClientIndex index = PerformanceIndex.forClient(client, converter, period, new ArrayList<Exception>());
double[] accumulated = index.getAccumulatedPercentage();
for (int ii = 0; ii < accumulated.length; ii++)
assertThat(accumulated[ii], IsCloseTo.closeTo(0d, PRECISION));
}
@Test
public void testThatInterstWithoutInvestmentDoesNotCorruptResultAndIsReported()
{
Client client = new Client();
new AccountBuilder() //
.interest("2012-01-02", 100) //
.addTo(client);
ReportingPeriod.FromXtoY period = new ReportingPeriod.FromXtoY(LocalDate.of(2012, Month.JANUARY, 1), //
LocalDate.of(2012, Month.JANUARY, 9));
List<Exception> errors = new ArrayList<Exception>();
CurrencyConverter converter = new TestCurrencyConverter();
ClientIndex index = PerformanceIndex.forClient(client, converter, period, errors);
double[] accumulated = index.getAccumulatedPercentage();
for (int ii = 0; ii < accumulated.length; ii++)
assertThat(accumulated[ii], IsCloseTo.closeTo(0d, PRECISION));
assertThat(errors.size(), is(1));
assertThat(errors.get(0).getMessage(),
startsWith(Messages.MsgDeltaWithoutAssets.substring(0,
Messages.MsgDeltaWithoutAssets.indexOf('{'))));
}
@Test
public void testFirstDataPointWhenInsideReportingInterval()
{
Client client = createClient();
ReportingPeriod.FromXtoY period = new ReportingPeriod.FromXtoY(LocalDate.of(2011, Month.DECEMBER, 20), //
LocalDate.of(2012, Month.JANUARY, 8));
CurrencyConverter converter = new TestCurrencyConverter();
ClientIndex index = PerformanceIndex.forClient(client, converter, period, new ArrayList<Exception>());
assertThat(index.getFirstDataPoint().get(), is(LocalDate.of(2011, 12, 31)));
assertThat(index.getFirstDataPoint().get(), not(period.toInterval().getStart()));
}
@Test
public void testThatPerformanceOfAnInvestmentIntoAnIndexIsIdenticalToIndex()
{
LocalDate startDate = LocalDate.of(2012, 1, 1);
LocalDate endDate = LocalDate.of(2012, 4, 29); // a weekend
long startPrice = Values.Quote.factorize(100);
Client client = new Client();
Security security = new SecurityBuilder() //
.generatePrices(startPrice, startDate, endDate) //
.addTo(client);
PortfolioBuilder portfolio = new PortfolioBuilder(new Account());
// add some buy transactions
LocalDate date = startDate;
while (date.isBefore(endDate))
{
long p = security.getSecurityPrice(date).getValue();
portfolio.inbound_delivery(security, date, Values.Share.factorize(100), p);
date = date.plusDays(20);
}
portfolio.addTo(client);
ReportingPeriod.FromXtoY period = new ReportingPeriod.FromXtoY(startDate, endDate);
List<Exception> warnings = new ArrayList<Exception>();
CurrencyConverter converter = new TestCurrencyConverter();
ClientIndex index = PerformanceIndex.forClient(client, converter, period, warnings);
assertTrue(warnings.isEmpty());
double[] accumulated = index.getAccumulatedPercentage();
long lastPrice = security.getSecurityPrice(endDate).getValue();
assertThat((double) (lastPrice - startPrice) / (double) startPrice,
IsCloseTo.closeTo(accumulated[accumulated.length - 1], PRECISION));
PerformanceIndex benchmark = PerformanceIndex.forSecurity(index, security);
assertThat(benchmark.getFinalAccumulatedPercentage(), is(index.getFinalAccumulatedPercentage()));
}
@Test
public void testThatPerformanceOfInvestmentAndIndexIsIdendicalWhenInForeignCurrency()
{
LocalDate startDate = LocalDate.of(2015, 1, 1);
LocalDate endDate = LocalDate.of(2015, 8, 1);
long startPrice = Values.Quote.factorize(100);
Client client = new Client();
Security security = new SecurityBuilder("USD") //
.generatePrices(startPrice, startDate, endDate) //
.addTo(client);
new PortfolioBuilder() //
.inbound_delivery(security, "2014-01-01", Values.Share.factorize(100), 100) //
.addTo(client);
ReportingPeriod.FromXtoY period = new ReportingPeriod.FromXtoY(startDate, endDate);
List<Exception> warnings = new ArrayList<Exception>();
CurrencyConverter converter = new TestCurrencyConverter().with(CurrencyUnit.EUR);
ClientIndex index = PerformanceIndex.forClient(client, converter, period, warnings);
assertTrue(warnings.isEmpty());
PerformanceIndex benchmark = PerformanceIndex.forSecurity(index, security);
assertTrue(warnings.isEmpty());
assertThat(benchmark.getFinalAccumulatedPercentage(), is(index.getFinalAccumulatedPercentage()));
PerformanceIndex investment = PerformanceIndex.forInvestment(client, converter, security, period, warnings);
assertTrue(warnings.isEmpty());
assertThat(investment.getFinalAccumulatedPercentage(), is(index.getFinalAccumulatedPercentage()));
}
@Test
public void testExcelSampleAggregatedWeekly()
{
// first day of week is locale dependent
Locale locale = Locale.getDefault();
Locale.setDefault(Locale.GERMAN);
try
{
Client client = createClient();
ReportingPeriod.FromXtoY reportInterval = new ReportingPeriod.FromXtoY( //
LocalDate.of(2011, Month.DECEMBER, 31), LocalDate.of(2012, Month.JANUARY, 8));
CurrencyConverter converter = new TestCurrencyConverter();
PerformanceIndex index = PerformanceIndex.forClient(client, converter, reportInterval,
new ArrayList<Exception>());
index = Aggregation.aggregate(index, Aggregation.Period.WEEKLY);
assertNotNull(index);
double[] delta = index.getDeltaPercentage();
assertThat(delta.length, is(2));
assertThat(delta[0], IsCloseTo.closeTo(0.023d, PRECISION));
assertThat(delta[1], IsCloseTo.closeTo(-0.0713587d, PRECISION));
double[] accumulated = index.getAccumulatedPercentage();
assertThat(accumulated[0], IsCloseTo.closeTo(0.023d, PRECISION));
assertThat(accumulated[1], IsCloseTo.closeTo(-0.05d, PRECISION));
}
finally
{
Locale.setDefault(locale);
}
}
@Test
public void testThatDepositsOnTheLastDayArePerformanceNeutral()
{
Client client = new Client();
new AccountBuilder() //
.deposit_("2012-01-01", 10000) //
.interest("2012-01-02", 1000) //
.deposit_("2012-01-10", 10000) //
.addTo(client);
ReportingPeriod.FromXtoY reportInterval = new ReportingPeriod.FromXtoY(LocalDate.of(2012, Month.JANUARY, 1), //
LocalDate.of(2012, Month.JANUARY, 10));
CurrencyConverter converter = new TestCurrencyConverter();
PerformanceIndex index = PerformanceIndex.forClient(client, converter, reportInterval,
new ArrayList<Exception>());
double[] accumulated = index.getAccumulatedPercentage();
assertThat(accumulated[accumulated.length - 2], IsCloseTo.closeTo(0.1d, PRECISION));
assertThat(accumulated[accumulated.length - 1], IsCloseTo.closeTo(0.1d, PRECISION));
}
@Test
public void testChangesOnFirstDayOfInvestment()
{
Client client = new Client();
new AccountBuilder() //
.deposit_("2012-01-01", 10000) //
.interest("2012-01-02", 1000) //
.addTo(client);
ReportingPeriod.FromXtoY reportInterval = new ReportingPeriod.FromXtoY(LocalDate.of(2012, Month.JANUARY, 1), //
LocalDate.of(2012, Month.JANUARY, 10));
CurrencyConverter converter = new TestCurrencyConverter();
PerformanceIndex index = PerformanceIndex.forClient(client, converter, reportInterval,
new ArrayList<Exception>());
double[] accumulated = index.getAccumulatedPercentage();
assertThat(accumulated[accumulated.length - 1], IsCloseTo.closeTo(0.1d, PRECISION));
}
}