yahoo_finance_lib 0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9afd058d869ba5ada9ed3379d38e22eabe9efad5
4
+ data.tar.gz: 7e17d093ac49cf799c3a7b1c4aced1b3fbd2ff8a
5
+ SHA512:
6
+ metadata.gz: b6de89a90b27f851f7d85af6ca8ce0e08722022919c3d402eeb0d356b59feb847c2c30230d4cd70127cc4764df67bb672563f93a61bb4e279e5ab89479f4080d
7
+ data.tar.gz: 67127365095889c951d9250f2c3eda19a506b34f8b191e7e45f983f694cc56c9f996171f7fd2d9ea4caeb21d11fd0e8bbd2de1737641d0d13731f5a885eb9945
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in yahoo_finance.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Takis Mercouris
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # YahooFinanceStock
2
+
3
+ YahooFinance lib is a gem that fetches stock quotes, key statistics, company events, and analyst opinion from Yahoo (TM) API and financial HTML pages. The loading of stock quotes uses the excellent nas/yahoo_stock gem. This gem provides a 'unified' attribute based interface, and abstracts the source of the information, i.e. the user only needs to specify an attribute without the need to specify the page source of the attribute. This gem leverages to the highest extend possible the YahooStock (yahoo_stock_nas) gem for all the attributes provided by that gem, and fails over to HTML scraping when the data is unavailable there. Naturally, this has an implication on performance; YahooStock queries bundle 50 stocks in each query, whereas HTML scraping happens one page per stock at a time.
4
+
5
+ #####This gem is currently still in development. HTML scraping now supports many attributes from Key Statistics, supports earnings announcements from the Company Events page, and most attributes from Analyst Opinion -- additional pages will be added as I find time.
6
+
7
+ #####Caveat: HTML parsing is susceptible to HTML encoding of Yahoo Finance pages; when these pages change, parsing will break. Core Yahoo API attributes from YahooStock will entirely depend on YahooStock continuing to support the Yahoo APIs.
8
+
9
+ #####Use the code entirely at your own risk, and subject to copyright & trademark restrictions on Yahoo Finance(TM). I am quite sure that are additional Yahoo restrictions which you will need to investigate (http://finance.yahoo.com/badges/tos). From past experience I know you cannot redistribute Yahoo Finance data, or use it for commercial purposes.
10
+
11
+
12
+ ## Installation
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ gem 'yahoo_finance_lib'
17
+
18
+ And then execute:
19
+
20
+ $ bundle update
21
+
22
+ Or install it yourself as:
23
+
24
+ $ gem install yahoo_finance_lib
25
+
26
+ ## Usage
27
+
28
+ Note that
29
+
30
+ Example:
31
+ irb
32
+ require 'rubygems'
33
+ require 'yahoo_finance'
34
+
35
+ stock = YahooFinance::Stock.new(['AAPL', 'YHOO'], [:market_cap, :bid, :brokers_count, :upgrades_downgrades_history])
36
+ # look at available fields you could fetch with this library
37
+ stock.available_fields
38
+
39
+ stock.add_field(:last_trade_price_only)
40
+
41
+ results = stock.fetch
42
+ aapl_bid = results["AAPL"][:bid]
43
+ yhoo_last = results["YHOO"][:last_trade_price_only]
44
+
45
+ !-- ## Contributing
46
+
47
+ 1. Fork it
48
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
49
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
50
+ 4. Push to the branch (`git push origin my-new-feature`)
51
+ 5. Create new Pull Request -->
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,8 @@
1
+ require "yahoo_finance/version"
2
+ require 'nokogiri'
3
+ require 'open-uri'
4
+ require 'yahoo_finance/stock'
5
+ require 'yahoo_finance/key_statistics'
6
+ require 'yahoo_finance/company_events'
7
+ require 'yahoo_finance/analyst_opinion'
8
+
@@ -0,0 +1,88 @@
1
+ require 'open-uri'
2
+ require 'nokogiri'
3
+
4
+ module YahooFinance
5
+ module AnalystOpinion
6
+ AVL_KEY_STATS = {
7
+ :mean_recommendation_this_week => ['Mean Recommendation (this week):', "Mean Recommendation (this week):"],
8
+ :mean_recommendation_last_week => ['Mean Recommendation (last week):', "Mean Recommendation (last week):"],
9
+ :price_target_mean => ['Mean Target:', 'Mean Price Target'],
10
+ :price_target_median => ['Median Target:', 'Meadian Price Target'],
11
+ :price_target_high => ['High Target:', 'High Price Target'],
12
+ :price_target_low => ['Low Target:', 'Low Price Target'],
13
+ :brokers_count => ['No. of Brokers:', 'Number of Brokers with recommendations'],
14
+ :upgrades_downgrades_history => ["", 'Upgrades/Downgrades History'],
15
+ :recommendation_trends => ["", "Recommendation Trends - current, 1 month ago, 2 months ago, and 3 months ago"]
16
+ }
17
+ def AnalystOpinion.key_events_available
18
+ return AVL_KEY_STATS.keys;
19
+ end
20
+
21
+ class AnalystOpinionPage
22
+ @symbol
23
+
24
+ def initialize symbol
25
+ @symbol = symbol
26
+ end
27
+
28
+ def fetch
29
+ url = "http://finance.yahoo.com/q/ao?s=#{@symbol}"
30
+ open(url) do |stream|
31
+ @doc = Nokogiri::HTML(stream)
32
+ end
33
+ end
34
+
35
+ def value_for key_stat
36
+ begin
37
+ if key_stat == :upgrades_downgrades_history
38
+ ret = []
39
+ tbl = @doc.xpath("//th[text() = 'Upgrades & Downgrades History']")[0].parent.parent.parent.children[1].xpath("tr")
40
+ for i in 1..(tbl.size-1) do
41
+ r = {}
42
+ r[:date] = YahooFinance.parse_yahoo_field(tbl[i].children[0].text)
43
+ r[:firm] = tbl[i].children[1].text
44
+ r[:action] = tbl[i].children[2].text
45
+ r[:from] = tbl[i].children[3].text
46
+ r[:to] = tbl[i].children[4].text
47
+ ret << r
48
+ end
49
+ return ret
50
+ end
51
+ if key_stat == :recommendation_trends
52
+ ret = {}
53
+ tbl1 = @doc.xpath("//th[text() = 'Recommendation Trends']")[0].parent.parent
54
+ elem = tbl1.parent
55
+ sibling_idx = elem.children.index(tbl1)+1
56
+ sibling = elem.children[sibling_idx]
57
+ # now get all the subnodes...
58
+ table_rows = sibling.xpath(".//table//tr") # really contained within a subtable
59
+ for i in 1..(table_rows.size-1) do
60
+ r = table_rows[i]
61
+ header = r.xpath(".//th")
62
+ symbol = header[0].text.gsub(' ','').to_sym
63
+ cells = r.xpath(".//td")
64
+ ar = []
65
+ cells.each do |cell|
66
+ begin
67
+ ar << YahooFinance.parse_yahoo_field(cell.text)
68
+ rescue
69
+ puts " COULD NOT PARSE NUMERIC TREND CELL"
70
+ ar << " "
71
+ end
72
+ end
73
+ ret[symbol] = ar
74
+ # THIS IS NOT WORKING YET. THE LOGIC IS FALSE
75
+ end
76
+ return ret
77
+ end
78
+ if [:upgrades_downgrades_history, :recommendation_trends].include?(key_stat) == false
79
+ value = @doc.xpath("//td[text() = '#{AVL_KEY_STATS[key_stat][0]}']")[0].parent.children[1].text
80
+ return YahooFinance.parse_yahoo_field(value)
81
+ end
82
+ rescue
83
+ end
84
+ return nil
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,41 @@
1
+ require 'open-uri'
2
+ require 'nokogiri'
3
+
4
+ module YahooFinance
5
+ module CompanyEvents
6
+ COMPANY_EVENTS_STATS = {
7
+ :next_earnings_announcement_date => ['Earnings announcement', "Next earnings call date"]
8
+ }
9
+ def CompanyEvents.key_events_available
10
+ return YahooFinance::CompanyEvents::COMPANY_EVENTS_STATS.keys;
11
+ end
12
+
13
+ class CompanyEventsPage
14
+ @symbol
15
+
16
+ def initialize symbol
17
+ @symbol = symbol
18
+ end
19
+
20
+ def fetch
21
+ url = "http://finance.yahoo.com/q/ce?s=#{@symbol}"
22
+ open(url) do |stream|
23
+ @doc = Nokogiri::HTML(stream)
24
+ end
25
+ end
26
+
27
+ def value_for key_stat
28
+ begin
29
+ if key_stat == :next_earnings_announcement_date
30
+ if @doc.css('table#yfncsumtab//table.yfnc_datamodoutline1//table//td.yfnc_tabledata1')[1].text == "Earnings announcement"
31
+ return YahooFinance.parse_yahoo_field(@doc.css('table#yfncsumtab//table.yfnc_datamodoutline1//table//td.yfnc_tabledata1')[0].text)
32
+ end
33
+ end
34
+ rescue
35
+ end
36
+ return nil
37
+ end
38
+
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,122 @@
1
+ require 'open-uri'
2
+ require 'nokogiri'
3
+
4
+ module YahooFinance
5
+ module KeyStatistics
6
+ AVL_KEY_STATS = {
7
+ # String_to_parse, Description, Source Value format factor (some values are in % but dont't show %)
8
+ :market_cap => ['Market Cap ', "Market Cap, intraday"],
9
+ :enterprise_value => ['Enterprise Value ', "Enterprise Value"],
10
+ :trailing_pe => ['Trailing P\/E \(ttm\, intraday\)\:', "Trailing PE, trailing twelve months, intraday (based on price)"],
11
+ :forward_pe => ['Forward P\/E ', "Forward P/E"],
12
+ :peg_ratio => ['PEG Ratio ', "PEG Ratio (5 year expected (forward looking))"],
13
+ :price_to_sales_ttm => ['Price\/Sales \(ttm\)\:', "Price/Sales, trailing 12 months"],
14
+ :price_to_book_mrq => ['Price\/Book \(mrq\):', "Price/Book, most recent quarter"],
15
+ :roa_ttm => ['Return on Assets \(ttm\)\:', "Return on Assets, trailing twelve months"],
16
+ :roe_ttm => ['Return on Equity \(ttm\)\:', "Return on Equity, trailing twelve months"],
17
+ :book_value_per_share_mrq => ['Book Value Per Share \(mrq\):', "Book Value per share, most recent quarter"],
18
+ # Income Statement
19
+ :revenue_ttm => ['Revenue \(ttm\)\:', "Revenue, trailing twelve months"],
20
+ :revenue_per_share_ttm => ['Revenue Per Share \(ttm\)\:', "Revenue per share, trailing twelve months"],
21
+ :qtrly_revenue_growth_yoy => ['Qtrly Revenue Growth \(yoy\)\:', "Quarterly Revenue Growth, year on year"],
22
+ :diluted_eps_ttm => ['Diluted EPS \(ttm\)\:', "Diluted Earnings per share, trailing twelve months"],
23
+ :qtrly_earnings_growth_yoy => ['Qtrly Earnings Growth \(yoy\)\:', "Quarterly Earnings Growth, year on year"],
24
+ # Balance Sheet
25
+ :total_cash_mrq => ['Total Cash \(mrq\)\:', "Total Cash, most recent quarter"],
26
+ :total_cash_per_share_mrq => ['Total Cash Per Share \(mrq\)\:', "Total Cash per Share, most recent quarter"],
27
+ :total_debt_mrq => ['Total Debt (mrq):', "Total Debt, most recent quarter"],
28
+ :total_debt_to_equity_mrq => ['Total Debt/Equity \(mrq\)\:', "Total Debt/Equity (expressed as a percentage), most recent quarter", 0.01],
29
+ :current_ratio_mrq => ['Current Ratio \(mrq\)\:', "Total Current Assets / Total Current Liabilities, most recent quarter"],
30
+ :book_value_per_share_mrq => ['Book Value Per Share \(mrq\)\:', "Total Common Equity / Total Common Shares Outstanding, most recent quarter"],
31
+ # Trading additional info:
32
+ :beta => ['Beta\:', "Equity monthly beta relative to S&P500. Uses 36 months when available."],
33
+ # Share Statistics
34
+ :shares_outstanding => ['Shares Outstanding', "Number of outstanding shares"],
35
+ :pcnt_held_by_insiders => ['\% Held by Insiders', "Percent of shares held by insiders (returned as a fraction of 1)"],
36
+ :pcnt_held_by_institutions => ['\% Held by Institutions', "Percent of shares held by institutions (returned as a fraction of 1)"],
37
+ :pcnt_short_of_float => ['Short % of Float \(', "Percent of shares shorted relative to total (returned as a fraction of 1)"],
38
+ # Cash Flow
39
+ :operating_cash_flow_ttm => ['Operating Cash Flow \(ttm\)\:', "Operating cash flow, trailing twelve months"],
40
+ :levered_cash_flow_ttm => ['Levered Free Cash Flow \(ttm\)\:', "Levered Free Cash Flow, trailing 12 months: (EBIT + Interest Expense) x (1 x Tax Rate) + Depreciation & Amort., Total + Other Amortization + Capital Expenditure + Sale (Purchase) of Intangible assets - Change in Net Working Capital + Pref. Dividends Paid + Total Debt Repaid + Total Debt Issued + Repurchase of Preferred + Issuance of Preferred Stock"]
41
+ # ,
42
+ # # Profitability
43
+ # :Profit_Margin,
44
+ # :Operating_Margin,
45
+ # # Mgt Effectiveness
46
+ # :Return_on_Assets,
47
+ # :Return_on_Equity,
48
+ # # Income Stmt
49
+ # :Qtrly_Revenue_Growth_yoy,
50
+ # :EBITDA,
51
+ # :Qtrly_Earnings_Growth_yoy,
52
+ # # Balance Sheet
53
+ # :Total_Cash_mrq,
54
+ # :Total_Debt_mrq,
55
+ # :Total_Debt_to_Equity_mrq,
56
+ # :Book_Value_per_share_mrq,
57
+ # :Total_Assets_to_Total_Liabilities,
58
+ # # Shares
59
+ # :Shares_Outstanding,
60
+ # :Pcnt_Shares_Held_by_Insiders,
61
+ # :Pcnt_Shares_Held_by_Institutions,
62
+ # :Shares_Short,
63
+ # :Shares_Short_Date,
64
+ # :Short_Ratio,
65
+ # :Shares_Short_prior_month,
66
+ # # Dividends
67
+ # :Fwd_Annual_Dividend_Yield,
68
+ # :Trailing_Annual_Dividend_Yield,
69
+ # :Five_Year_Average_Dividend_Yield,
70
+ # :Next_Ex_Dividend_Date,
71
+ # :Next_Dividend_Date
72
+ }
73
+ def KeyStatistics.key_stats_available
74
+ return AVL_KEY_STATS.keys;
75
+ end
76
+
77
+ class StatsPage
78
+ @page_keys = []
79
+ @page_values = []
80
+ @symbol
81
+
82
+ def initialize symbol
83
+ @symbol = symbol
84
+ end
85
+
86
+ def fetch
87
+ url = "http://finance.yahoo.com/q/ks?s=#{@symbol}"
88
+ begin
89
+ tries ||= 3
90
+ open(url) do |stream|
91
+ doc = Nokogiri::HTML(stream)
92
+ # puts "DATA IS: #{data}"
93
+ @page_keys = doc.xpath('//td[@class="yfnc_tablehead1"]')
94
+ @page_values = doc.xpath('//td[@class="yfnc_tabledata1"]')
95
+ end
96
+ rescue Exception => e
97
+ puts "Failed to open and parse key stats for #{@symbol} because of #{e.message}. #{(tries > 0) ? 'retrying' : ''}"
98
+ if (tries -= 1) > 0
99
+ retry
100
+ end
101
+ end
102
+ end
103
+
104
+ def value_for key_stat
105
+ return nil if !@page_keys
106
+
107
+ matchstr = "#{AVL_KEY_STATS[key_stat][0]}"
108
+ @page_keys.each_with_index do |key, i|
109
+ if key.text.match(/^#{AVL_KEY_STATS[key_stat][0]}/)
110
+ value = YahooFinance.parse_yahoo_field @page_values[i].text.to_s
111
+ if AVL_KEY_STATS[key_stat][2]
112
+ value *= AVL_KEY_STATS[key_stat][2]
113
+ end
114
+ return value
115
+ end
116
+ end
117
+ return nil
118
+ end
119
+
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,232 @@
1
+
2
+ require 'yahoo_stock'
3
+ require 'yahoo_finance/key_statistics'
4
+ require 'yahoo_finance/company_events'
5
+ require 'yahoo_finance/analyst_opinion'
6
+
7
+
8
+ module YahooFinance
9
+ AVL_FIELDS = {
10
+ :YAHOO_STOCK_FIELDS => YahooStock::Quote.new(:stock_symbols => ['AAPL']).valid_parameters,
11
+ :KEY_STATISTICS => YahooFinance::KeyStatistics.key_stats_available,
12
+ :COMPANY_EVENTS => YahooFinance::CompanyEvents.key_events_available,
13
+ :ANALYST_OPINION => YahooFinance::AnalystOpinion.key_events_available
14
+ }
15
+ DRIVERS = {
16
+ :KEY_STATISTICS => YahooFinance::KeyStatistics::StatsPage,
17
+ :COMPANY_EVENTS => YahooFinance::CompanyEvents::CompanyEventsPage,
18
+ :ANALYST_OPINION => YahooFinance::AnalystOpinion::AnalystOpinionPage
19
+ }
20
+
21
+ # We are not interested parsing every type of field
22
+ # Key Fields: number (that may include , for 000s separator, NOT SUPPORTED YET), scaled number
23
+ # percentage, date
24
+ def YahooFinance.parse_yahoo_field aField
25
+ # we really need to oarse numbers, dates, and strings %%% STUB ALERT %%%%
26
+
27
+ aField.strip!
28
+
29
+ if aField.match /^([\-\+]{,1})([\d\,])*(\.[\d]+)*$/
30
+ # it's a number as far as we care; let's strip the commas....
31
+ aField.gsub! /\,/, ''
32
+ end
33
+
34
+ m=aField.match /^([\-\+]{0,1})([\d]*)(\.[\d]{1,})([KMB\%]{0,1})$/
35
+ if m # && (m.size == 3)
36
+ num = ((m[1] || "")+(m[2] || "")+(m[3] || "")).to_f
37
+
38
+ case m[4]
39
+ when "K" then
40
+ num *= 1000
41
+ # puts "NUM got K = #{num.to_s}"
42
+ when "M" then
43
+ num *= 1000000
44
+ # puts "NUM got B = #{num.to_s}"
45
+ when "B" then
46
+ num *= 1000000000
47
+ # puts "NUM got B = #{num.to_s}"
48
+ when '%' then
49
+ num /= 100
50
+ # puts "NUM got % = #{num.to_s}"
51
+ end
52
+ return num
53
+ end
54
+
55
+ if aField.match /^([\-\+]*[0-9]*)(\.[0-9]*)*$/
56
+ # simple number
57
+ return aField.to_f
58
+ end
59
+
60
+
61
+ m=aField.match /^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[\s]+([\d]{1,2})\,[\s]*([\d]{4})$/
62
+ if m
63
+ # got a date
64
+ dt = nil
65
+ begin
66
+ dt = Date.strptime aField, '%b %d, %Y'
67
+ rescue
68
+ end
69
+ return dt if dt
70
+ end
71
+
72
+ #else make an attempt to parse as date
73
+ dt = nil
74
+ begin
75
+ dt = Date.parse(aField)
76
+ rescue
77
+ end
78
+ return dt if dt
79
+
80
+ aField
81
+ end
82
+
83
+ class Stock
84
+ @@available_fields = (AVL_FIELDS[:YAHOO_STOCK_FIELDS] + AVL_FIELDS[:KEY_STATISTICS] + AVL_FIELDS[:COMPANY_EVENTS] + AVL_FIELDS[:ANALYST_OPINION])
85
+ @@insert_variable_delays = true
86
+ @@insert_variable_delay = 4
87
+ @@retry_times = 3
88
+ @@retry_variable_delay = 30
89
+ @symbols = []
90
+ @fields = []
91
+ @fields_hash = {}
92
+ @results_hash = {}
93
+
94
+ def self.insert_variable_delays
95
+ @@insert_variable_delays
96
+ end
97
+
98
+ def self.insert_variable_delays= trueFalse
99
+ @@insert_variable_delays = trueFalse
100
+ end
101
+
102
+ def self.insert_variable_delay
103
+ @@insert_variable_delay
104
+ end
105
+
106
+ def self.insert_variable_delay= aDelay
107
+ @@insert_variable_delay = aDelay
108
+ end
109
+
110
+ def initialize(symbols, fields = nil)
111
+ @symbols = symbols
112
+ @fields = []
113
+ if fields
114
+ fields.each do |aField|
115
+ add_field aField
116
+ end
117
+ end
118
+ end
119
+
120
+ def symbols
121
+ return @symbols
122
+ end
123
+
124
+ def fields
125
+ return @fields
126
+ end
127
+
128
+ def available_fields
129
+ @@available_fields
130
+ end
131
+
132
+ # NOTE -- UPDATE THIS WHEN YOU ADD A NEW PAGE %%
133
+ def available_fields_and_descriptions
134
+ ysh = {}; YahooFinance::AVL_FIELDS[:YAHOO_STOCK_FIELDS].each { |field| ysh[field] = "" }
135
+ ysh.merge(YahooFinance::KeyStatistics::AVL_KEY_STATS).merge(YahooFinance::CompanyEvents::AVL_KEY_STATS)
136
+ end
137
+
138
+ def add_symbol(aSymbol)
139
+ aSymbol = aSymbol.strip || ""
140
+ @symbols << aSymbol if (aSymbol != "" && (@symbols.include?(aSymbol) == false))
141
+ end
142
+
143
+ def add_field(aField)
144
+ if @@available_fields.include? aField
145
+ @fields << aField
146
+ end
147
+ end
148
+
149
+ def fetch
150
+ allocate_fields_to_connections
151
+ # initialize symbol rows
152
+ @results_hash = {}
153
+ @symbols.each do |aSymbol|
154
+ @results_hash[aSymbol.upcase] = {} # we do this to avoid problems with symbols misstyped with mixed upper/lower case
155
+ end
156
+
157
+ # First we fetch symbols under YAHOO_STOCK_FIELDS
158
+ if @fields_hash[:YAHOO_STOCK_FIELDS].size > 0
159
+ # puts "SYMBOLS ARE: #{@symbols.to_s} AND PARAMETERS: #{@fields_hash[:YAHOO_STOCK_FIELDS].to_s}"
160
+
161
+ # Yahoo Stock API has some limits, therefore, we need to chunk the symbols
162
+ @symbols.each_slice(50).to_a.each do |symbol_slice|
163
+
164
+ qt = YahooStock::Quote.new(:stock_symbols => symbol_slice)
165
+ qt.add_parameters(:symbol)
166
+ @fields_hash[:YAHOO_STOCK_FIELDS].each do |aField|
167
+ qt.add_parameters(aField)
168
+ end
169
+
170
+ yshs = qt.results(:to_hash).output
171
+ @fields_hash[:YAHOO_STOCK_FIELDS].each do |aField|
172
+ yshs.each do |aSymbolRow|
173
+ symbol = aSymbolRow[:symbol]
174
+ value = aSymbolRow[aField]
175
+ begin
176
+ @results_hash[symbol][aField] = YahooFinance.parse_yahoo_field(value)
177
+ rescue Exception => e
178
+ puts "Failed in symbol #{symbol.to_s} field #{aField.to_s} value #{(value || "NULL").to_s}"
179
+ puts "RESULT ROW: #{aSymbolRow.to_s}"
180
+ end
181
+ end
182
+ end
183
+ sleep(Random.new(Random.new_seed).rand*@@insert_variable_delay) if @@insert_variable_delays
184
+ end
185
+ end
186
+ # Then we fetch fields from KEY STATISTICS
187
+ DRIVERS.keys.each do |driver|
188
+ # puts "CHECKING #{driver.to_s}"
189
+ if @fields_hash[driver].size > 0
190
+ # puts "FETCHING FIELDS FOR #{driver.to_s}"
191
+ @symbols.each do |aSymbol|
192
+ tries = 0
193
+ begin
194
+ stp = DRIVERS[driver].new(aSymbol)
195
+ stp.fetch
196
+ @fields_hash[driver].each do |aField|
197
+ value = stp.value_for(aField) # this already parses the numbers
198
+ @results_hash[aSymbol][aField] = value
199
+ end
200
+ sleep(Random.new(Random.new_seed).rand*@@insert_variable_delay) if @@insert_variable_delays
201
+ rescue
202
+ tries += 1
203
+ sleep(@@retry_variable_delay)
204
+ retry if tries <= @@retry_times
205
+ end
206
+ end
207
+ end
208
+ end
209
+ @results_hash
210
+ end
211
+
212
+ def allocate_fields_to_connections
213
+ @fields_hash = {}
214
+ AVL_FIELDS.keys.each do |driver|
215
+ @fields_hash[driver] = []
216
+ end
217
+
218
+ @fields.each do |aField|
219
+ if AVL_FIELDS[:YAHOO_STOCK_FIELDS].include? aField
220
+ @fields_hash[:YAHOO_STOCK_FIELDS] << aField
221
+ else
222
+ DRIVERS.keys.each do |driver|
223
+ # puts "CHECKING DRIVER: #{driver} for field #{aField}"
224
+ if AVL_FIELDS[driver].include? aField
225
+ @fields_hash[driver] << aField
226
+ end
227
+ end
228
+ end
229
+ end
230
+ end
231
+ end
232
+ end
@@ -0,0 +1,3 @@
1
+ module YahooFinance
2
+ VERSION = "0.5"
3
+ end
@@ -0,0 +1,4 @@
1
+ $: << File.join(File.dirname(__FILE__), "/../lib")
2
+
3
+ require 'rspec'
4
+ require 'yahoo_finance_stock'
@@ -0,0 +1,20 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe YahooFinance::AnalystOpinion do
4
+ # before(:each) do
5
+ # @tsymbols = ['AAPL', 'DDD']
6
+ # @page = YahooFinance::Page.new(@tsymbols)
7
+ # end
8
+
9
+ describe "Analyst Opinion" do
10
+ it "available stats should include things like :brokers_count" do
11
+ YahooFinance::AnalystOpinion.key_events_available.include?(:brokers_count).should == true
12
+ end
13
+ it "should fetch a stat like :brokers_count for AAPL and should greater than zero" do
14
+ pg = YahooFinance::AnalystOpinion::AnalystOpinionPage.new('AAPL')
15
+ pg.fetch
16
+ aapl_brokers_count = pg.value_for :brokers_count
17
+ aapl_brokers_count.should > 0
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,26 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe YahooFinance::CompanyEvents do
4
+ # before(:each) do
5
+ # @tsymbols = ['AAPL', 'DDD']
6
+ # @page = YahooFinance::Page.new(@tsymbols)
7
+ # end
8
+
9
+ describe "Company Events" do
10
+ it "available stats should include things like :next_earnings_announcement_date" do
11
+ YahooFinance::CompanyEvents.key_events_available.include?(:next_earnings_announcement_date).should == true
12
+ end
13
+ it "should fetch a stat like :next_earnings_announcement_date for AAPL and should be either Date or String" do
14
+ pg = YahooFinance::CompanyEvents::CompanyEventsPage.new('AAPL')
15
+ pg.fetch
16
+ aapl_next_earnings_date = pg.value_for :next_earnings_announcement_date
17
+ case aapl_next_earnings_date.class.name
18
+ when "Date" then aapl_next_earnings_date.class.name.should == "Date"
19
+ when "String" then aapl_next_earnings_date.class.name.should == "String"
20
+ else
21
+ aapl_next_earnings_date.class.name.should == "Date" #force a failure
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,91 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe YahooFinance do
4
+ describe "Parsing Yahoo Fields" do
5
+ it "should parse an int correctly, such as \"3\"" do
6
+ YahooFinance.parse_yahoo_field("3").should == 3
7
+ end
8
+ it "should parse a float correctly, such as \"3.25\"" do
9
+ YahooFinance.parse_yahoo_field("3.25").should == 3.25
10
+ end
11
+ it "should parse a float with comma separated 000s correctly, such as \"3,245.25\"" do
12
+ YahooFinance.parse_yahoo_field("3,245.25").should == 3245.25
13
+ end
14
+ it "should parse a negative float with comma separated 000s correctly, such as \"-3,245.25\"" do
15
+ YahooFinance.parse_yahoo_field("-3,245.25").should == -3245.25
16
+ end
17
+ it "should parse a scaled float correctly, such as \"3.25K\"" do
18
+ YahooFinance.parse_yahoo_field("3.25K").should == 3250.0
19
+ end
20
+ it "should parse a scaled float correctly, such as \"3.25B\"" do
21
+ YahooFinance.parse_yahoo_field("3.25B").should == 3250000000.0
22
+ end
23
+ it "should parse a date, such as 'Feb 3, 2009'" do
24
+ dt = YahooFinance.parse_yahoo_field("Feb 3, 2009")
25
+ dt.class.name.should == "Date"
26
+ dt.month.should == 2
27
+ dt.day.should == 3
28
+ dt.year.should == 2009
29
+ end
30
+ it "should recognize that a string like 'hello 1, 2000' is a string and neither a number or a date" do
31
+ str = YahooFinance.parse_yahoo_field("hello 1, 2000")
32
+ str.class.name.should == "String"
33
+ str.should == "hello 1, 2000"
34
+ end
35
+ end
36
+ end
37
+
38
+ describe YahooFinance::Stock do
39
+ before(:each) do
40
+ @tsymbols = ['AAPL', 'DDD']
41
+ @stock = YahooFinance::Stock.new(@tsymbols)
42
+ end
43
+
44
+ describe "initialization" do
45
+ it "should remember the symbols it was initialized with" do
46
+ @stock.symbols.uniq.sort.should == @tsymbols
47
+ end
48
+
49
+ it "should allow you to add a symbol" do
50
+ @stock.add_symbol('TEST')
51
+ @stock.symbols.size == 3
52
+ # puts "#{@page.symbols.to_s}"
53
+ @stock.symbols.uniq.sort.should == @tsymbols
54
+ end
55
+ end
56
+
57
+ describe "execution - fetching yahoo_stock outputs" do
58
+ it "should return value for a field such as :p_e_ratio, and it should be of type float" do
59
+ @stock.add_field(:p_e_ratio)
60
+ results = @stock.fetch
61
+ # puts "GOT RESULTS: #{results[0][:p_e_ratio]}"
62
+ # let's parse the output
63
+ pe_ratio = results['AAPL'][:p_e_ratio]
64
+ case pe_ratio.class.name
65
+ when "Float" then pe_ratio.class.name.should == "Float"
66
+ when "String" then pe_ratio.should == "N/A"
67
+ else
68
+ pe_ratio.class.name.should == "Float" #force a failure
69
+ end
70
+ end
71
+ it "should return value for a field such as :bid and it should be of type float, or should return 'N/A'" do
72
+ @stock.add_field(:bid)
73
+ results = @stock.fetch
74
+ # puts "GOT RESULTS: #{results[0][:p_e_ratio]}"
75
+ # let's parse the output
76
+ bid = results['AAPL'][:bid]
77
+ case bid.class.name
78
+ when "Float" then bid.class.name.should == "Float"
79
+ when "String" then bid.should == "N/A"
80
+ else
81
+ bid.class.name.should == "Float" #force a failure
82
+ end
83
+ end
84
+ it "should return value for a field such as :brokers_count and it should be > 0 for AAPL" do
85
+ @stock.add_field(:brokers_count)
86
+ results = @stock.fetch
87
+ brokers_count = results['AAPL'][:brokers_count]
88
+ brokers_count.should > 0
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,21 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe YahooFinance::KeyStatistics do
4
+ # before(:each) do
5
+ # @tsymbols = ['AAPL', 'DDD']
6
+ # @page = YahooFinance::Page.new(@tsymbols)
7
+ # end
8
+
9
+ describe "key statistics" do
10
+ it "available stats should include things like total_debt_to_equity_mrq" do
11
+ YahooFinance::KeyStatistics::key_stats_available.include?(:price_to_sales_ttm).should == true
12
+ end
13
+ it "should fetch a key stat like :total_debt_to_equity_mrq for AAPL" do
14
+ pg = YahooFinance::KeyStatistics::StatsPage.new('AAPL')
15
+ pg.fetch
16
+ aapl_total_debt_to_equity = pg.value_for :total_debt_to_equity_mrq
17
+ aapl_total_debt_to_equity.class.name.should == "Float"
18
+ aapl_total_debt_to_equity.should >= 0.0
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'yahoo_finance/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "yahoo_finance_lib"
8
+ spec.version = YahooFinance::VERSION
9
+ spec.authors = ["Takis Mercouris"]
10
+ spec.email = ["tm@polymechanus.com"]
11
+ spec.description = %q{Unified library to stock quotes and various pages in yahoo finance, including key statistics, company events, and analyst opinion}
12
+ spec.summary = %q{Fetch stock information from yahoo finance API and HTML pages}
13
+ spec.homepage = "http://polymechanus.com/yahoo_finance-ruby-gem-tutorial/"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency "nas-yahoo_stock", "~> 1.0.8", ">= 1.0.8"
22
+ spec.add_runtime_dependency "nokogiri", "~> 1.6.1", ">= 1.6.1"
23
+ spec.add_development_dependency "bundler", "~> 1.3"
24
+ spec.add_development_dependency "rake", "~> 0"
25
+ spec.add_development_dependency "rspec", "~> 2.14.1", '>= 2.14.1'
26
+ end
metadata ADDED
@@ -0,0 +1,155 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: yahoo_finance_lib
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.5'
5
+ platform: ruby
6
+ authors:
7
+ - Takis Mercouris
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-08-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nas-yahoo_stock
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 1.0.8
20
+ - - '>='
21
+ - !ruby/object:Gem::Version
22
+ version: 1.0.8
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 1.0.8
30
+ - - '>='
31
+ - !ruby/object:Gem::Version
32
+ version: 1.0.8
33
+ - !ruby/object:Gem::Dependency
34
+ name: nokogiri
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ~>
38
+ - !ruby/object:Gem::Version
39
+ version: 1.6.1
40
+ - - '>='
41
+ - !ruby/object:Gem::Version
42
+ version: 1.6.1
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ~>
48
+ - !ruby/object:Gem::Version
49
+ version: 1.6.1
50
+ - - '>='
51
+ - !ruby/object:Gem::Version
52
+ version: 1.6.1
53
+ - !ruby/object:Gem::Dependency
54
+ name: bundler
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ~>
58
+ - !ruby/object:Gem::Version
59
+ version: '1.3'
60
+ type: :development
61
+ prerelease: false
62
+ version_requirements: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ~>
65
+ - !ruby/object:Gem::Version
66
+ version: '1.3'
67
+ - !ruby/object:Gem::Dependency
68
+ name: rake
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ~>
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ type: :development
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ~>
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ - !ruby/object:Gem::Dependency
82
+ name: rspec
83
+ requirement: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ~>
86
+ - !ruby/object:Gem::Version
87
+ version: 2.14.1
88
+ - - '>='
89
+ - !ruby/object:Gem::Version
90
+ version: 2.14.1
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ~>
96
+ - !ruby/object:Gem::Version
97
+ version: 2.14.1
98
+ - - '>='
99
+ - !ruby/object:Gem::Version
100
+ version: 2.14.1
101
+ description: Unified library to stock quotes and various pages in yahoo finance, including
102
+ key statistics, company events, and analyst opinion
103
+ email:
104
+ - tm@polymechanus.com
105
+ executables: []
106
+ extensions: []
107
+ extra_rdoc_files: []
108
+ files:
109
+ - .gitignore
110
+ - Gemfile
111
+ - LICENSE.txt
112
+ - README.md
113
+ - Rakefile
114
+ - lib/yahoo_finance.rb
115
+ - lib/yahoo_finance/analyst_opinion.rb
116
+ - lib/yahoo_finance/company_events.rb
117
+ - lib/yahoo_finance/key_statistics.rb
118
+ - lib/yahoo_finance/stock.rb
119
+ - lib/yahoo_finance/version.rb
120
+ - spec/spec_helper.rb
121
+ - spec/yahoo_analyst_opinion_spec.rb
122
+ - spec/yahoo_company_events_spec.rb
123
+ - spec/yahoo_finance_spec.rb
124
+ - spec/yahoo_key_stats_spec.rb
125
+ - yahoo_finance.gemspec
126
+ homepage: http://polymechanus.com/yahoo_finance-ruby-gem-tutorial/
127
+ licenses:
128
+ - MIT
129
+ metadata: {}
130
+ post_install_message:
131
+ rdoc_options: []
132
+ require_paths:
133
+ - lib
134
+ required_ruby_version: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ required_rubygems_version: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - '>='
142
+ - !ruby/object:Gem::Version
143
+ version: '0'
144
+ requirements: []
145
+ rubyforge_project:
146
+ rubygems_version: 2.4.1
147
+ signing_key:
148
+ specification_version: 4
149
+ summary: Fetch stock information from yahoo finance API and HTML pages
150
+ test_files:
151
+ - spec/spec_helper.rb
152
+ - spec/yahoo_analyst_opinion_spec.rb
153
+ - spec/yahoo_company_events_spec.rb
154
+ - spec/yahoo_finance_spec.rb
155
+ - spec/yahoo_key_stats_spec.rb