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

Commit

Permalink
Added depth estimator.
Browse files Browse the repository at this point in the history
  • Loading branch information
Takanori Takase committed Jan 22, 2018
1 parent 8dd128e commit 4b05789
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
import com.after_sunrise.cryptocurrency.cryptotrader.framework.Request;
import com.after_sunrise.cryptocurrency.cryptotrader.framework.Trade;
import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.math3.stat.descriptive.SummaryStatistics;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.NavigableMap;
import java.util.Objects;
import java.util.stream.Collectors;

import static java.math.BigDecimal.*;
import static java.math.RoundingMode.HALF_UP;
Expand All @@ -22,6 +23,10 @@
*/
public class DepthEstimator extends AbstractEstimator {

private static final int I_NOTIONAL = 0;

private static final int I_QUANTITY = 1;

private static final String SAMPLES_KEY = "samples";

private static final int SAMPLES_VAL = 60;
Expand All @@ -45,33 +50,29 @@ public Estimation estimate(Context context, Request request) {

double[] averages = {0.0, 0.0};

BigDecimal ceiling = mid.add(deviation);
BigDecimal ceiling = mid.multiply(ONE.add(deviation));

trimToEmpty(context.getAskPrices(key)).entrySet().stream()
.filter(e -> e.getValue() != null)
.filter(e -> e.getKey() != null)
.filter(e -> e.getKey().signum() > 0)
.filter(e -> e.getKey().compareTo(ceiling) <= 0)
.filter(e -> e.getValue() != null)
.filter(e -> e.getValue().signum() > 0)
.forEach(e -> {
averages[0] = averages[0] + e.getValue().doubleValue() * e.getKey().doubleValue();
averages[1] = averages[1] + e.getValue().doubleValue();
averages[I_NOTIONAL] = averages[I_NOTIONAL] + e.getValue().doubleValue() * e.getKey().doubleValue();
averages[I_QUANTITY] = averages[I_QUANTITY] + e.getValue().doubleValue();
});

BigDecimal floor = mid.subtract(deviation);
BigDecimal floor = mid.multiply(ONE.subtract(deviation));

trimToEmpty(context.getBidPrices(key)).entrySet().stream()
.filter(e -> e.getValue() != null)
.filter(e -> e.getKey() != null)
.filter(e -> e.getKey().signum() > 0)
.filter(e -> e.getKey().compareTo(floor) >= 0)
.filter(e -> e.getValue() != null)
.filter(e -> e.getValue().signum() > 0)
.forEach(e -> {
averages[0] = averages[0] + e.getValue().doubleValue() * e.getKey().doubleValue();
averages[1] = averages[1] + e.getValue().doubleValue();
averages[I_NOTIONAL] = averages[I_NOTIONAL] + e.getValue().doubleValue() * e.getKey().doubleValue();
averages[I_QUANTITY] = averages[I_QUANTITY] + e.getValue().doubleValue();
});

double average = averages[0] / averages[1];
double average = averages[I_NOTIONAL] / averages[I_QUANTITY];

if (!Double.isFinite(average)) {
return BAIL;
Expand All @@ -81,7 +82,7 @@ public Estimation estimate(Context context, Request request) {

BigDecimal p = mid.subtract(d);

BigDecimal c = ONE.subtract(d.divide(deviation, SCALE, HALF_UP).abs()).max(ONE).min(ZERO);
BigDecimal c = ONE.subtract(deviation).min(ONE).max(ZERO);

log.debug("Estimated : {} (confidence=[{}] mid=[{}] dev=[{}] avg=[{}])", p, c, mid, deviation, average);

Expand All @@ -92,37 +93,33 @@ public Estimation estimate(Context context, Request request) {
@VisibleForTesting
BigDecimal calculateDeviation(Context context, Request request) {

Instant now = request.getCurrentTime();
Instant to = request.getCurrentTime();

Duration interval = Duration.between(now, request.getTargetTime());
Duration interval = Duration.between(to, request.getTargetTime());

Instant from = request.getCurrentTime().minus(interval.toMillis() * getSamples(), MILLIS);

List<Trade> trades = context.listTrades(getKey(context, request), from.minus(interval));

List<BigDecimal> prices = trimToEmpty(trades).stream()
.filter(Objects::nonNull)
.filter(t -> t.getPrice() != null)
.filter(t -> t.getPrice().signum() != 0)
.filter(t -> t.getSize() != null)
.filter(t -> t.getSize().signum() != 0)
.map(Trade::getPrice)
.collect(Collectors.toList());
NavigableMap<Instant, BigDecimal> prices = collapsePrices(trades, interval, from, to, false);

NavigableMap<Instant, BigDecimal> returns = calculateReturns(prices);

SummaryStatistics stats = new SummaryStatistics();

if (prices.size() <= 1) {
returns.values().stream().filter(Objects::nonNull).forEach(r -> stats.addValue(r.doubleValue()));

if (stats.getN() <= 1) {
return null;
}

BigDecimal sum = prices.stream().reduce(ZERO, BigDecimal::add);

BigDecimal avg = sum.divide(valueOf(prices.size()), SCALE, HALF_UP);
double avg = stats.getMean();

BigDecimal vrc = prices.stream().map(p -> p.subtract(avg).pow(2)).reduce(ZERO, BigDecimal::add)
.divide(valueOf(trades.size() - 1), SCALE, HALF_UP);
double dev = stats.getStandardDeviation();

BigDecimal dev = valueOf(Math.sqrt(vrc.doubleValue())).setScale(SCALE, HALF_UP);
double sum = Math.abs(avg) + (dev * getSigma(stats.getN() - 1).doubleValue());

return dev.multiply(getSigma(trades.size() - 1));
return Double.isFinite(sum) ? BigDecimal.valueOf(sum).setScale(SCALE, HALF_UP) : null;

}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.after_sunrise.cryptocurrency.cryptotrader.service.estimator;

import com.after_sunrise.cryptocurrency.cryptotrader.framework.Context;
import com.after_sunrise.cryptocurrency.cryptotrader.framework.Estimator.Estimation;
import com.after_sunrise.cryptocurrency.cryptotrader.framework.Request;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

import static com.after_sunrise.cryptocurrency.cryptotrader.framework.Context.Key.from;
import static org.mockito.Mockito.*;
import static org.testng.Assert.assertEquals;

/**
* @author takanori.takase
* @version 0.0.1
*/
public class DepthEstimatorTest {

private DepthEstimator target;

private Context context;

@BeforeMethod
public void setUp() {

context = mock(Context.class);

target = spy(new DepthEstimator());

}

@Test
public void testEstimate() throws Exception {

Request request = Request.builder().site("a").instrument("b").build();

Runnable initializer = () -> {

doReturn(new BigDecimal("0.1")).when(target).calculateDeviation(context, request);
doReturn(new BigDecimal("200")).when(context).getMidPrice(from(request));

Map<BigDecimal, BigDecimal> asks = new HashMap<>();
asks.put(null, new BigDecimal("01")); // Exclude
asks.put(new BigDecimal("222"), new BigDecimal("12")); // Exclude
asks.put(new BigDecimal("221"), new BigDecimal("43")); // Exclude
asks.put(new BigDecimal("220"), new BigDecimal("56"));
asks.put(new BigDecimal("219"), new BigDecimal("87"));
asks.put(new BigDecimal("218"), new BigDecimal("90"));
asks.put(new BigDecimal("217"), null); // Exclude
doReturn(asks).when(context).getAskPrices(from(request));

Map<BigDecimal, BigDecimal> bids = new HashMap<>();
bids.put(new BigDecimal("183"), null); // Exclude
bids.put(new BigDecimal("182"), new BigDecimal("12"));
bids.put(new BigDecimal("181"), new BigDecimal("43"));
bids.put(new BigDecimal("180"), new BigDecimal("56"));
bids.put(new BigDecimal("179"), new BigDecimal("87")); // Exclude
bids.put(new BigDecimal("178"), new BigDecimal("90")); // Exclude
bids.put(null, new BigDecimal("01")); // Exclude
doReturn(bids).when(context).getBidPrices(from(request));

};

// VWAP = 206.51162790697674
// Predict = 200 + (200 - VWAP) = 193.48837209302326
initializer.run();
Estimation result = target.estimate(context, request);
assertEquals(result.getPrice(), new BigDecimal("193.48837209302326"));
assertEquals(result.getConfidence(), new BigDecimal("0.9"));

}

}

0 comments on commit 4b05789

Please sign in to comment.