Module: YahooFinance

Defined in:
lib/yahoofinance.rb

Overview

Module for retrieving quote data (‘live’ and ‘historical’) from YahooFinance!

This module is made up of 2 parts:

1. Retrieving 'live' quote data. 'Live' quotes are the current

data available for a given stock. Most often delayed 20mins.

2. Retrieving 'historical' quote data.  'Historical' quotes are

for past pricings of a given stock.

Defined Under Namespace

Classes: BaseQuote, ExtendedQuote, HistoricalQuote, RealTimeQuote, StandardQuote

Constant Summary collapse

STDHASH =

‘Live’ quote data retrieval.

{
  "s" => [ "symbol", "val" ],
  "n" => [ "name", "val" ],
  "l1" => [ "lastTrade", "val.to_f" ],
  "d1" => [ "date", "val" ],
  "t1" => [ "time", "val" ],
  "c" => [ "change", "val" ],
  "c1" => [ "changePoints", "val.to_f" ],
  "p2" => [ "changePercent", "val.to_f" ],
  "p" => [ "previousClose", "val.to_f" ],
  "o" => [ "open", "val.to_f" ],
  "h" => [ "dayHigh", "val.to_f" ],
  "g" => [ "dayLow", "val.to_f" ],
  "v" => [ "volume", "val.to_i" ],
  "m" => [ "dayRange", "val" ],
  "l" => [ "lastTradeWithTime", "val" ],
  "t7" => [ "tickerTrend", "convert(val)" ],
  "a2" => [ "averageDailyVolume", "val.to_i" ],
  "b" => [ "bid", "val.to_f" ],
  "a" => [ "ask", "val.to_f" ]
# These return integers like "1,000".  The CVS parser gets confused by this
# so I've removed them for the time being.
#    "b6" => [ "bidSize", "val" ],
#    "a5" => [ "askSize", "val" ]
#    "k3" => [ "lastTradeSize", "convert(val)" ],
}
EXTENDEDHASH =
{
  "s" => [ "symbol", "val" ],
  "n" => [ "name", "val" ],
  "w" => [ "weeks52Range", "val" ],
  "j5" => [ "weeks52ChangeFromLow", "val.to_f" ],
  "j6" => [ "weeks52ChangePercentFromLow", "val" ],
  "k4" => [ "weeks52ChangeFromHigh", "val.to_f" ],
  "k5" => [ "weeks52ChangePercentFromHigh", "val" ],
  "e" => [ "earningsPerShare", "val.to_f" ],
  "r" => [ "peRatio", "val.to_f" ],
  "s7" => [ "shortRatio", "val" ],
  "r1" => [ "dividendPayDate", "val" ],
  "q" => [ "exDividendDate", "val" ],
  "d" => [ "dividendPerShare", "convert(val)" ],
  "y" => [ "dividendYield", "convert(val)" ],
  "j1" => [ "marketCap", "convert(val)" ],
  "t8" => [ "oneYearTargetPrice", "val" ],
  "e7" => [ "epsEstimateCurrentYear", "val" ],
  "e8" => [ "epsEstimateNextYear", "val" ],
  "e9" => [ "epsEstimateNextQuarter", "val" ],
  "r6" => [ "pricePerEPSEstimateCurrentYear", "val" ],
  "r7" => [ "pricePerEPSEstimateNextYear", "val" ],
  "r5" => [ "pegRatio", "val.to_f" ],
  "b4" => [ "bookValue", "val.to_f" ],
  "p6" => [ "pricePerBook", "val.to_f" ],
  "p5" => [ "pricePerSales", "val.to_f" ],
  "j4" => [ "ebitda", "val" ],
  "m3" => [ "movingAve50days", "val" ],
  "m7" => [ "movingAve50daysChangeFrom", "val" ],
  "m8" => [ "movingAve50daysChangePercentFrom", "val" ],
  "m4" => [ "movingAve200days", "val" ],
  "m5" => [ "movingAve200daysChangeFrom", "val" ],
  "m6" => [ "movingAve200daysChangePercentFrom", "val" ],
  "s1" => [ "sharesOwned", "val" ],
  "p1" => [ "pricePaid", "val" ],
  "c3" => [ "commission", "val" ],
  "v1" => [ "holdingsValue", "val" ],
  "w1" => [ "dayValueChange", "val" ],
  "g1" => [ "holdingsGainPercent", "val" ],
  "g4" => [ "holdingsGain", "val" ],
  "d2" => [ "tradeDate", "val" ],
  "g3" => [ "annualizedGain", "val" ],
  "l2" => [ "highLimit", "val" ],
  "l3" => [ "lowLimit", "val" ],
  "n4" => [ "notes", "val" ],
  "x" => [ "stockExchange", "val" ]
# This returns an integer like "1,000,000".
# The CVS parser gets confused by this
# so I've removed it for the time being.
#    "f6" => [ "floatShares", "val" ],
}
REALTIMEHASH =
{ 
  "s" => [ "symbol", "val" ],
  "n" => [ "name" , "val" ],
  "b2" => [ "ask", "val.to_f" ],
  "b3" => [ "bid", "val.to_f" ],
  "k2" => [ "change", "val" ],
  "k1" => [ "lastTradeWithTime", "val" ],
  "c6" => [ "changePoints", "val.to_f" ],
  "m2" => [ "dayRange", "val" ],
  "j3" => [ "marketCap", "convert(val)" ],
#     "v7" => [ "holdingsValue", "val" ],
#     "w4" => [ "dayValueChange", "val" ],
#     "g5" => [ "holdingsGainPercent", "val" ],
#     "g6" => [ "holdingsGain", "val" ],
#     "r2" => [ "pe", "val" ],
#     "c8" => [ "afterHoursChange", "val" ],
#     "i5" => [ "orderBook", "val" ],
}
DEFAULT_READ_TIMEOUT =
5

