Skip to content
This repository has been archived by the owner on Mar 12, 2020. It is now read-only.

Commit

Permalink
Enhanced composite estimation.
Browse files Browse the repository at this point in the history
  • Loading branch information
Takanori Takase committed Jan 28, 2018
1 parent 1ecf235 commit 10bb997
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,17 @@
import com.after_sunrise.cryptocurrency.cryptotrader.framework.Request;
import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import java.math.BigDecimal;
import java.util.AbstractMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.math.BigDecimal.ONE;
import static java.math.BigDecimal.*;
import static java.math.RoundingMode.HALF_UP;

/**
Expand All @@ -25,11 +24,6 @@
*/
public class CompositeEstimator extends AbstractEstimator {

private static final Map<Character, BinaryOperator<BigDecimal>> OPERATORS = Stream.of(
new AbstractMap.SimpleEntry<>('*', (BinaryOperator<BigDecimal>) BigDecimal::multiply),
new AbstractMap.SimpleEntry<>('/', (BinaryOperator<BigDecimal>) (o1, o2) -> o1.divide(o2, SCALE, HALF_UP))
).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

static final CompositeEstimator INSTANCE = new CompositeEstimator();

private CompositeEstimator() {
Expand All @@ -51,9 +45,9 @@ Estimation estimate(Context context, Request request, BiFunction<Context, Reques

Request.RequestBuilder builder = Request.build(request);

BigDecimal price = ONE;
BigDecimal[] prices = new BigDecimal[1];

BigDecimal confidence = ONE;
BigDecimal[] confidences = new BigDecimal[1];

for (Composite composite : composites) {

Expand All @@ -63,9 +57,27 @@ Estimation estimate(Context context, Request request, BiFunction<Context, Reques
return BAIL;
}

BinaryOperator<BigDecimal> operator = OPERATORS.get(site.charAt(0));
char operation = site.charAt(0);

BinaryOperator<BigDecimal> operator = null;

if ('+' == operation) {
operator = BigDecimal::add;
}

if ('-' == operation) {
operator = BigDecimal::subtract;
}

if ('*' == operation) {
operator = BigDecimal::multiply;
}

if ('/' == operation) {
operator = (o1, o2) -> o1.divide(o2, SCALE, HALF_UP);
}

if (operator == null) {
if ('@' != operation && operator == null) {
return BAIL;
}

Expand All @@ -79,13 +91,31 @@ Estimation estimate(Context context, Request request, BiFunction<Context, Reques
return BAIL;
}

price = operator.apply(price, estimation.getPrice());
if (operator != null) {

prices[0] = operator.apply(trim(prices[0], ONE), estimation.getPrice());

confidences[0] = trim(confidences[0], ONE).multiply(estimation.getConfidence());

} else {

prices = ArrayUtils.add(prices, estimation.getPrice());

confidence = confidence.multiply(estimation.getConfidence());
confidences = ArrayUtils.add(confidences, estimation.getConfidence());

}

}

return Estimation.builder().price(price).confidence(confidence).build();
long countPrice = Stream.of(prices).filter(Objects::nonNull).count();
BigDecimal totalPrice = Stream.of(prices).filter(Objects::nonNull).reduce(BigDecimal::add).orElse(ZERO);
BigDecimal averagePrice = totalPrice.divide(valueOf(countPrice), SCALE, HALF_UP);

long countConfidence = Stream.of(confidences).filter(Objects::nonNull).count();
BigDecimal totalConfidence = Stream.of(confidences).filter(Objects::nonNull).reduce(BigDecimal::add).orElse(ONE);
BigDecimal averageConfidence = totalConfidence.divide(valueOf(countConfidence), SCALE, HALF_UP);

return Estimation.builder().price(averagePrice).confidence(averageConfidence).build();

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public void testCompositeEstimator_EstimateComposite() {
composites.add(new Composite("/b", "2"));
composites.add(new Composite("/b", "3"));
composites.add(new Composite("*c", "4"));
composites.add(new Composite("@d", "5"));
Request request = Request.builder().currentTime(Instant.now()).estimatorComposites(composites).build();

BiFunction<Context, Request, Estimator.Estimation> function = mock(BiFunction.class);
Expand Down Expand Up @@ -89,21 +90,42 @@ public void testCompositeEstimator_EstimateComposite() {
return b.price(new BigDecimal("0.4")).confidence(new BigDecimal("0.6")).build();
}

if ("d".equals(r.getSite()) && "5".equals(r.getInstrument())) {
return b.price(new BigDecimal("0.5")).confidence(new BigDecimal("0.5")).build();
}

return null;

}).when(function).apply(same(context), any());

CompositeEstimator target = CompositeEstimator.INSTANCE;

// Price = 1 * 0.1 / 0.2 / 0.3 * 0.4 = 0.66666666666...
// Confidence = 0.9 * 0.8 * 0.7 * 0.6 = 0.3024
// Multiplied Price = 1 * 0.1 / 0.2 / 0.3 * 0.4 = 0.66666666666...
// Average Price = (0.666666 + 0.5) / 2
// Confidence = [(0.9 * 0.8 * 0.7 * 0.6) + 0.5] / 2 = 0.7
Estimator.Estimation result = target.estimate(context, request, function);
assertEquals(result.getPrice(), new BigDecimal("0.66666666668"));
assertEquals(result.getConfidence(), new BigDecimal("0.3024"));
assertEquals(result.getPrice(), new BigDecimal("0.5833333333"));
assertEquals(result.getConfidence(), new BigDecimal("0.4012000000"));

// Composite Only
composites.clear();
composites.add(new Composite("*a", "1"));
composites.add(new Composite("/b", "2"));
result = target.estimate(context, request, function);
assertEquals(result.getPrice(), new BigDecimal("0.5000000000"));
assertEquals(result.getConfidence(), new BigDecimal("0.7200000000"));

// Average Only
composites.clear();
composites.add(new Composite("@c", "4"));
composites.add(new Composite("@d", "5"));
result = target.estimate(context, request, function);
assertEquals(result.getPrice(), new BigDecimal("0.4500000000"));
assertEquals(result.getConfidence(), new BigDecimal("0.5500000000"));

// Unknown Operator ('@')
composites.clear();
composites.add(new Composite("@a", "1"));
composites.add(new Composite("%a", "1"));
result = target.estimate(context, request, function);
assertEquals(result.getPrice(), null);
assertEquals(result.getConfidence(), new BigDecimal("0"));
Expand Down

0 comments on commit 10bb997

Please sign in to comment.