<commit_before>/*
 * Copyright 2016 <NAME>
 *
 * 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.File;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.github.triceo.robozonky.remote.Rating;
import com.github.triceo.robozonky.strategy.InvestmentStrategy;
import com.github.triceo.robozonky.strategy.InvestmentStrategyParseException;
import com.github.triceo.robozonky.strategy.InvestmentStrategyService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SimpleInvestmentStrategyService implements InvestmentStrategyService {

    private static final Logger LOGGER = LoggerFactory.getLogger(SimpleInvestmentStrategyService.class);
    // below are strings that identify different variables in the strategy file

    private static ImmutableConfiguration getConfig(final File strategyFile) throws InvestmentStrategyParseException {
        return ImmutableConfiguration.from(strategyFile);
    }

    static int getMinimumBalance(final ImmutableConfiguration config) {
        final int minimumBalance = StrategyFileProperty.MINIMUM_BALANCE.getValue(config::getInt)
                .orElseThrow(() -> new IllegalStateException("Minimum balance is missing."));
        if (minimumBalance < InvestmentStrategy.MINIMAL_INVESTMENT_ALLOWED) {
            throw new IllegalStateException("Minimum balance is less than "
                    + InvestmentStrategy.MINIMAL_INVESTMENT_ALLOWED + " CZK.");
        }
        return minimumBalance;
    }

    static int getMaximumInvestment(final ImmutableConfiguration config) {
        final int maximumInvestment = StrategyFileProperty.MAXIMUM_INVESTMENT.getValue(config::getInt)
                .orElseThrow(() -> new IllegalStateException("Maximum investment is missing."));
        if (maximumInvestment < InvestmentStrategy.MINIMAL_INVESTMENT_ALLOWED) {
            throw new IllegalStateException("Maximum investment is less than "
                    + InvestmentStrategy.MINIMAL_INVESTMENT_ALLOWED + " CZK.");
        }
        return maximumInvestment;
    }

    private static StrategyPerRating createIndividualStrategy(final Rating r, final BigDecimal targetShare,
                                                              final BigDecimal maxShare, final int minTerm,
                                                              final int maxTerm, final int minAskAmount,
                                                              final int maxAskAmount, final int minLoanAmount,
                                                              final int maxLoanAmount, final BigDecimal minLoanShare,
                                                              final BigDecimal maxLoanShare,
                                                              final boolean preferLongerTerms) {
        SimpleInvestmentStrategyService.LOGGER.debug("Adding strategy for rating '{}'.", r.getCode());
        SimpleInvestmentStrategyService.LOGGER.debug("Range of acceptable share for rating '{}' among total " +
                        "investments is <{}, {}>.", r.getCode(), targetShare, maxShare);
        SimpleInvestmentStrategyService.LOGGER.debug("Range of acceptable loan amounts for rating '{}' is "
                + "<{}, {}> CZK.", r.getCode(), minAskAmount, maxAskAmount < 0 ? "+inf" : maxAskAmount);
        SimpleInvestmentStrategyService.LOGGER.debug("Acceptable range of investment terms for rating '{}' is "
                + "<{}, {}) months.", r.getCode(), minTerm == -1 ? 0 : minTerm, maxTerm < 0 ? "+inf" : maxTerm + 1);
        SimpleInvestmentStrategyService.LOGGER.debug("Acceptable range of investment amount for rating '{}' is "
                + "<{}, {}>.", r.getCode(), minLoanAmount, maxLoanAmount < 0 ? "+inf" : maxLoanAmount);
        SimpleInvestmentStrategyService.LOGGER.debug("Acceptable range of investment share for rating '{}' is "
                + "<{}, {}>.", r.getCode(), minLoanShare, maxLoanShare);
        if (preferLongerTerms) {
            SimpleInvestmentStrategyService.LOGGER.debug("Rating '{}' will prefer longer terms.", r.getCode());
        } else {
            SimpleInvestmentStrategyService.LOGGER.debug("Rating '{}' will prefer shorter terms.", r.getCode());
        }
        return new StrategyPerRating(r, targetShare, maxShare, minTerm, maxTerm, minLoanAmount, maxLoanAmount,
                minLoanShare, maxLoanShare, minAskAmount, maxAskAmount, preferLongerTerms);
    }

    private static StrategyPerRating parseRating(final Rating rating, final ImmutableConfiguration config) {
        final boolean preferLongerTerms = StrategyFileProperty.PREFER_LONGER_TERMS.getValue(rating, config::getBoolean);
        // shares of a rating on the total pie
        final BigDecimal targetShare = StrategyFileProperty.TARGET_SHARE.getValue(rating, config::getBigDecimal);
        if (!Util.isBetweenZeroAndOne(targetShare)) {
            throw new IllegalStateException("Target share for rating " + rating + " outside of range <0, 1>: "
                    + targetShare);
        }
        final BigDecimal maximumShare = StrategyFileProperty.MAXIMUM_SHARE.getValue(rating, config::getBigDecimal);
        if (!Util.isBetweenAAndB(maximumShare, targetShare, BigDecimal.ONE)) {
            throw new IllegalStateException("Maximum share for rating " + rating + " outside of range <" + targetShare
                    + ", 1>: " + maximumShare);
        }
        // terms
        final int minTerm = StrategyFileProperty.MINIMUM_TERM.getValue(rating, config::getInt);
        if (minTerm < -1) {
            throw new IllegalStateException("Minimum acceptable term for rating " + rating + " negative.");
        }
        final int maxTerm = StrategyFileProperty.MAXIMUM_TERM.getValue(rating, config::getInt);
        if (maxTerm < -1) {
            throw new IllegalStateException("Maximum acceptable term for rating " + rating + " negative.");
        } else if (minTerm > maxTerm && maxTerm != -1) {
            throw new IllegalStateException("Maximum acceptable term for rating " + rating + " less than minimum.");
        }
        // loan asks
        final int minAskAmount = StrategyFileProperty.MINIMUM_ASK.getValue(rating, config::getInt);
        if (minAskAmount < -1) {
            throw new IllegalStateException("Minimum acceptable ask amount for rating " + rating + " negative.");
        }
        final int maxAskAmount = StrategyFileProperty.MAXIMUM_ASK.getValue(rating, config::getInt);
        if (maxAskAmount < -1) {
            throw new IllegalStateException("Maximum acceptable ask for rating " + rating + " negative.");
        } else if (minAskAmount > maxAskAmount && maxAskAmount != -1) {
            throw new IllegalStateException("Maximum acceptable ask for rating " + rating + " less than minimum.");
        }
        // investment amounts
        final int minLoanAmount = StrategyFileProperty.MINIMUM_LOAN_AMOUNT.getValue(rating, config::getInt);
        if (minLoanAmount < InvestmentStrategy.MINIMAL_INVESTMENT_ALLOWED) {
            throw new IllegalStateException("Minimum investment amount for rating " + rating
                    + " less than minimum investment allowed: " + minLoanAmount + " CZK.");
        }
        final int maxLoanAmount = StrategyFileProperty.MAXIMUM_LOAN_AMOUNT.getValue(rating, config::getInt);
        if (maxLoanAmount != -1 && maxLoanAmount < minLoanAmount) {
            throw new IllegalStateException("Maximum investment amount for rating " + rating + " less than minimum.");
        }
        final BigDecimal minLoanShare = StrategyFileProperty.MINIMUM_LOAN_SHARE.getValue(rating,
                config::getBigDecimal);
        if (!Util.isBetweenZeroAndOne(minLoanShare)) {
            throw new IllegalStateException("Minimum investment share for rating " + rating
                    + " outside of range <0, 1>: " + targetShare);
        }
        final BigDecimal maxLoanShare = StrategyFileProperty.MAXIMUM_LOAN_SHARE.getValue(rating, config::getBigDecimal);
        if (!Util.isBetweenAAndB(maxLoanShare, minLoanShare, BigDecimal.ONE)) {
            throw new IllegalStateException("Maximum investment share for rating " + rating
                    + " outside of range (min, 1>: " + targetShare);
        }
        return SimpleInvestmentStrategyService.createIndividualStrategy(rating, targetShare, maximumShare,
                minTerm, maxTerm, minAskAmount, maxAskAmount, minLoanAmount, maxLoanAmount, minLoanShare,
                maxLoanShare, preferLongerTerms);
    }

    private void checkIndividualStrategies(final Map<Rating, StrategyPerRating> strategies) {
        final BigDecimal ratingShareSum = strategies.values().stream()
                .map(StrategyPerRating::getTargetShare)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        if (ratingShareSum.compareTo(BigDecimal.ONE) != 0) {
            throw new IllegalStateException("Sum total of target shares is not 1. It is: " + ratingShareSum + ".");
        }
    }

    @Override
    public InvestmentStrategy parse(final File strategyFile) throws InvestmentStrategyParseException {
        try {
            SimpleInvestmentStrategyService.LOGGER.info("Using strategy: '{}'", strategyFile.getAbsolutePath());
            final ImmutableConfiguration c = SimpleInvestmentStrategyService.getConfig(strategyFile);
            final int minimumBalance = SimpleInvestmentStrategyService.getMinimumBalance(c);
            SimpleInvestmentStrategyService.LOGGER.debug("Minimum balance to invest must be {} CZK.", minimumBalance);
            final int maximumInvestment = SimpleInvestmentStrategyService.getMaximumInvestment(c);
            SimpleInvestmentStrategyService.LOGGER.debug("Maximum investment must not exceed {} CZK.", maximumInvestment);
            final Map<Rating, StrategyPerRating> individualStrategies = Arrays.stream(Rating.values())
                    .collect(Collectors.toMap(Function.identity(), r -> SimpleInvestmentStrategyService.parseRating(r, c)));
            this.checkIndividualStrategies(individualStrategies);
            return new SimpleInvestmentStrategy(minimumBalance, maximumInvestment, individualStrategies);
        } catch (final IllegalStateException ex) {
            throw new InvestmentStrategyParseException(ex);
        }<commit_msg>Much less logging regarding strategies<commit_after>/*
 * Copyright 2016 <NAME>
 *
 * 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.File;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.github.triceo.robozonky.remote.Rating;
import com.github.triceo.robozonky.strategy.InvestmentStrategy;
import com.github.triceo.robozonky.strategy.InvestmentStrategyParseException;
import com.github.triceo.robozonky.strategy.InvestmentStrategyService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SimpleInvestmentStrategyService implements InvestmentStrategyService {

    private static final Logger LOGGER = LoggerFactory.getLogger(SimpleInvestmentStrategyService.class);

    private static ImmutableConfiguration getConfig(final File strategyFile) throws InvestmentStrategyParseException {
        return ImmutableConfiguration.from(strategyFile);
    }

    static int getMinimumBalance(final ImmutableConfiguration config) {
        final int minimumBalance = StrategyFileProperty.MINIMUM_BALANCE.getValue(config::getInt)
                .orElseThrow(() -> new IllegalStateException("Minimum balance is missing."));
        if (minimumBalance < InvestmentStrategy.MINIMAL_INVESTMENT_ALLOWED) {
            throw new IllegalStateException("Minimum balance is less than "
                    + InvestmentStrategy.MINIMAL_INVESTMENT_ALLOWED + " CZK.");
        }
        return minimumBalance;
    }

    static int getMaximumInvestment(final ImmutableConfiguration config) {
        final int maximumInvestment = StrategyFileProperty.MAXIMUM_INVESTMENT.getValue(config::getInt)
                .orElseThrow(() -> new IllegalStateException("Maximum investment is missing."));
        if (maximumInvestment < InvestmentStrategy.MINIMAL_INVESTMENT_ALLOWED) {
            throw new IllegalStateException("Maximum investment is less than "
                    + InvestmentStrategy.MINIMAL_INVESTMENT_ALLOWED + " CZK.");
        }
        return maximumInvestment;
    }

    private static StrategyPerRating createIndividualStrategy(final Rating r, final BigDecimal targetShare,
                                                              final BigDecimal maxShare, final int minTerm,
                                                              final int maxTerm, final int minAskAmount,
                                                              final int maxAskAmount, final int minLoanAmount,
                                                              final int maxLoanAmount, final BigDecimal minLoanShare,
                                                              final BigDecimal maxLoanShare,
                                                              final boolean preferLongerTerms) {
        SimpleInvestmentStrategyService.LOGGER.debug("Acceptable shares of rating '{}' on total investments is " +
                "between {} and {}.", r.getCode(), targetShare, maxShare);
        SimpleInvestmentStrategyService.LOGGER.debug("Acceptable loans for rating '{}' are between {} and {} CZK on " +
                        "terms between {} and {} months, {} preferred.", r.getCode(), minAskAmount,
                maxAskAmount < 0 ? "+inf" : maxAskAmount, minTerm == -1 ? 0 : minTerm,
                maxTerm < 0 ? "+inf" : maxTerm + 1, preferLongerTerms ? "longer" : "shorter");
        SimpleInvestmentStrategyService.LOGGER.debug("Acceptable investment for a loan of rating '{}' is between {} " +
                        "and {} CZK with investment share between {} and {}.", r.getCode(), minLoanAmount,
                maxLoanAmount < 0 ? "+inf" : maxLoanAmount, minLoanShare, maxLoanShare);
        return new StrategyPerRating(r, targetShare, maxShare, minTerm, maxTerm, minLoanAmount, maxLoanAmount,
                minLoanShare, maxLoanShare, minAskAmount, maxAskAmount, preferLongerTerms);
    }

    private static StrategyPerRating parseRating(final Rating rating, final ImmutableConfiguration config) {
        SimpleInvestmentStrategyService.LOGGER.debug("- Adding strategy for rating '{}'.", rating.getCode());
        final boolean preferLongerTerms = StrategyFileProperty.PREFER_LONGER_TERMS.getValue(rating, config::getBoolean);
        // shares of a rating on the total pie
        final BigDecimal targetShare = StrategyFileProperty.TARGET_SHARE.getValue(rating, config::getBigDecimal);
        if (!Util.isBetweenZeroAndOne(targetShare)) {
            throw new IllegalStateException("Target share for rating " + rating + " outside of range <0, 1>: "
                    + targetShare);
        }
        final BigDecimal maximumShare = StrategyFileProperty.MAXIMUM_SHARE.getValue(rating, config::getBigDecimal);
        if (!Util.isBetweenAAndB(maximumShare, targetShare, BigDecimal.ONE)) {
            throw new IllegalStateException("Maximum share for rating " + rating + " outside of range <" + targetShare
                    + ", 1>: " + maximumShare);
        }
        // terms
        final int minTerm = StrategyFileProperty.MINIMUM_TERM.getValue(rating, config::getInt);
        if (minTerm < -1) {
            throw new IllegalStateException("Minimum acceptable term for rating " + rating + " negative.");
        }
        final int maxTerm = StrategyFileProperty.MAXIMUM_TERM.getValue(rating, config::getInt);
        if (maxTerm < -1) {
            throw new IllegalStateException("Maximum acceptable term for rating " + rating + " negative.");
        } else if (minTerm > maxTerm && maxTerm != -1) {
            throw new IllegalStateException("Maximum acceptable term for rating " + rating + " less than minimum.");
        }
        // loan asks
        final int minAskAmount = StrategyFileProperty.MINIMUM_ASK.getValue(rating, config::getInt);
        if (minAskAmount < -1) {
            throw new IllegalStateException("Minimum acceptable ask amount for rating " + rating + " negative.");
        }
        final int maxAskAmount = StrategyFileProperty.MAXIMUM_ASK.getValue(rating, config::getInt);
        if (maxAskAmount < -1) {
            throw new IllegalStateException("Maximum acceptable ask for rating " + rating + " negative.");
        } else if (minAskAmount > maxAskAmount && maxAskAmount != -1) {
            throw new IllegalStateException("Maximum acceptable ask for rating " + rating + " less than minimum.");
        }
        // investment amounts
        final int minLoanAmount = StrategyFileProperty.MINIMUM_LOAN_AMOUNT.getValue(rating, config::getInt);
        if (minLoanAmount < InvestmentStrategy.MINIMAL_INVESTMENT_ALLOWED) {
            throw new IllegalStateException("Minimum investment amount for rating " + rating
                    + " less than minimum investment allowed: " + minLoanAmount + " CZK.");
        }
        final int maxLoanAmount = StrategyFileProperty.MAXIMUM_LOAN_AMOUNT.getValue(rating, config::getInt);
        if (maxLoanAmount != -1 && maxLoanAmount < minLoanAmount) {
            throw new IllegalStateException("Maximum investment amount for rating " + rating + " less than minimum.");
        }
        final BigDecimal minLoanShare = StrategyFileProperty.MINIMUM_LOAN_SHARE.getValue(rating,
                config::getBigDecimal);
        if (!Util.isBetweenZeroAndOne(minLoanShare)) {
            throw new IllegalStateException("Minimum investment share for rating " + rating
                    + " outside of range <0, 1>: " + targetShare);
        }
        final BigDecimal maxLoanShare = StrategyFileProperty.MAXIMUM_LOAN_SHARE.getValue(rating, config::getBigDecimal);
        if (!Util.isBetweenAAndB(maxLoanShare, minLoanShare, BigDecimal.ONE)) {
            throw new IllegalStateException("Maximum investment share for rating " + rating
                    + " outside of range (min, 1>: " + targetShare);
        }
        return SimpleInvestmentStrategyService.createIndividualStrategy(rating, targetShare, maximumShare,
                minTerm, maxTerm, minAskAmount, maxAskAmount, minLoanAmount, maxLoanAmount, minLoanShare,
                maxLoanShare, preferLongerTerms);
    }

    private static void checkIndividualStrategies(final Map<Rating, StrategyPerRating> strategies) {
        final BigDecimal ratingShareSum = strategies.values().stream()
                .map(StrategyPerRating::getTargetShare)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        if (ratingShareSum.compareTo(BigDecimal.ONE) != 0) {
            throw new IllegalStateException("Sum total of target shares is not 1. It is: " + ratingShareSum + ".");
        }
    }

    @Override
    public InvestmentStrategy parse(final File strategyFile) throws InvestmentStrategyParseException {
        try {
            SimpleInvestmentStrategyService.LOGGER.info("Using strategy: '{}'", strategyFile.getAbsolutePath());
            final ImmutableConfiguration c = SimpleInvestmentStrategyService.getConfig(strategyFile);
            final int minimumBalance = SimpleInvestmentStrategyService.getMinimumBalance(c);
            SimpleInvestmentStrategyService.LOGGER.debug("Minimum balance to invest must be {} CZK.", minimumBalance);
            final int maximumInvestment = SimpleInvestmentStrategyService.getMaximumInvestment(c);
            SimpleInvestmentStrategyService.LOGGER.debug("Maximum investment must not exceed {} CZK.", maximumInvestment);
            final Map<Rating, StrategyPerRating> individualStrategies = Arrays.stream(Rating.values())
                    .collect(Collectors.toMap(Function.identity(), r -> SimpleInvestmentStrategyService.parseRating(r, c)));
            SimpleInvestmentStrategyService.checkIndividualStrategies(individualStrategies);
            return new SimpleInvestmentStrategy(minimumBalance, maximumInvestment, individualStrategies);
        } catch (final IllegalStateException ex) {
            throw new InvestmentStrategyParseException(ex);
        }
{'fraction_non_alphanumeric': 0.08648749186273598, 'fraction_numerical': 0.0028829163954245327, 'mean_word_length': 2.9082318735235324, 'pattern_counts': {'":': 0, '<': 42, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 24, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '425976', 'n_tokens_mistral': 5524, 'n_tokens_neox': 5225, 'n_words': 1389}