# Fun with Yahoo Finance API

## Google Finance API No More!

As of March 2018, something happened to Google Finance - it got taken to the chopping board and is now a miserable husk of its former self! Long gone are the days where one could simply hook into the API and download a fat, juicy csv-file of historical stock price data… or a sensible JSON of option prices.

Thankfully, there are many alternatives out there.

## YahooFinanceAPI

Since I’ve been accessing the API through Java, I’m taking a look at this unofficial library.

### Getting Historical Data

You can construct a Stock object such that it contains 5 years historical data. Then use the getHistory() method to return a collection of HistoricalQuote elements.

Calendar from = Calendar.getInstance();
Calendar to = Calendar.getInstance();

Stock google = YahooFinance.get("GOOG", from, to, Interval.DAILY);


### HistoricalQuote

To look at, say, the closing price on each day, we’ll have to traverse the List and invoke the method getClose().

for(HistoricalQuote historicalQuote : historyGoogle)
System.out.println(historicalQuote.getClose());


Looking at the documentation, we can see that this method (and others like it) returns a BigDecimal.

## Stream()

If you want, you can stream() instead of iterating through the collection.

BigDecimal totalClose = historyGoogle
.stream()
.map(HistoricalQuote::getClose)
.reduce(    BigDecimal.ZERO // identity
System.out.printf("Total close: %s\n", totalClose);


All we are doing here is totalling every closing price.

In map, we map from the stream to create a tuple. In this case, we use the getClose() method to emit a singleton of BigDecimal.

In reduce we aggregate. And in this instance, we’re adding.

Our reduce operation takes two arguments:

• Identity
• Accumulator

The former is both the initial value of the sum and the default value in case the mapping returns a null-value at any point in the stream. That is, zero.

The latter is the part that adds the BigDecimal element to the running total of closing prices.

### Average Closing Price

To calculate any average, we need both a total and a count. We will again use stream() and return both these values.

BigDecimal[] totalWithCount = historyGoogle.stream()
.map(HistoricalQuote -> new BigDecimal[]{HistoricalQuote.getClose(), BigDecimal.ONE})
.reduce(    new BigDecimal[]{BigDecimal.ZERO, BigDecimal.ZERO},         // identity
BigDecimal mean = totalWithCount[0].divide(totalWithCount[1], RoundingMode.HALF_UP);
System.out.println(mean);


#### Stream.map

Now in the map operation we have two values:

• a closing price
• unity

When we emit from map to reduce, they are simply added to a running total and running count.

#### Stream.reduce

As before, reduce takes both identity and accumulator arguments. But now, map is sending a pair of values. So in both reduce arguments, we construct a BigDecimal[] and give it two elements.

As before, we use zero in identity. But now, the accumulator now has two parameters: (a,b). Where a is a two-element array which contains the running total and count in the first and second elements respectively. Likewise, b is a two element array, but it contains the next two elements to add to a.

### Equal Weighted Variance

How would we go about computing a simple variance from the stream?

Suppose our variance is defined as:

Our method is defined as

public BigDecimal varianceEqualWeighted(List<HistoricalQuote> history, BigDecimal mean)


This requires that we have computed our mean in a prior step.

BigDecimal totalProduct = IntStream
.range(0, history.size())


This time our stream is an instantiation of an IntStream, which we use to address the index of history.

            .mapToObj(i -> history.get(i).getClose().subtract(mean))
.map(bd -> bd.multiply(bd))


Now we map the difference between the mean and the closing price.

Then we emit a tuple containing the square of the previous map.

			.reduce(BigDecimal.ZERO, BigDecimal::add);


Next, we aggregate the square. BigDecimal.ZERO is our zero element, necessary for computing the partial total at the first element.

    return totalProduct.divide(new BigDecimal(history.size() - 1), RoundingMode.HALF_UP);


When all is said and done, we return BigDecimal totalProduct and divide it by the number of elements in the stream. We subtract 1 from this divisor to reduce error caused by bias.

## Yahoo!

And so we have it - a quick introduction to YahooFinanceAPI and a quick spin with Java 8 streams! Yeah! MapReduce!