/* * Copyright (c) 2012, 2013, Trivadis (Anatole Tresch), Werner Keil. * * 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 org.javamoney.calc.common; import org.javamoney.calc.CalculationContext; import org.javamoney.calc.ComplexCalculation; import org.javamoney.calc.ComplexType; import org.javamoney.calc.ComplexValue; import org.javamoney.moneta.Money; import javax.money.MonetaryAmount; import javax.money.MonetaryOperator; import java.math.BigDecimal; import java.math.MathContext; import java.text.NumberFormat; import java.util.*; /** * This class allows to extract the Weighted Average of a collection of {@link MonetaryAmount} or * number instances. * * * The concept of weighted average is used in various financial formulas. Weighted average cost of * capital (WACC) and weighted average beta are two examples that use this formula. * * Another example of using the weighted average formula is when a company has a wide fluctuation * in sales, perhaps due to producing a seasonal product. If the company would like to calculate the * average of one of their variable expenses, the company could use the weighted average formula with * sales as the weight to gain a better understanding of their expenses compared to how much they * produce or sell. * * This class does - other as the example referenced above - not require that all weights sum up to {@code 1.0}. * Instead of it calculates the overall sum of weights and uses this as the overall reference value * to be used for determining the effective weights given. * * @author Anatole Tresch * * @see <a href="http://www.financeformulas.net/Weighted_Average.html#Calc-Header"> * http://www.financeformulas.net/Weighted_Average.html#Calc-Header</a> */ public final class WeightedAverage { /** * A weighted value is a value also with a weight attached. */ public static final class WeightedValue{ private BigDecimal value; private BigDecimal weight; private WeightedValue(BigDecimal value, BigDecimal weight){ this.value = Objects.requireNonNull(value); this.weight = Objects.requireNonNull(weight); } /** * Access the value. * @return the value, not null. */ public BigDecimal getValue() { return value; } /** * Access the weight. * @return the weight, not null. */ public BigDecimal getWeight() { return weight; } @Override public String toString() { return "WeightedValue{" + "value=" + value + ", weight=" + weight + '}'; } } private WeightedAverage(Collection<WeightedValue> values){ this.values.addAll(values); } /** * Allows to create a weighted value. * @param value the value, not null. * @param weight the weight, not null * @return the new instance, never null. */ public static WeightedValue ofWeightedValue(BigDecimal value, BigDecimal weight){ return new WeightedValue(value, weight); } /** * List of values to be used to calculate the overal weighted average. */ private final List<WeightedValue> values = new ArrayList<>(); /** * Creates a new builder instance. * @return a new builder, never null. */ public static Builder newBuilder() { return new Builder(); } /** * Access the weighted values of thgis calculation. * @return the weighted values */ public Collection<WeightedValue> getValues() { return Collections.unmodifiableCollection(values); } /** * Get the weighted average for the current weigted values. * @return the weighted average, not null. */ public BigDecimal calculateWeightedAverage(){ return calculateWeightedAverage(this.values); } /** * Get the weighted average for the current weigted values. * @return the weighted average, not null. */ public static BigDecimal calculateWeightedAverage(Collection<WeightedValue> values){ BigDecimal totalWeight = new BigDecimal(0, MathContext.DECIMAL64); for(WeightedValue val:values){ totalWeight = totalWeight.add(val.getWeight(), CalculationContext.mathContext()); } BigDecimal total = CalculationContext.zero(); for(WeightedValue val:values){ BigDecimal weight = val.getWeight().divide(totalWeight, CalculationContext.mathContext()); total = total.add(val.getValue().multiply(weight, CalculationContext.mathContext())); } return total; } public static final class Builder{ /** * List of values to be used to calculate the overal weighted average. */ private final List<WeightedValue> values = new ArrayList<>(); private Builder(){} public Builder add(WeightedValue val){ this.values.add(val); return this; } public Builder add(BigDecimal value, BigDecimal weight){ this.values.add(ofWeightedValue(value, weight)); return this; } public Builder add(Number value, Number weight){ this.values.add(ofWeightedValue( new BigDecimal(value.toString()), new BigDecimal(weight.toString()))); return this; } public BigDecimal calculate(){ return WeightedAverage.calculateWeightedAverage(this.values); } public WeightedAverage build(){ return new WeightedAverage(this.values); } @Override public String toString() { return "WeightedAverage.Builder{" + "values=" + values + '}'; } } }