com.github.triceo.robozonky.strategy.simple.SimpleStrategyService Maven / Gradle / Ivy
/*
* Copyright 2017 Lukáš Petrovický
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.github.triceo.robozonky.strategy.simple;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.github.triceo.robozonky.api.remote.enums.Rating;
import com.github.triceo.robozonky.api.strategies.InvestmentStrategy;
import com.github.triceo.robozonky.api.strategies.LoanDescriptor;
import com.github.triceo.robozonky.api.strategies.PortfolioOverview;
import com.github.triceo.robozonky.api.strategies.PurchaseStrategy;
import com.github.triceo.robozonky.api.strategies.RecommendedLoan;
import com.github.triceo.robozonky.api.strategies.SellStrategy;
import com.github.triceo.robozonky.api.strategies.StrategyService;
import com.github.triceo.robozonky.strategy.natural.DefaultInvestmentShare;
import com.github.triceo.robozonky.strategy.natural.DefaultPortfolio;
import com.github.triceo.robozonky.strategy.natural.DefaultValues;
import com.github.triceo.robozonky.strategy.natural.InvestmentSize;
import com.github.triceo.robozonky.strategy.natural.LoanAmountCondition;
import com.github.triceo.robozonky.strategy.natural.LoanRatingEnumeratedCondition;
import com.github.triceo.robozonky.strategy.natural.LoanTermCondition;
import com.github.triceo.robozonky.strategy.natural.MarketplaceFilter;
import com.github.triceo.robozonky.strategy.natural.NaturalLanguageInvestmentStrategy;
import com.github.triceo.robozonky.strategy.natural.ParsedStrategy;
import com.github.triceo.robozonky.strategy.natural.PortfolioShare;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Deprecated
public class SimpleStrategyService implements StrategyService {
private static final Logger LOGGER = LoggerFactory.getLogger(SimpleStrategyService.class);
private static int getMinimumBalance(final ImmutableConfiguration config) {
return StrategyFileProperty.MINIMUM_BALANCE.getValue(config::getInt)
.orElseThrow(() -> new IllegalStateException("Minimum balance is missing."));
}
private static int getTargetPortfolioSize(final ImmutableConfiguration config) {
return StrategyFileProperty.MAXIMUM_INVESTMENT.getValue(config::getInt)
.orElseThrow(() -> new IllegalStateException("Maximum investment is missing."));
}
private static int getInvestmentShare(final ImmutableConfiguration config) {
return Stream.of(Rating.values())
.mapToInt(r ->
SimpleStrategyService.getShare(config,
StrategyFileProperty.MAXIMUM_LOAN_SHARE, r))
.distinct()
.min() // natural strategy only allows for one such value; let's pick the smallest available for safety
.orElseThrow(() -> new IllegalStateException("Maximum loan share is missing"));
}
private static int getShare(final ImmutableConfiguration config, final StrategyFileProperty prop,
final Rating rating) {
final BigDecimal share = prop.getValue(rating, config::getBigDecimal);
return share.multiply(BigDecimal.valueOf(100)).intValue();
}
private static InvestmentStrategy parseOrThrow(final InputStream strategy) {
final ImmutableConfiguration config = ImmutableConfiguration.from(strategy);
// set default values
final DefaultValues d = new DefaultValues(DefaultPortfolio.EMPTY);
d.setMinimumBalance(SimpleStrategyService.getMinimumBalance(config));
d.setTargetPortfolioSize(SimpleStrategyService.getTargetPortfolioSize(config));
d.setInvestmentShare(new DefaultInvestmentShare(SimpleStrategyService.getInvestmentShare(config)));
final LoanRatingEnumeratedCondition c = new LoanRatingEnumeratedCondition();
Stream.of(Rating.values())
.filter(r -> StrategyFileProperty.REQUIRE_CONFIRMATION.getValue(r, config::getBoolean))
.forEach(c::add);
d.setConfirmationCondition(c);
// assemble strategy
final Collection investmentSizes = Stream.of(Rating.values())
.map(r -> {
final int min = StrategyFileProperty.MINIMUM_LOAN_AMOUNT.getValue(r, config::getInt);
final int max = StrategyFileProperty.MAXIMUM_LOAN_AMOUNT.getValue(r, config::getInt);
return new InvestmentSize(r, min, max);
}).collect(Collectors.toList());
final Collection portfolio = Stream.of(Rating.values())
.map(r -> {
final int min =
SimpleStrategyService.getShare(config, StrategyFileProperty.TARGET_SHARE, r);
final int max =
SimpleStrategyService.getShare(config, StrategyFileProperty.MAXIMUM_SHARE, r);
return new PortfolioShare(r, min, max);
}).collect(Collectors.toList());
final Collection filters = new ArrayList<>();
Stream.of(Rating.values()) // filter loans with less than minimum term
.map(r -> {
final int min = StrategyFileProperty.MINIMUM_TERM.getValue(r, config::getInt);
return new LoanTermCondition(0, Math.max(0, min - 1)); // <0; 0> is not a problem
}).map(condition -> {
final MarketplaceFilter f = new MarketplaceFilter();
f.ignoreWhen(Collections.singleton(condition));
return f;
}).forEach(filters::add);
Stream.of(Rating.values()) // filter loans with more than maximum term
.map(r -> {
final int max = StrategyFileProperty.MAXIMUM_TERM.getValue(r, config::getInt);
return new LoanTermCondition(max + 1);
}).map(condition -> {
final MarketplaceFilter f = new MarketplaceFilter();
f.ignoreWhen(Collections.singleton(condition));
return f;
}).forEach(filters::add);
Stream.of(Rating.values()) // filter loans with ask for less than minimum
.map(r -> {
final int min = StrategyFileProperty.MINIMUM_ASK.getValue(r, config::getInt);
return new LoanAmountCondition(0, Math.max(0, min - 1)); // <0; 0> is not a problem
}).map(condition -> {
final MarketplaceFilter f = new MarketplaceFilter();
f.ignoreWhen(Collections.singleton(condition));
return f;
}).forEach(filters::add);
Stream.of(Rating.values()) // filter loans with ask more than maximum
.map(r -> {
final int max = StrategyFileProperty.MAXIMUM_ASK.getValue(r, config::getInt);
return new LoanAmountCondition(max + 1);
}).map(condition -> {
final MarketplaceFilter f = new MarketplaceFilter();
f.ignoreWhen(Collections.singleton(condition));
return f;
}).forEach(filters::add);
final Map> resultingFilters = new HashMap<>();
resultingFilters.put(true, filters);
resultingFilters.put(false, Collections.emptyList());
final ParsedStrategy p = new ParsedStrategy(d, portfolio, investmentSizes, resultingFilters,
Collections.emptyList());
final InvestmentStrategy result = new NaturalLanguageInvestmentStrategy(p);
return new SimpleStrategyService.ExclusivelyPrimaryMarketplaceInvestmentStrategy(result);
}
@Override
public Optional toInvest(final InputStream strategy) {
try {
final InvestmentStrategy s = SimpleStrategyService.parseOrThrow(strategy);
SimpleStrategyService.LOGGER.warn("You are using a deprecated strategy format!");
return Optional.of(s);
} catch (final Exception ex) {
SimpleStrategyService.LOGGER.debug("Failed converting to a natural strategy. May be OK.", ex);
return Optional.empty();
}
}
@Override
public Optional toSell(final InputStream strategy) {
return Optional.empty(); // not supported
}
@Override
public Optional toPurchase(final InputStream strategy) {
return Optional.empty(); // not supported
}
private static final class ExclusivelyPrimaryMarketplaceInvestmentStrategy implements InvestmentStrategy {
private final InvestmentStrategy child;
public ExclusivelyPrimaryMarketplaceInvestmentStrategy(final InvestmentStrategy child) {
this.child = child;
}
@Override
public Stream recommend(final Collection availableLoans,
final PortfolioOverview portfolio) {
return child.recommend(availableLoans, portfolio);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy