Search code examples
rubyyahoo-finance

Calculate moving averages with ruby and simple_statistics and appending to JSON


I am trying to calculate moving averages (simple and exponential) and I have come across the simple_statistics gem, which is perfect for my needs. I am trying to modify the code from this link: How to calculate simple moving average) for my purposes.

GOAL: I have a JSON like this which lists historical prices for a single stock over a long time period:

[
  {
    "Low": 8.63,
    "Volume": 14211900,
    "Date": "2012-10-26",
    "High": 8.79,
    "Close": 8.65,
    "Adj Close": 8.65,
    "Open": 8.7
  },

To this, I would like to add moving averages for each day (simple and exponential - which the simple_statistics gem seems to do easily) for 20, and 50 day averages (and others as required) so it would appear something like this for each day:

[
  {
    "Low": 8.63,
    "Volume": 14211900,
    "Date": "2012-10-26",
    "High": 8.79,
    "Close": 8.65,
    "Adj Close": 8.65,
    "Open": 8.7,
    "SMA20":
    "SMA50":
  },

I would prefer to use the yahoo_finance, and simple_statistics gems and then append the output to the original JSON as I have a feeling that once I gain a better understanding, it will be easier for me to modify.

Right now, I'm still reading up on how I will do this (any help is appreciated) Below is my attempt to calculate a 20 day simple moving average for Microsoft (doesn't work). This way (using HistoricalQuotes_days) seems to assume that the start date is today, which wont work for my overall goal.

require 'rubygems'
require 'yahoofinance'
require 'simple_statistics'

averages = {}

dates.each do |dates|
  quotes = YahooFinance::get_HistoricalQuotes_days ( 'MSFT' , 20 ) start, finish
  closes = quotes.collect { |quote| quote.close }
  averages = closes.mean
end

Thank you

UPDATE: I don't actually need to use YahooFinance gem as I already have the data in a JSON. What I dont know how to do is pull from the JSON array, make the calculations using the simple_statistics gem, and then add the new data into the original JSON.


Solution

  • Using the gem, I see two ways to get your data. Here they are (note they both can take a block):

    YahooFinance::get_HistoricalQuotes_days('MSFT', 20)
    

    Which returns an array of YahooFinance::HistoricalQuote objects with the following methods:

    [ :recno, :recno=, :symbol, :symbol=, :date, :date=, 
      :open, :open=, :high, :high=, :low, :low=, :close, 
      :close=, :adjClose, :adjClose=, :volume, :volume=, 
      :to_a, :date_to_Date  ]
    

    Or:

    YahooFinance::get_historical_quotes_days('MSFT', 20)
    

    which returns an array of values from the documentation :

    Getting the historical quote data as a raw array.
    The elements of the array are:
      [0] - Date
      [1] - Open
      [2] - High
      [3] - Low
      [4] - Close
      [5] - Volume
      [6] - Adjusted Close
    

    And to take an average (simple moving average), you can easily do:

    ary.reduce(:+) / ary.length

    Where ary would hold the values to average (need to be floats or it will integer divide). To do the exponential moving average, just use the following formula:

    (close - previous_ema) * (2 / (amount_of_days_ago + 1) ) + previous_ema
    

    Where close is the stock's close, previous_ema is yesterday's ema, and amount_of_days_ago is the range of the average into the past, for instance 20 (days).

    edit

    oh. Yeah parsing json is easy: https://github.com/flori/json

    I can't write a whole beginning ruby guide, but the basics for what you need are Hash and Array. Look up how to use ruby hashes and arrays, and thats probably a good 30% of ruby programming right there.

    For example to get the json objects in an array and then get just the closes, you could use Array#map like so:

    stocks = JSON.parse( your_json_here )
    array = stocks.map{ |hash| hash["Close"] }
    # => [8.65, 9.32, etc... ]
    

    hope that gets you started n good luck