Class Method Summary collapse

Class Method Details

.get(symbols, format, timeout = DEFAULT_READ_TIMEOUT) ⇒ Object

Return a string containing the results retrieved from YahooFinance. If there was an error during the execution of this method, the string “” is returned. In practice, this means that no *Quote objects will be created (empty hashes will be returned from get_*_quotes methods).



164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/yahoofinance.rb', line 164

def YahooFinance.get( symbols, format, timeout=DEFAULT_READ_TIMEOUT )
  return "" if symbols == nil
  symbols = symbols.join( "," ) if symbols.class == Array
  symbols.strip!
  return "" if symbols == ""

  # Catch any exceptions that might occur here.  Possible exceptions
  # are the read_timeout and...
  #

  #
  # Don't catch any exceptions!  Just let them be thrown.
  #
  proxy = ENV['http_proxy'] ? URI.parse( ENV['http_proxy'] ) : OpenStruct.new
  Net::HTTP::Proxy( proxy.host, proxy.port, 
                    proxy.user, proxy.password ).start( "download.finance.yahoo.com",
                                                        80 ) do |http|
    http.read_timeout = timeout
    response = nil
    response = http.get( "/d/quotes.csv?s=#{symbols}&f=#{format}&e=.csv" )
    return "" if !response
    response.body.chomp
  end
end

.get_extended_quotes(symbols) ⇒ Object



214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/yahoofinance.rb', line 214

def YahooFinance.get_extended_quotes( symbols )
  csvquotes = YahooFinance::get( symbols, EXTENDEDHASH.keys.join )
  ret = Hash.new
  CSV.parse( csvquotes ) do |row|
    qt = ExtendedQuote.new( row )
    if block_given?
      yield qt
    end
    ret[qt.symbol] = qt
  end
  ret
end

.get_historical_quotes(symbol, startDate, endDate) ⇒ Object



437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
# File 'lib/yahoofinance.rb', line 437

def YahooFinance.get_historical_quotes( symbol, startDate, endDate )
  rows = []
  rowct = 0
  gotchunk = false

  #
  # Yahoo is only able to provide 200 data points at a time for Some
  # "international" markets.  We want to download all of the data
  # points between the startDate and endDate, regardless of how many
  # 200 data point chunks it comes in.  In this section we download
  # as many chunks as needed to get all of the data points.
  #
  begin
    gotchunk = false
    r = YahooFinance.retrieve_raw_historical_quotes( symbol, 
                                                     startDate, endDate )
    if block_given?
      # If we were given a block, yield to it for every row of data
      # downloaded.
      r.each { |row| yield row }
    else
      rows += r
    end

    # Is this a chunk?  
    if r.length == 200
      # Adjust the endDate for when we retrieve the next chunk.
      endDate = HistoricalQuote.parse_date_to_Date( r.last[0] ) - 1
      # Marke this as a chunk so do the download again with the new endDate.
      gotchunk = true
    end

  end while gotchunk

  if block_given?
    # If we have already yielded to every row, just return nil.
    return nil
  else
    # Otherwise return the big array of arrays.
    rows
  end

