Skip to content
This repository has been archived by the owner on Oct 31, 2021. It is now read-only.

Commit

Permalink
Understand loan interest rates properly (fixes #401)
Browse files Browse the repository at this point in the history
Loan interests from Zonky come as percentages, while strategy expects
them to be a hundred times that.
  • Loading branch information
triceo committed Mar 12, 2019
1 parent 4b970de commit ebf35f9
Show file tree
Hide file tree
Showing 8 changed files with 63 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,14 @@

package com.github.robozonky.strategy.natural;

import java.math.BigDecimal;
import java.util.Objects;

import com.github.robozonky.api.remote.entities.Participation;
import com.github.robozonky.api.remote.entities.sanitized.Investment;
import com.github.robozonky.api.remote.entities.sanitized.Loan;
import com.github.robozonky.api.strategies.Descriptor;
import com.github.robozonky.internal.util.BigDecimalCalculator;

abstract class AbstractWrapper<T extends Descriptor<?, ?, ?>> implements Wrapper<T> {

Expand All @@ -28,6 +33,17 @@ protected AbstractWrapper(final T original) {
this.original = original;
}

/**
* Rates are coming from the API ({@link Investment}, {@link Loan}, {@link Participation}) in the form of a share
* (1 % becomes "0.01"), while the strategy excepts them in the format of a percentage (1 & becomes "1"). This
* method converts from the former to the latter.
* @param bigDecimal
* @return
*/
protected static BigDecimal adjustRateForStrategy(final BigDecimal bigDecimal) {
return BigDecimalCalculator.times(bigDecimal, 100);
}

@Override
public T getOriginal() {
return original;
Expand All @@ -50,5 +66,4 @@ public boolean equals(final Object o) {
public int hashCode() {
return Objects.hash(original);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@ public MainIncomeType getMainIncomeType() {

@Override
public BigDecimal getInterestRate() {
return investment.getInterestRate();
return adjustRateForStrategy(investment.getInterestRate());
}

@Override
public BigDecimal getRevenueRate() {
return investment.getRevenueRate();
return adjustRateForStrategy(investment.getRevenueRate());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@ public MainIncomeType getMainIncomeType() {

@Override
public BigDecimal getInterestRate() {
return loan.getInterestRate();
return adjustRateForStrategy(loan.getInterestRate());
}

@Override
public BigDecimal getRevenueRate() {
return loan.getRevenueRate();
return adjustRateForStrategy(loan.getRevenueRate());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@ public MainIncomeType getMainIncomeType() {

@Override
public BigDecimal getInterestRate() {
return participation.getInterestRate();
return adjustRateForStrategy(participation.getInterestRate());
}

@Override
public BigDecimal getRevenueRate() { // TODO check if Participation has this field too
return getLoan().getRevenueRate();
public BigDecimal getRevenueRate() {
return adjustRateForStrategy(getLoan().getRevenueRate());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import com.github.robozonky.api.strategies.LoanDescriptor;
import org.junit.jupiter.api.Test;

import static com.github.robozonky.internal.util.BigDecimalCalculator.times;
import static org.assertj.core.api.SoftAssertions.assertSoftly;

class WrapperTest {
Expand All @@ -34,6 +35,7 @@ void fromInvestment() {
.setId(1)
.setAmount(100_000)
.setRevenueRate(BigDecimal.ONE)
.setInterestRate(BigDecimal.TEN)
.setAnnuity(BigDecimal.ONE)
.build();
final int invested = 200;
Expand All @@ -44,8 +46,8 @@ void fromInvestment() {
softly.assertThat(w.getRegion()).isEqualTo(loan.getRegion());
softly.assertThat(w.getRating()).isEqualTo(loan.getRating());
softly.assertThat(w.getOriginalAmount()).isEqualTo(loan.getAmount());
softly.assertThat(w.getInterestRate()).isEqualTo(loan.getInterestRate());
softly.assertThat(w.getRevenueRate()).isEqualTo(investment.getRevenueRate());
softly.assertThat(w.getInterestRate()).isEqualTo(times(loan.getInterestRate(), 100));
softly.assertThat(w.getRevenueRate()).isEqualTo(times(investment.getRevenueRate(), 100));
softly.assertThat(w.getOriginalAnnuity()).isEqualTo(loan.getAnnuity().intValue());
softly.assertThat(w.getRemainingTermInMonths()).isEqualTo(investment.getRemainingMonths());
softly.assertThat(w.getRemainingPrincipal()).isEqualTo(BigDecimal.valueOf(invested));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import com.github.robozonky.strategy.natural.Wrapper;
import org.junit.jupiter.api.Test;

import static com.github.robozonky.internal.util.BigDecimalCalculator.times;
import static org.assertj.core.api.SoftAssertions.assertSoftly;

class InvestmentWrapperTest {
Expand All @@ -52,7 +53,7 @@ void values() {
final Wrapper<InvestmentDescriptor> w = Wrapper.wrap(original);
assertSoftly(softly -> {
softly.assertThat(w.isInsuranceActive()).isEqualTo(INVESTMENT.isInsuranceActive());
softly.assertThat(w.getInterestRate()).isEqualTo(INVESTMENT.getInterestRate());
softly.assertThat(w.getInterestRate()).isEqualTo(times(INVESTMENT.getInterestRate(), 100));
softly.assertThat(w.getRegion()).isEqualTo(LOAN.getRegion());
softly.assertThat(w.getRating()).isEqualTo(INVESTMENT.getRating());
softly.assertThat(w.getMainIncomeType()).isEqualTo(LOAN.getMainIncomeType());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import com.github.robozonky.strategy.natural.Wrapper;
import org.junit.jupiter.api.Test;

import static com.github.robozonky.internal.util.BigDecimalCalculator.times;
import static org.assertj.core.api.SoftAssertions.assertSoftly;

class LoanWrapperTest {
Expand All @@ -49,7 +50,7 @@ void values() {
final Wrapper<LoanDescriptor> w = Wrapper.wrap(original);
assertSoftly(softly -> {
softly.assertThat(w.isInsuranceActive()).isEqualTo(l.isInsuranceActive());
softly.assertThat(w.getInterestRate()).isEqualTo(l.getInterestRate());
softly.assertThat(w.getInterestRate()).isEqualTo(times(l.getInterestRate(), 100));
softly.assertThat(w.getRegion()).isEqualTo(l.getRegion());
softly.assertThat(w.getRating()).isEqualTo(l.getRating());
softly.assertThat(w.getMainIncomeType()).isEqualTo(l.getMainIncomeType());
Expand Down Expand Up @@ -87,5 +88,4 @@ void equality() {
softly.assertThat(w).isNotEqualTo(null);
});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package com.github.robozonky.strategy.natural.conditions;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.UUID;

import com.github.robozonky.api.remote.entities.Participation;
Expand All @@ -26,9 +28,12 @@
import com.github.robozonky.api.remote.enums.Rating;
import com.github.robozonky.api.remote.enums.Region;
import com.github.robozonky.api.strategies.ParticipationDescriptor;
import com.github.robozonky.internal.util.BigDecimalCalculator;
import com.github.robozonky.strategy.natural.Wrapper;
import org.junit.jupiter.api.Test;

import static com.github.robozonky.internal.util.BigDecimalCalculator.times;
import static org.assertj.core.api.Assertions.*;
import static org.assertj.core.api.SoftAssertions.assertSoftly;
import static org.mockito.Mockito.*;

Expand All @@ -49,6 +54,7 @@ class ParticipationWrapperTest {

private static Participation mockParticipation(final Loan loan) {
final Participation p = mock(Participation.class);
when(p.getInterestRate()).thenReturn(BigDecimal.ONE);
when(p.getPurpose()).thenReturn(loan.getPurpose());
when(p.getRating()).thenReturn(loan.getRating());
when(p.getIncomeType()).thenReturn(loan.getMainIncomeType());
Expand All @@ -61,7 +67,7 @@ void values() {
final Wrapper<ParticipationDescriptor> w = Wrapper.wrap(original);
assertSoftly(softly -> {
softly.assertThat(w.isInsuranceActive()).isEqualTo(PARTICIPATION.isInsuranceActive());
softly.assertThat(w.getInterestRate()).isEqualTo(PARTICIPATION.getInterestRate());
softly.assertThat(w.getInterestRate()).isEqualTo(times(PARTICIPATION.getInterestRate(), 100));
softly.assertThat(w.getRegion()).isEqualTo(LOAN.getRegion());
softly.assertThat(w.getRating()).isEqualTo(PARTICIPATION.getRating());
softly.assertThat(w.getMainIncomeType()).isEqualTo(LOAN.getMainIncomeType());
Expand All @@ -87,4 +93,29 @@ void equality() {
softly.assertThat(w).isNotEqualTo(null);
});
}

@Test
void elseConditionWorks() {
final AbstractEnumeratedCondition<Region> c = new BorrowerRegionCondition();
c.add(Region.USTECKY);
c.add(Region.MORAVSKOSLEZSKY);
c.add(Region.KARLOVARSKY);
final MarketplaceFilter f = new MarketplaceFilter();
f.when(Collections.singleton(c));
final AbstractEnumeratedCondition<MainIncomeType> c2 = new BorrowerIncomeCondition();
c2.add(MainIncomeType.EMPLOYMENT);
final AbstractRangeCondition c3 =
new LoanInterestRateCondition(BigDecimal.ZERO, BigDecimalCalculator.lessThan(new BigDecimal("16.0")));
f.butNotWhen(Arrays.asList(c2, c3));
final Loan l = Loan.custom()
.setRegion(Region.USTECKY)
.build();
final Participation p = mock(Participation.class);
when(p.getIncomeType()).thenReturn(MainIncomeType.EMPLOYMENT);
when(p.getInterestRate()).thenReturn(new BigDecimal("0.15"));
final ParticipationDescriptor pd = new ParticipationDescriptor(p, () -> l);
final Wrapper<ParticipationDescriptor> w = Wrapper.wrap(pd);
assertThat(f).rejects(w);
}

}

0 comments on commit ebf35f9

Please sign in to comment.