# 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();
from.add(Calendar.YEAR, -5);
Stock google = YahooFinance.get("GOOG", from, to, Interval.DAILY);
List<HistoricalQuote> historyGoogle = google.getHistory();
```

### 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
, BigDecimal::add); // accumulator
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
(a,b) -> new BigDecimal[]{a[0].add(b[0]), a[1].add(b[1])}); // accumulator
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!

## References

- YahooFinanceAPI
- Oracle Java Documentation: Reduction
- What Happened to Google Finance?
- YahooFinanceAPI Documentation
- How to average BigDecimals Streams: WillShackleford