module StockHistory

Constants

VERSION

Public Class Methods

history(symbol, params={}) click to toggle source

Get daily history for a symbol Available params: :start_date => '2016-01-01' :end_date => Date.today.to_s :freq => 'd', 'w' or 'm'

# File lib/stock_history.rb, line 16
def self.history(symbol, params={})
  his_params = {}
  his_params[:start_date] = to_date(params[:start_date])
  his_params[:stop_date] = to_date(params[:stop_date])
  his_params[:freq] = (%w(d w m).include?(params[:freq]) ? params[:freq] : 'd')

  url = build_url(symbol, his_params)
  uri = URI(url)
  response = Net::HTTP.get(uri)
  parse_response(response)
end

Private Class Methods

build_url(symbol, params={}) click to toggle source
# File lib/stock_history.rb, line 114
def self.build_url(symbol, params={})
  params[:start_date] ||= Date.parse('2016-01-01')
  params[:stop_date]  ||= Date.today
  params[:freq]  ||= 'd'

  url_params = {}
  url_params['s'] = symbol.upcase
  url_params['a'] = params[:start_date].month - 1
  url_params['b'] = params[:start_date].day
  url_params['c'] = params[:start_date].year
  url_params['d'] = params[:stop_date].month - 1
  url_params['e'] = params[:stop_date].day
  url_params['f'] = params[:stop_date].year
  url_params['g'] = params[:freq]        
  url_params['y'] = '0'
  url_params['z'] = '30000'

  url = 'http://real-chart.finance.yahoo.com/x?'
  url_params.keys.sort.each do |key|
    url += "&#{key}=#{url_params[key]}"
  end

  url
end
parse_response(response) click to toggle source
# File lib/stock_history.rb, line 29
def self.parse_response(response)
  results = []
  lines = response.split("\n")

  if lines.count <= 5
    raise ParseException.new("Response format not correct")      
  end

  # Date,Open,High,Low,Close,Volume,Adj Close
  if lines[0] != "Date,Open,High,Low,Close,Volume,Adj Close"
    raise ParseException.new("Header not correct")
  end

  if lines[-1] != "STATUS, 0"
    raise ParseException.new("Status not correct")
  end

  # TOTALSIZE, 24
  total_size = 0
  unless lines[-2].start_with?("TOTALSIZE")
    raise ParseException.new("Size not parsed out")
  else
    total_size = lines[-2].split(',').last.to_i
  end

  # STARTDATE, 20161230
  # ENDDATE, 20170203
  start_date = nil
  end_date = nil
  unless lines[-3].start_with?("ENDDATE") or lines[-4].start_with?("STARTDATE")
    raise ParseException.new("Dates not parsed out")
  else
    start_date = lines[-4].split(',').last.strip
    end_date = lines[-3].split(',').last.strip
  end

  dividends = {}
  splits = {}

  lines[1..-5].each do |line|
    # puts line

    elements = line.split(",").map(&:strip)
    if elements.first == 'SPLIT'
      # this is reverse split (merge), and will apear above the daily history on that day
      # SPLIT, 20170123,1:8
      rates = elements.last.split(':').map(&:to_f)
      splits[elements[1]] = {
        :date => elements[1],
        :split => rates[0] < rates[1] ? 1.0 : rates[0] / rates[1],
        :merge => rates[0] < rates[1] ? rates[1] / rates[0] : 1.0
      }
    elsif elements.first == 'DIVIDEND'
      # this will apear above the daily history on that day
      # DIVIDEND, 20161219,1.507000
      dividends[elements[1]] = {
        :date => elements[1],
        :dividend => elements[2].to_f.round(2)
      }
    else
      # Date,Open,High,Low,Close,Volume,Adj Close
      # 20161219,28.889999,28.92,28.120001,28.43,27938700,28.43
      trading_date = to_date(elements.first)
      if trading_date.nil?
        raise ParseException.new("Format error on line: #{line}")
      end
      results << {
        :date         => elements[0],
        :date_parsed  => to_date(elements[0]),
        :open         => elements[1].to_f.round(2),
        :high         => elements[2].to_f.round(2),
        :low          => elements[3].to_f.round(2),
        :close        => elements[4].to_f.round(2),
        :volume       => elements[5].to_f.round(2),
        :adjusted_close => elements[6].to_f.round(2),
        :split        => splits[elements[0]] == nil ? nil : splits[elements[0]][:split],
        :merge        => splits[elements[0]] == nil ? nil : splits[elements[0]][:merge],
        :dividend     => dividends[elements[0]] == nil ? nil : dividends[elements[0]][:dividend],
      }
    end
  end

  results
end