@Override public BigDecimal getMinimumBalance(final Tenant tenant) { return BigDecimal.valueOf(tenant.getRestrictions().getMinimumInvestmentAmount()); }
protected static Zonky harmlessZonky(final int availableBalance) { final Zonky zonky = mock(Zonky.class); final BigDecimal balance = BigDecimal.valueOf(availableBalance); when(zonky.getWallet()).thenReturn(new Wallet(1, 2, balance, balance)); when(zonky.getRestrictions()).thenReturn(new Restrictions(true)); when(zonky.getBlockedAmounts()).thenAnswer(i -> Stream.empty()); when(zonky.getStatistics()).thenReturn(Statistics.empty()); when(zonky.getDevelopments(anyInt())).thenAnswer(i -> Stream.empty()); return zonky; }
@Override public boolean isEnabled(final Tenant tenant) { return !tenant.getRestrictions().isCannotAccessSmp(); }
@Test void minimumOverRemaining() { final Restrictions restrictions = new Restrictions(); final int minimumInvestment = restrictions.getMinimumInvestmentAmount(); final Loan l = mockLoan(minimumInvestment - 1); final ParsedStrategy s = mock(ParsedStrategy.class); when(s.getMinimumInvestmentSizeInCzk(eq(l.getRating()))).thenReturn(minimumInvestment); when(s.getMaximumInvestmentSizeInCzk(eq(l.getRating()))) .thenReturn(minimumInvestment * 2); when(s.getMaximumInvestmentShareInPercent()).thenReturn(100); final BiFunction<MarketplaceLoan, Integer, Integer> r = new InvestmentSizeRecommender(s, restrictions); assertThat(r.apply(l, minimumInvestment * 2)).isEqualTo(0); }
private int[] getInvestmentBounds(final ParsedStrategy strategy, final MarketplaceLoan loan) { final Rating rating = loan.getRating(); final int absoluteMinimum = Math.max(strategy.getMinimumInvestmentSizeInCzk(rating), restrictions.getMinimumInvestmentAmount()); final int minimumRecommendation = roundToNearestIncrement(absoluteMinimum, restrictions.getInvestmentStep()); final int maximumUserRecommendation = roundToNearestIncrement(strategy.getMaximumInvestmentSizeInCzk(rating), restrictions.getInvestmentStep()); final int maximumInvestmentAmount = restrictions.getMaximumInvestmentAmount(); if (maximumUserRecommendation > maximumInvestmentAmount) { Decisions.report(l -> l.info("Maximum investment amount reduced to {} by Zonky.", maximumInvestmentAmount)); } final int maximumRecommendation = Math.min(maximumUserRecommendation, maximumInvestmentAmount); final int loanId = loan.getId(); Decisions.report(l -> l.trace("Strategy gives investment range for loan #{} of <{}; {}> CZK.", loanId, minimumRecommendation, maximumRecommendation)); final int minimumInvestmentByShare = getPercentage(loan.getAmount(), strategy.getMinimumInvestmentShareInPercent()); final int minimumInvestment = Math.max(minimumInvestmentByShare, strategy.getMinimumInvestmentSizeInCzk(loan.getRating())); final int maximumInvestmentByShare = getPercentage(loan.getAmount(), strategy.getMaximumInvestmentShareInPercent()); final int maximumInvestment = Math.min(maximumInvestmentByShare, strategy.getMaximumInvestmentSizeInCzk(loan.getRating())); // minimums are guaranteed to be <= maximums due to the contract of strategy implementation return new int[]{minimumInvestment, maximumInvestment}; }
@Override public boolean isEnabled(final Tenant tenant) { return !tenant.getRestrictions().isCannotInvest(); }
@Override public Integer apply(final MarketplaceLoan loan, final Integer balance) { final int id = loan.getId(); final int[] recommended = getInvestmentBounds(strategy, loan); final int minimumRecommendation = recommended[0]; final int maximumRecommendation = recommended[1]; Decisions.report(l -> l.debug("Recommended investment range for loan #{} is <{}; {}> CZK.", id, minimumRecommendation, maximumRecommendation)); // round to nearest lower increment final int loanRemaining = loan.getNonReservedRemainingInvestment(); if (minimumRecommendation > balance) { Decisions.report(l -> l.debug("Not recommending loan #{} due to minimum over balance.", id)); return 0; } else if (minimumRecommendation > loanRemaining) { Decisions.report(l -> l.debug("Not recommending loan #{} due to minimum over remaining.", id)); return 0; } final int recommendedAmount = Math.min(balance, Math.min(maximumRecommendation, loanRemaining)); final int r = roundToNearestIncrement(recommendedAmount, restrictions.getInvestmentStep()); if (r < minimumRecommendation) { Decisions.report(l -> l.debug("Not recommending loan #{} due to recommendation below minimum.", id)); return 0; } else { Decisions.report(l -> l.debug("Final recommendation for loan #{} is {} CZK.", id, r)); return r; } } }
@Test void nothingMoreToInvest() { final Restrictions restrictions = new Restrictions(); final ParsedStrategy s = getStrategy(); final Loan l = mockLoan(restrictions.getMinimumInvestmentAmount() - 1); final InvestmentSizeRecommender r = new InvestmentSizeRecommender(s, restrictions); // with unlimited balance, make maximum possible recommendation final int actualInvestment = r.apply(l, Integer.MAX_VALUE); assertThat(actualInvestment).isEqualTo(0); }
private int[] getInvestmentBounds(final ParsedStrategy strategy, final MarketplaceLoan loan) { final Rating rating = loan.getRating(); final int absoluteMinimum = Math.max(strategy.getMinimumInvestmentSizeInCzk(rating), restrictions.getMinimumInvestmentAmount()); final int minimumRecommendation = roundToNearestIncrement(absoluteMinimum, restrictions.getInvestmentStep()); final int maximumUserRecommendation = roundToNearestIncrement(strategy.getMaximumInvestmentSizeInCzk(rating), restrictions.getInvestmentStep()); final int maximumInvestmentAmount = restrictions.getMaximumInvestmentAmount(); if (maximumUserRecommendation > maximumInvestmentAmount) { Decisions.report(l -> l.info("Maximum investment amount reduced to {} by Zonky.", maximumInvestmentAmount)); } final int maximumRecommendation = Math.min(maximumUserRecommendation, maximumInvestmentAmount); final int loanId = loan.getId(); Decisions.report(l -> l.trace("Strategy gives investment range for loan #{} of <{}; {}> CZK.", loanId, minimumRecommendation, maximumRecommendation)); final int minimumInvestmentByShare = getPercentage(loan.getAmount(), strategy.getMinimumInvestmentShareInPercent()); final int minimumInvestment = Math.max(minimumInvestmentByShare, strategy.getMinimumInvestmentSizeInCzk(loan.getRating())); final int maximumInvestmentByShare = getPercentage(loan.getAmount(), strategy.getMaximumInvestmentShareInPercent()); final int maximumInvestment = Math.min(maximumInvestmentByShare, strategy.getMaximumInvestmentSizeInCzk(loan.getRating())); // minimums are guaranteed to be <= maximums due to the contract of strategy implementation return new int[]{minimumInvestment, maximumInvestment}; }
@Override public boolean isEnabled(final Tenant tenant) { return !tenant.getRestrictions().isCannotInvest(); }
@Override public Integer apply(final MarketplaceLoan loan, final Integer balance) { final int id = loan.getId(); final int[] recommended = getInvestmentBounds(strategy, loan); final int minimumRecommendation = recommended[0]; final int maximumRecommendation = recommended[1]; Decisions.report(l -> l.debug("Recommended investment range for loan #{} is <{}; {}> CZK.", id, minimumRecommendation, maximumRecommendation)); // round to nearest lower increment final int loanRemaining = loan.getNonReservedRemainingInvestment(); if (minimumRecommendation > balance) { Decisions.report(l -> l.debug("Not recommending loan #{} due to minimum over balance.", id)); return 0; } else if (minimumRecommendation > loanRemaining) { Decisions.report(l -> l.debug("Not recommending loan #{} due to minimum over remaining.", id)); return 0; } final int recommendedAmount = Math.min(balance, Math.min(maximumRecommendation, loanRemaining)); final int r = roundToNearestIncrement(recommendedAmount, restrictions.getInvestmentStep()); if (r < minimumRecommendation) { Decisions.report(l -> l.debug("Not recommending loan #{} due to recommendation below minimum.", id)); return 0; } else { Decisions.report(l -> l.debug("Final recommendation for loan #{} is {} CZK.", id, r)); return r; } } }
@Test void recommendationRoundedUnderMinimum() { final Restrictions restrictions = new Restrictions(); final int minimumInvestment = restrictions.getMinimumInvestmentAmount(); final Loan l = mockLoan(minimumInvestment - 1); final ParsedStrategy s = mock(ParsedStrategy.class); // next line will cause the recommendation to be rounded to 800, which will be below the minimum investment when(s.getMinimumInvestmentSizeInCzk(eq(l.getRating()))).thenReturn( minimumInvestment - 1); when(s.getMaximumInvestmentSizeInCzk(eq(l.getRating()))) .thenReturn(minimumInvestment); when(s.getMaximumInvestmentShareInPercent()).thenReturn(100); final BiFunction<MarketplaceLoan, Integer, Integer> r = new InvestmentSizeRecommender(s, restrictions); assertThat(r.apply(l, minimumInvestment * 2)).isEqualTo(0); } }
@Override public BigDecimal getMinimumBalance(final Tenant tenant) { return BigDecimal.valueOf(tenant.getRestrictions().getMinimumInvestmentAmount()); }
protected static Zonky harmlessZonky(final int availableBalance) { final Zonky zonky = mock(Zonky.class); final BigDecimal balance = BigDecimal.valueOf(availableBalance); when(zonky.getWallet()).thenReturn(new Wallet(1, 2, balance, balance)); when(zonky.getRestrictions()).thenReturn(new Restrictions(true)); when(zonky.getBlockedAmounts()).thenAnswer(i -> Stream.empty()); when(zonky.getStatistics()).thenReturn(Statistics.empty()); when(zonky.getDevelopments(anyInt())).thenAnswer(i -> Stream.empty()); return zonky; }
@Override public boolean isEnabled(final Tenant tenant) { return !tenant.getRestrictions().isCannotAccessSmp(); }
public static Collection<Investment> invest(final Investor investor, final PowerTenant tenant, final Collection<LoanDescriptor> loans, final InvestmentStrategy strategy) { final InvestingSession s = new InvestingSession(loans, investor, tenant); final PortfolioOverview portfolioOverview = tenant.getPortfolio().getOverview(); final long balance = portfolioOverview.getCzkAvailable().longValue(); s.tenant.fire(executionStartedLazy(() -> executionStarted(loans, portfolioOverview))); if (balance >= tenant.getRestrictions().getMinimumInvestmentAmount() && !s.getAvailable().isEmpty()) { s.invest(strategy); } final Collection<Investment> result = s.getResult(); // make sure we get fresh portfolio reference here s.tenant.fire(executionCompletedLazy(() -> executionCompleted(result, tenant.getPortfolio().getOverview()))); return Collections.unmodifiableCollection(result); }
@Test void unacceptablePortfolioDueToLowBalance() { final ParsedStrategy p = new ParsedStrategy(DefaultPortfolio.EMPTY); final PurchaseStrategy s = new NaturalLanguagePurchaseStrategy(p); final PortfolioOverview portfolio = mock(PortfolioOverview.class); when(portfolio.getCzkAvailable()).thenReturn(BigDecimal.ZERO); when(portfolio.getCzkInvested()).thenReturn(BigDecimal.ZERO); final Stream<RecommendedParticipation> result = s.recommend(Collections.singletonList(mockDescriptor()), portfolio, new Restrictions()); assertThat(result).isEmpty(); }
public static Collection<Investment> invest(final Investor investor, final PowerTenant tenant, final Collection<LoanDescriptor> loans, final InvestmentStrategy strategy) { final InvestingSession s = new InvestingSession(loans, investor, tenant); final PortfolioOverview portfolioOverview = tenant.getPortfolio().getOverview(); final long balance = portfolioOverview.getCzkAvailable().longValue(); s.tenant.fire(executionStartedLazy(() -> executionStarted(loans, portfolioOverview))); if (balance >= tenant.getRestrictions().getMinimumInvestmentAmount() && !s.getAvailable().isEmpty()) { s.invest(strategy); } final Collection<Investment> result = s.getResult(); // make sure we get fresh portfolio reference here s.tenant.fire(executionCompletedLazy(() -> executionCompleted(result, tenant.getPortfolio().getOverview()))); return Collections.unmodifiableCollection(result); }
@Test void unacceptablePortfolioDueToLowBalance() { final ParsedStrategy p = new ParsedStrategy(DefaultPortfolio.EMPTY); final InvestmentStrategy s = new NaturalLanguageInvestmentStrategy(p); final PortfolioOverview portfolio = mock(PortfolioOverview.class); when(portfolio.getCzkAvailable()).thenReturn(BigDecimal.valueOf(p.getMinimumBalance() - 1)); final Stream<RecommendedLoan> result = s.recommend(Collections.singletonList(new LoanDescriptor(mockLoan(2))), portfolio, new Restrictions()); assertThat(result).isEmpty(); }