end

.get_historical_quotes_days(symbol, days, &block) ⇒ Object



481
482
483
484
485
# File 'lib/yahoofinance.rb', line 481

def YahooFinance.get_historical_quotes_days( symbol, days, &block )
  endDate = Date.today()
  startDate = Date.today() - days
  YahooFinance.get_historical_quotes( symbol, startDate, endDate, &block )
end

.get_HistoricalQuotes(symbol, startDate, endDate) ⇒ Object



487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
# File 'lib/yahoofinance.rb', line 487

def YahooFinance.get_HistoricalQuotes( symbol, startDate, endDate ) 
  ret = []
  YahooFinance.get_historical_quotes( symbol, startDate, endDate ) { |row|
    if block_given?
      yield HistoricalQuote.new( symbol, row )
    else
      ret << HistoricalQuote.new( symbol, row )
    end
  }
  if block_given?
    return nil
  else
    return ret
  end
end

.get_HistoricalQuotes_days(symbol, days, &block) ⇒ Object



503
504
505
506
507
# File 'lib/yahoofinance.rb', line 503

def YahooFinance.get_HistoricalQuotes_days( symbol, days, &block )
  endDate = Date.today()
  startDate = Date.today() - days
  YahooFinance.get_HistoricalQuotes( symbol, startDate, endDate, &block )
end

.get_quotes(quote_class, symbols, &block) ⇒ Object



189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/yahoofinance.rb', line 189

def YahooFinance.get_quotes( quote_class, symbols, &block )
  if quote_class == YahooFinance::StandardQuote
    return get_standard_quotes( symbols, &block )
  elsif quote_class == YahooFinance::ExtendedQuote
    return get_extended_quotes( symbols, &block )
  elsif quote_class == YahooFinance::RealTimeQuote
    return get_realtime_quotes( symbols, &block )
  else
    # Use the standard quote if the given quote_class was not recoginized.
    return get_standard_quotes( symbols, &block )
  end
end

.get_realtime_quotes(symbols) ⇒ Object



202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/yahoofinance.rb', line 202

def YahooFinance.get_realtime_quotes( symbols )
  csvquotes = YahooFinance::get( symbols, REALTIMEHASH.keys.join )
  ret = Hash.new
  CSV.parse( csvquotes ) do |row|
    qt = RealTimeQuote.new( row )
    if block_given?
      yield qt
    end
    ret[qt.symbol] = qt
  end
  ret
end

.get_standard_quotes(symbols) ⇒ Object



226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/yahoofinance.rb', line 226

def YahooFinance.get_standard_quotes( symbols )
  csvquotes = YahooFinance::get( symbols, STDHASH.keys.join )
  ret = Hash.new
  CSV.parse( csvquotes ) do |row|
    qt = StandardQuote.new( row )
    if block_given?
      yield qt
    end
    ret[qt.symbol] = qt
  end
  ret
end

.retrieve_raw_historical_quotes(symbol, startDate, endDate) ⇒ Object



402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
# File 'lib/yahoofinance.rb', line 402

def YahooFinance.retrieve_raw_historical_quotes( symbol, startDate, endDate )

  # Don't try to download anything if the starting date is before
  # the end date.
  return [] if startDate > endDate

  proxy = ENV['http_proxy'] ? URI.parse( ENV['http_proxy'] ) : OpenStruct.new
  Net::HTTP::Proxy( proxy.host, 
                    proxy.port, 
                    proxy.user, 
                    proxy.password ).start( "itable.finance.yahoo.com",
                                                        80 ) { |http|
    #Net::HTTP.start( "itable.finance.yahoo.com", 80 ) { |http|
    query = "/table.csv?s=#{symbol}&g=d" +
      "&a=#{startDate.month-1}&b=#{startDate.mday}&c=#{startDate.year}" + 
      "&d=#{endDate.month-1}&e=#{endDate.mday}&f=#{endDate.year.to_s}"
    #puts "#{query}"
    response = http.get( query )
    #puts "#{response.body}"
    body = response.body.chomp

    # If we don't get the first line like this, there was something
    # wrong with the data (404 error, new data formet, etc).
    return [] if body !~ /Date,Open,High,Low,Close,Volume,Adj Close/

    # Parse into an array of arrays.
    rows = CSV.parse( body )
    # Remove the first array since it is just the field headers.
    rows.shift
    #puts "#{rows.length}"

    return rows
  }
end