yahoo_finance_lib 0.7.1 → 0.7.9.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0d983b115acb96dd6eac3720ed79b1118f776444
4
- data.tar.gz: c83f71a08f569cfbb5c7a08043d834a4ec87d362
3
+ metadata.gz: cd71a533f1acc521ce2c0640edca3edf271d4839
4
+ data.tar.gz: b89733ecc5c7c3e96626b70efb7d83c6ba71318f
5
5
  SHA512:
6
- metadata.gz: c98697a3fe35489401b736aad6fedef4c677359656d904b0251530a74d40731d36af833f0ea63de2116478f0c2aa6553413b92eda37e733b210f22d3b5ab1c9d
7
- data.tar.gz: b5b93dc9c7752c345390a971754c6eaa6ca868f1c216cfbf4b9d38bfb347c12de7e2142a073c4bda07a1edbdac602e4671fd5c78caeeb5d015ac05192f760a4a
6
+ metadata.gz: f9c5122b36f66b8acd2d615c5824f458b9c71223f2827bd9036c3aa1f41f788a17e69c4edea0d0add7d7f1bfb6848bc49ef6707243de9a3f3f0b30ee9d5123b3
7
+ data.tar.gz: 8bd86c5c3eac252821d39074c624f53c0f1d2934d379a1f0cc920fa43baec4fa89e9696a0c6d8075cedb754e3a885733cf70df129df9db8c2b5a1e249d512b2b
data/History.md CHANGED
@@ -1,8 +1,7 @@
1
- # History of changes
1
+ ## History of changes
2
2
 
3
- ### Version 0.6
4
- Original release, supporting key statistics, company events, and analyst opinion
5
- ### Version 0.7
6
- Added company profile scraping :sector, and :industry
7
- ### Version 0.7.1
8
- Added :website attribute in company profile; also added YahooFinance::StockHistory abstraction to nas/yahoo_finance
3
+ Version | Description
4
+ ------- | -----------
5
+ 0.6 | Original release, supporting key statistics, company events, and analyst opinion
6
+ 0.7 | Added company profile scraping :sector, and :industry
7
+ 0.7.1 |Added :website attribute in company profile; also added YahooFinance::StockHistory abstraction to nas/yahoo_finance
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # YahooFinance library
2
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.
3
+ YahooFinance lib is a gem that fetches stock quotes, key statistics, company profile, company events, and analyst opinion from Yahoo (TM) API and financial HTML pages. Additionally, it has interfaces for historical data, and financial statements. 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
4
 
5
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
6
 
@@ -23,13 +23,20 @@ Or install it yourself as:
23
23
 
24
24
  $ gem install yahoo_finance_lib
25
25
 
26
+ ## Test Before You Use
27
+ Web pages get occasionally updated -- updates could break any or all of the classes in the gem. Do not use any classes that don't pass the RSpec tests. To test, run the rspec tests from the root directory:
28
+ ```
29
+ rspec -c -f d
30
+ ```
31
+
26
32
  ## Usage
27
33
 
28
34
 
29
35
 
30
36
  Example:
37
+
38
+ ```ruby
31
39
  irb
32
- require 'rubygems'
33
40
  require 'yahoo_finance'
34
41
 
35
42
  stock = YahooFinance::Stock.new(['AAPL', 'YHOO'], [:market_cap, :bid, :brokers_count, :upgrades_downgrades_history])
@@ -41,15 +48,35 @@ Example:
41
48
  results = stock.fetch
42
49
  aapl_bid = results["AAPL"][:bid]
43
50
  yhoo_last = results["YHOO"][:last_trade_price_only]
51
+ ```
44
52
 
45
53
  A simple interface using yahoo_stock to fetch history. It returns history as an array of hashes, e.g.
54
+
55
+ ```ruby
46
56
  irb
47
57
  require 'yahoo_finance'
48
58
  require 'date'
49
59
 
50
60
  start_date = Date.today - 30
51
- history = YahooFinance::StockHistory.new('AAPL', start_date)
61
+ history = YahooFinance::StockHistory.new('AAPL', start_date) # when you don't specify end date, end date = today - 1
52
62
  history.fetch
63
+ ```
64
+
65
+ A simple interface to scrape financial statements - Income Statement, Balance Sheet, and Cash Flow. It can fetch either quarterly or annual data, and it returns the value in a hash. e.g.
66
+
67
+ ```ruby
68
+ irb
69
+ require 'yahoo_finance'
70
+
71
+ income_stmt = YahooFinance::FinancialStatement::QuarterlyIncomeStatementPage.new 'YHOO'
72
+ result = income_stmt.fetch
73
+ most_recent_qtr = income_stmt.statement_periods[0]
74
+ available_fields = income_stmt.available_fields
75
+
76
+ puts "Net income for the most recent quarter ending on #{most_recent_qtr.to_s} is #{result[:net_income][0]}"
77
+ ```
78
+
79
+ The classes for the financial statement are: QuarterlyIncomeStatementPage, AnnualIncomeStatementPage, AnnualBalanceSheetPage, QuarterlyBalanceSheetPage, QuarterlyCashFlowStatementPage, AnnualCashFlowStatementPage
53
80
 
54
81
  !-- ## Contributing
55
82
 
@@ -6,4 +6,5 @@ require 'yahoo_finance/key_statistics'
6
6
  require 'yahoo_finance/company_events'
7
7
  require 'yahoo_finance/analyst_opinion'
8
8
  require 'yahoo_finance/company_profile'
9
+ require 'yahoo_finance/financial_statement'
9
10
 
@@ -19,9 +19,9 @@ module YahooFinance
19
19
  end
20
20
 
21
21
  class AnalystOpinionPage
22
- @symbol
22
+ attr_accessor :symbol
23
23
 
24
- def initialize symbol
24
+ def initialize symbol=nil
25
25
  @symbol = symbol
26
26
  end
27
27
 
@@ -11,9 +11,9 @@ module YahooFinance
11
11
  end
12
12
 
13
13
  class CompanyEventsPage
14
- @symbol
14
+ attr_accessor :symbol
15
15
 
16
- def initialize symbol
16
+ def initialize symbol=nil
17
17
  @symbol = symbol
18
18
  end
19
19
 
@@ -38,4 +38,4 @@ module YahooFinance
38
38
 
39
39
  end
40
40
  end
41
- end
41
+ end
@@ -6,16 +6,18 @@ module YahooFinance
6
6
  COMPANY_PROFILE_STATS = {
7
7
  :sector => ['Sector:', "Sector classification"],
8
8
  :industry => ['Industry:', 'Industry classification'],
9
- :website => ['Website:', 'Company website']
9
+ :website => ['Website:', 'Company website'],
10
+ :symbol => ['Symbol', 'Company Exchange Symbol'],
11
+ :company_name => ['Company Name', 'Company Name']
10
12
  }
11
13
  def CompanyProfile.key_events_available
12
14
  return YahooFinance::CompanyProfile::COMPANY_PROFILE_STATS.keys;
13
15
  end
14
16
 
15
17
  class CompanyProfilePage
16
- @symbol
18
+ attr_accessor :symbol
17
19
 
18
- def initialize symbol
20
+ def initialize symbol=nil
19
21
  @symbol = symbol
20
22
  end
21
23
 
@@ -34,6 +36,9 @@ module YahooFinance
34
36
  if key_stat == :website
35
37
  r = @doc.xpath("//td[contains(., \"#{YahooFinance::CompanyProfile::COMPANY_PROFILE_STATS[key_stat][0]}\")]")
36
38
  return r.xpath("./a[contains(., 'http:')]").text
39
+ elsif key_stat == :company_name
40
+ name = @doc.xpath('//td[@class="yfnc_modtitlew1"]//b')[0].text
41
+ return name
37
42
  elsif CompanyProfile::key_events_available.include? key_stat
38
43
  begin
39
44
  value = @doc.xpath("//td[text() = \"#{YahooFinance::CompanyProfile::COMPANY_PROFILE_STATS[key_stat][0]}\"]")[0].parent.children[1].text
@@ -0,0 +1,324 @@
1
+ require 'open-uri'
2
+ require 'nokogiri'
3
+ require 'date'
4
+
5
+ module YahooFinance
6
+ module FinancialStatement
7
+ INCOME_STMT_FIELDS = {
8
+ :statement_periods => ['Period Ending', "Ending date per period"],
9
+ :total_revenue => ['Total Revenue', "Total Revenue (per period)"],
10
+ :cost_of_revenue => ['Cost of Revenue', 'Cost of Revenue (per period)'],
11
+ :gross_profit => ['Gross Profit', 'Gross profit (per period)'],
12
+ :research_development => ['Research Development', 'Research and Development (per period)'],
13
+ :selling_general_and_administrative => ['Selling General and Administrative', 'Selling General and Administrative (per period)'],
14
+ :non_recurring => ['Non Recurring', 'Non Recurring (per period)'],
15
+ :other_expenses => ['Others', 'Other expenses (per period)'],
16
+ :total_operating_expenses => ['Total Operating Expenses', 'Total Operating Expenses (per period)'],
17
+ :operating_income_or_loss => ['Operating Income or Loss', 'Operating Income or Loss (per period)'],
18
+ :total_other_income_expenses_net => ['Total Other Income/Expenses Net', 'Total Other Income/Expenses Net (per period)'],
19
+ :EBIT => ['Earnings Before Interest And Taxes', ' Earnings Before Interest And Taxes (per period)'],
20
+ :interest_expense => ['Interest Expense', 'Interest Expense (per period)'],
21
+ :income_before_tax => ['Income Before Tax', 'Income Before Tax (per period)'],
22
+ :income_tax_expense => ['Income Tax Expense', 'Income Tax Expense (per period)'],
23
+ :minority_interest => ['Minority Interest', 'Minority Interest (per period)'],
24
+ :net_income_from_continuing_operations => ['Net Income From Continuing Ops', 'Net Income From Continuing Ops (per period)'],
25
+ :discontinued_operations => ['Discontinued Operations', 'Discontinued Operations (per period)'],
26
+ :extraordinary_items =>['Extraordinary Items', 'Extraordinary Items (per period)'],
27
+ :effect_of_accounting_changes => ['Effect Of Accounting Changes', 'Effect Of Accounting Changes (per period)'],
28
+ :other_items => ['Other Items', 'Other Items (per period)'],
29
+ :net_income => ['Net Income', 'Net Income (per period)'],
30
+ :preferred_stock_and_other_adjustments => ['Preferred Stock And Other Adjustments', 'Preferred Stock And Other Adjustments (per period)'],
31
+ :net_income_applicable_to_common_shares => ['Net Income Applicable To Common Shares', 'Net Income Applicable To Common Shares (per period)']
32
+ }
33
+
34
+ BALANCE_SHEET_FIELDS = {
35
+ :statement_periods => ['Period Ending', "Ending date per period"],
36
+ :cash_and_cash_equivalents => ['Cash And Cash Equivalents', 'Cash And Cash Equivalents'],
37
+ :short_term_investments => ['Short Term Investments', ' Short Term Investments'],
38
+ :net_receivables => ['Net Receivables', 'Net Receivables'],
39
+ :inventory => ['Inventory', 'Inventory'],
40
+ :other_current_assets => ['Other Current Assets', 'Other Current Assets'],
41
+ :total_current_assets => ['Total Current Assets', 'Total Current Assets'],
42
+ :long_term_investments => ['Long Term Investments', 'Long Term Investments'],
43
+ :property_plant_and_equipment => ['Property Plant and Equipment', 'Property Plant and Equipment'],
44
+ :goodwill => ['Goodwill', 'Goodwill'],
45
+ :intagible_assets => ['Intangible Assets', 'Intangible Assets'],
46
+ :accumulated_amortization => ['Accumulated Amortization', 'Accumulated Amortization'],
47
+ :other_assets => ['Other Assets', 'Other Assets'],
48
+ :deferred_long_term_asset_charges => ['Deferred Long Term Asset Charges', 'Deferred Long Term Asset Charges'],
49
+ :total_assets => ['Total Assets', 'Total Assets'],
50
+ :accounts_payable => ['Accounts Payable', 'Accounts Payable'],
51
+ :short_current_long_term_debt => ['Short/Current Long Term Debt', 'Short/Current Long Term Debt'],
52
+ :other_current_liabilities => ['Other Current Liabilities', 'Other Current Liabilities'],
53
+ :total_current_liabilities => ['Total Current Liabilities', 'Total Current Liabilities'],
54
+ :long_term_debt => ['Long Term Debt', 'Long Term Debt'],
55
+ :other_liabilities => ['Other Liabilities', 'Other Liabilities'],
56
+ :deferred_long_term_liability_charges => ['Deferred Long Term Liability Charges', 'Deferred Long Term Liability Charges'],
57
+ :minority_interest => ['Minority Interest', 'Minority Interest'],
58
+ :negative_goodwill => ['Negative Goodwill', 'Negative Goodwill'],
59
+ :total_liabilities => ['Total Liabilities', 'Total Liabilities'],
60
+ :misc_stocks_options_warrants => ['Misc Stocks Options Warrants', 'Misc Stocks Options Warrants'],
61
+ :redeemable_preferred_stock => ['Redeemable Preferred Stock', 'Redeemable Preferred Stock'],
62
+ :preferred_stock => ['Preferred Stock', 'Preferred Stock'],
63
+ :common_stock => ['Common Stock', 'Common Stock'],
64
+ :retained_earnings => ['Retained Earnings', 'Retained Earnings'],
65
+ :treasury_stock => ['Treasury Stock', 'Treasury Stock'],
66
+ :capital_surplus => ['Capital Surplus', 'Capital Surplus'],
67
+ :other_stockholder_equity => ['Other Stockholder Equity', 'Other Stockholder Equity'],
68
+ :total_stockholder_equity => ['Total Stockholder Equity', 'Total Stockholder Equity'],
69
+ :net_tangible_assets => ['Net Tangible Assets', 'Net Tangible Assets']
70
+ }
71
+
72
+ CASH_FLOW_STMT_FIELDS = {
73
+ :statement_periods => ['Period Ending', "Ending date per period"],
74
+ :net_income => ['Net Income', 'Net Income'],
75
+ :depreciation => ['Depreciation', 'Depreciation'],
76
+ :adjustments_to_net_income => ['Adjustments To Net Income','Adjustments To Net Income'],
77
+ :changes_in_accounts_receivables => ['Changes In Accounts Receivables', 'Changes In Accounts Receivables'],
78
+ :changes_in_liabilities => ['Changes In Liabilities','Changes In Liabilities'],
79
+ :changes_in_inventories => ['Changes In Inventories', 'Changes In Inventories'],
80
+ :changes_in_other_operating_activities => ['Changes In Other Operating Activities', 'Changes In Other Operating Activities'],
81
+ :total_cash_flow_from_operating_activities => ['Total Cash Flow From Operating Activities', 'Total Cash Flow From Operating Activities'],
82
+ :capital_expenditures => ['Capital Expenditures', 'Capital Expenditures'],
83
+ :investments => ['Investments', 'Investments'],
84
+ :other_cash_flows_from_investing_activities => ['Other Cash flows from Investing Activities', 'Other Cash flows from Investing Activities'],
85
+ :total_cash_flows_from_investing_activities => ['Total Cash Flows From Investing Activities', 'Total Cash Flows From Investing Activities'],
86
+ :dividends_paid => ['Dividends Paid', 'Dividends Paid'],
87
+ :sale_purchase_of_stock => ['Sale Purchase of Stock', 'Sale Purchase of Stock'],
88
+ :net_borrowings => ['Net Borrowings', 'Net Borrowings'],
89
+ :other_cash_flows_from_financing_activities => ['Other Cash Flows from Financing Activities', 'Other Cash Flows from Financing Activities'],
90
+ :total_cash_flows_from_financing_activities => ['Total Cash Flows From Financing Activities', 'Total Cash Flows From Financing Activities'],
91
+ :effect_of_exchange_rate_changes => ['Effect Of Exchange Rate Changes', 'Effect Of Exchange Rate Changes'],
92
+ :change_in_cash_and_cash_equivalents => ['Change In Cash and Cash Equivalents', 'Change In Cash and Cash Equivalents']
93
+ }
94
+
95
+
96
+ # Quarterly Data
97
+ class FinancialStatementPage
98
+ attr_accessor :symbol
99
+
100
+ def initialize symbol=nil
101
+ @symbol = symbol
102
+ @term = :quarterly
103
+ @periods = []
104
+ @number_multiplier = 1000.00 # all numbers in the statements are in thousands... I need to verify whether I need to parse that
105
+ @financial_statement = {}
106
+ @data_columns = 4
107
+ @type="is"
108
+ @field_defs = YahooFinance::FinancialStatement::INCOME_STMT_FIELDS
109
+ end
110
+
111
+ def term= aValue
112
+ if term == :quarterly || term == :annual
113
+ @term = term
114
+ end
115
+ end
116
+
117
+ def available_fields
118
+ return @field_defs.keys;
119
+ end
120
+
121
+ def fetch
122
+ url = "http://finance.yahoo.com/q/#{@type}?s=#{@symbol}&#{@term.to_s}"
123
+ doc = nil
124
+ tries = 0
125
+ begin
126
+ # SINCE YAHOO HAS SOME TIMES THE TENDENCY TO RETURN AN OOPS PAGE RATHER THAN A HTTP FAILURE
127
+ # WE ARE USING A TEST OF A HEADER FIELD AS A CONDITION OF A CORRECTLY FETCHED PAGE
128
+ fetched_ok = false
129
+ fetch_tries = 1
130
+ while (fetched_ok == false) && (fetch_tries <= 3)
131
+ open(url) do |stream|
132
+ doc = Nokogiri::HTML(stream)
133
+ end
134
+ search_field = ((@type == "is") ? "Income Statement" : ((@type == "bs") ? "Balance Sheet" : "Cash Flow") )
135
+ fetched_test = doc.xpath("//b[text()[contains(., '" + search_field +"')]]")
136
+ if fetched_test && fetched_test.size > 0
137
+ # THERE IS STILL A CHANCE THAT YAHOO WILL NOT HAVE THE DATA FOR THIS STOCK, IN WHICH CASE, IT WILL DISPLAY
138
+ # a PAGE WITH A STATEMENT LIKE: 'There is no Income Statement data available for AMBR.' --- WE SEARCH FOR THAT AND RETURN NIL IF FOUND
139
+ nodata_path_search_expr = "//p[text()[contains(., 'There is no "+ search_field + " data available for #{symbol}.')]]"
140
+ no_statement = doc.xpath(nodata_path_search_expr)
141
+ # puts "EXPR: #{nodata_path_search_expr} NO STATEMENT: #{no_statement} AND SIZE: #{no_statement.size}"
142
+ if (no_statement && (no_statement.size > 0))
143
+ puts "WARNING: FOUND There is no #{search_field} data available for #{symbol}. -- RETURNING NIL"
144
+ return nil # YAHOO DOESN'T HAVE THE DATA...
145
+ else
146
+ fetched_ok = true
147
+ end
148
+ else
149
+ puts "Fetching URL #{url} again - previous one failed. Trying #{fetch_tries} of 3"
150
+ sleep (1)
151
+ fetch_tries += 1
152
+ end
153
+ end
154
+ rescue
155
+ tries += 1
156
+ if fetch_tries <= 3
157
+ puts "Exception: #{$!.inspect} -- Retrying symbol #{@symbol} time: #{fetch_tries.to_s} (total 3)"
158
+ sleep(2)
159
+ retry
160
+ end
161
+
162
+ end
163
+ #
164
+ # %%% OK, WE MUST MAKE THIS MORE ROBUST TO HANDLE FEWER REPORTING PERIODS, e.g. ACT Actavis has 3 quarters reported
165
+ #
166
+ # let's initialize periods: by finding 'Period Endinng
167
+ got_periods = true
168
+ begin
169
+ row = doc.xpath("//span[text() = 'Period Ending']")[0].parent.parent.parent
170
+ period = []
171
+ 1.upto(@data_columns) do |i|
172
+ begin
173
+ pd = Date.parse(row.children[i].text)
174
+ @periods << pd
175
+ rescue
176
+ break; # end of the line here
177
+ end
178
+ end
179
+ @financial_statement[:statement_periods] = @periods
180
+ rescue
181
+ puts "Exception: #{$!.inspect} -- Skipping symbol #{@symbol} URL: #{url}"
182
+ # puts "DOC: #{doc}"
183
+ got_periods = false
184
+ end
185
+
186
+ return nil if !got_periods
187
+
188
+ # YahooFinance.parse_financial_statement_field field, multiplier
189
+ # let's fetch everything here & store in the income statement
190
+ @field_defs.keys.each do |incst_key|
191
+ next if incst_key == :statement_periods
192
+ path_expr = "//td[text()[contains(., '" + @field_defs[incst_key][0] + "')]]"
193
+ row = nil
194
+ elem = doc.xpath(path_expr)
195
+ index = 1
196
+ regex = "\\s\*#{@field_defs[incst_key][0]}\\s\*"
197
+ if elem && elem.size == 0
198
+ # this is because we have a strong type; let's try again
199
+ path_expr = "//td/strong[text()[contains(., '" + @field_defs[incst_key][0] +"')]]"
200
+ elem = doc.xpath(path_expr)
201
+ if elem.size > 0
202
+ elem.each do |e|
203
+ if e.text.match regex
204
+ # we found it
205
+ row = e.parent.parent
206
+ end
207
+ end
208
+ if !row
209
+ # wasn't found
210
+ puts "#{@field_defs[incst_key][0]} was not found!"
211
+ next
212
+ end
213
+ index = row.children.index(elem[0].parent)
214
+ end
215
+ else
216
+ elem.each do |e|
217
+ # puts "Checking #{e}: class #{e.text.class.name} with text <<#{e.text}>> and regex #{regex}"
218
+ if e.text.match regex
219
+ # we found it
220
+ row = e.parent
221
+ break
222
+ end
223
+ end
224
+ if !row
225
+ # wasn't found
226
+ puts "#{@field_defs[incst_key][0]} was not found!"
227
+ next
228
+ end
229
+ index = row.children.index(elem[0])
230
+ end
231
+ if elem && elem.size > 0
232
+ # puts "\tFOUND!!!! PATH EXPRESSION IS #{path_expr} "
233
+ # puts "\t\tROW: #{row}"
234
+ rstbl = []
235
+ 1.upto(@periods.size) do |i|
236
+ field = row.children[index+i].text.tr('^[0-9\,\.\-\(\)]', '')
237
+ rs = YahooFinance.parse_financial_statement_field(field, @number_multiplier)
238
+ rstbl << rs
239
+ end
240
+
241
+ @financial_statement[incst_key] = rstbl
242
+ # puts "\tPARSED #{incst_key}"
243
+ else
244
+ puts "\tNOT FOUND!!!! #{incst_key}"
245
+ end
246
+ end
247
+ @financial_statement
248
+ end
249
+
250
+ def statement_periods
251
+ @periods
252
+ end
253
+
254
+ def value_for key_stat
255
+ begin
256
+ return @financial_statement[key_stat]
257
+ rescue
258
+ end
259
+ return nil
260
+ end
261
+ end
262
+
263
+ class QuarterlyIncomeStatementPage < FinancialStatementPage
264
+ def initialize symbol = nil
265
+ super symbol
266
+ @term = :quarterly
267
+ @data_columns = 4
268
+ @type="is"
269
+ @field_defs = YahooFinance::FinancialStatement::INCOME_STMT_FIELDS
270
+ end
271
+ end
272
+
273
+ class AnnualIncomeStatementPage < FinancialStatementPage
274
+ def initialize symbol = nil
275
+ super symbol
276
+ @term = :annual
277
+ @data_columns = 3
278
+ @type="is"
279
+ @field_defs = YahooFinance::FinancialStatement::INCOME_STMT_FIELDS
280
+ end
281
+ end
282
+
283
+ class QuarterlyBalanceSheetPage < FinancialStatementPage
284
+ def initialize symbol = nil
285
+ super symbol
286
+ @term = :quarterly
287
+ @data_columns = 4
288
+ @type="bs"
289
+ @field_defs = YahooFinance::FinancialStatement::BALANCE_SHEET_FIELDS
290
+ end
291
+ end
292
+
293
+ class AnnualBalanceSheetPage < FinancialStatementPage
294
+ def initialize symbol = nil
295
+ super symbol
296
+ @term = :annual
297
+ @data_columns = 3
298
+ @type="bs"
299
+ @field_defs = YahooFinance::FinancialStatement::BALANCE_SHEET_FIELDS
300
+ end
301
+ end
302
+
303
+ class QuarterlyCashFlowStatementPage < FinancialStatementPage
304
+ def initialize symbol = nil
305
+ super symbol
306
+ @term = :quarterly
307
+ @data_columns = 4
308
+ @type="cf"
309
+ @field_defs = YahooFinance::FinancialStatement::CASH_FLOW_STMT_FIELDS
310
+ end
311
+ end
312
+
313
+ class AnnualCashFlowStatementPage < FinancialStatementPage
314
+ def initialize symbol = nil
315
+ super symbol
316
+ @term = :annual
317
+ @data_columns = 3
318
+ @type="cf"
319
+ @field_defs = YahooFinance::FinancialStatement::CASH_FLOW_STMT_FIELDS
320
+ end
321
+ end
322
+
323
+ end
324
+ end
@@ -75,11 +75,12 @@ module YahooFinance
75
75
  end
76
76
 
77
77
  class StatsPage
78
+ attr_accessor :symbol
78
79
  @page_keys = []
79
80
  @page_values = []
80
- @symbol
81
+
81
82
 
82
- def initialize symbol
83
+ def initialize symbol=nil
83
84
  @symbol = symbol
84
85
  end
85
86
 
@@ -72,6 +72,18 @@ module YahooFinance
72
72
  return dt if dt
73
73
  end
74
74
 
75
+ m=aField.match /^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[\s]+([\d]{1,2})\s*\-\s*(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[\s]+([\d]{1,2})\,[\s]*([\d]{4})/
76
+ if m && (m.size == 6)
77
+ # we found it, and it is a date range
78
+ dt1 = dt2 = nil
79
+ begin
80
+ dt1 = Date.strptime "#{m[1]} #{m[2]}, #{m[5]}", '%b %d, %Y'
81
+ dt2 = Date.strptime "#{m[3]} #{m[4]}, #{m[5]}", '%b %d, %Y'
82
+ rescue
83
+ end
84
+ return [dt1, dt2]
85
+ end
86
+
75
87
  #else make an attempt to parse as date
76
88
  dt = nil
77
89
  begin
@@ -83,16 +95,42 @@ module YahooFinance
83
95
  aField
84
96
  end
85
97
 
98
+ # this really parses numeric fields only
99
+ def YahooFinance.parse_financial_statement_field aField, multiplier = 1000.0
100
+ aField = aField.strip
101
+
102
+ multiplier = 1000.00 if !multiplier # just in case a nil is forced...
103
+
104
+ if aField.match /^([\d\,])*(\.[\d]+)*$/
105
+ # it's a number as far as we care; let's strip the commas....
106
+ return aField.tr('\,', '').to_f * multiplier
107
+ end
108
+
109
+ # process the accounting negative field -- parentheses
110
+ m = aField.match /^\(([\d\,])*(\.[\d]+)*\)$/
111
+ if m
112
+ # it's a number as far as we care; let's strip the commas....
113
+ aField = aField.tr('\(\)\,', '')
114
+ return aField.to_f * multiplier * -1.0
115
+ end
116
+
117
+ # anything else becomes zero by definition
118
+ return 0.0
119
+
120
+ end
121
+
86
122
  class Stock
87
- @@available_fields = (AVL_FIELDS[:YAHOO_STOCK_FIELDS] + AVL_FIELDS[:KEY_STATISTICS] + AVL_FIELDS[:COMPANY_EVENTS] + AVL_FIELDS[:ANALYST_OPINION])
88
- @@insert_variable_delays = true
89
- @@insert_variable_delay = 4
123
+ attr_reader :results_hash, :print_progress
124
+ @@available_fields = (AVL_FIELDS[:YAHOO_STOCK_FIELDS] + AVL_FIELDS[:KEY_STATISTICS] + AVL_FIELDS[:COMPANY_EVENTS] + AVL_FIELDS[:ANALYST_OPINION] + AVL_FIELDS[:COMPANY_PROFILE])
125
+ @@insert_variable_delays = false
126
+ @@insert_variable_delay = 2
90
127
  @@retry_times = 3
91
128
  @@retry_variable_delay = 30
92
129
  @symbols = []
93
130
  @fields = []
94
131
  @fields_hash = {}
95
132
  @results_hash = {}
133
+ @print_progress = false
96
134
 
97
135
  def self.insert_variable_delays
98
136
  @@insert_variable_delays
@@ -188,13 +226,16 @@ module YahooFinance
188
226
  end
189
227
  # Then we fetch fields from KEY STATISTICS
190
228
  DRIVERS.keys.each do |driver|
191
- # puts "CHECKING #{driver.to_s}"
192
229
  if @fields_hash[driver].size > 0
193
- # puts "FETCHING FIELDS FOR #{driver.to_s}"
230
+ puts "FETCHING FIELDS FOR #{driver.to_s}" if @print_progress
231
+ stp = DRIVERS[driver].new # will overwrite symbol below
194
232
  @symbols.each do |aSymbol|
195
233
  tries = 0
234
+ now = Time.now
235
+ stp.symbol = aSymbol
236
+ puts "#{now.hour}:#{now.min}:#{now.sec}\tWorking on symbol #{aSymbol}" if @print_progress
196
237
  begin
197
- stp = DRIVERS[driver].new(aSymbol)
238
+ # stp = DRIVERS[driver].new(aSymbol) # will overwrite symbol below
198
239
  stp.fetch
199
240
  @fields_hash[driver].each do |aField|
200
241
  value = stp.value_for(aField) # this already parses the numbers
@@ -203,12 +244,17 @@ module YahooFinance
203
244
  sleep(Random.new(Random.new_seed).rand*@@insert_variable_delay) if @@insert_variable_delays
204
245
  rescue
205
246
  tries += 1
206
- sleep(@@retry_variable_delay)
207
- retry if tries <= @@retry_times
247
+ puts "Exception: #{$!.inspect} -- Retrying symbol #{aSymbol} time: #{tries.to_s}"
248
+ if tries <= @@retry_times
249
+ sleep(@@retry_variable_delay)
250
+ retry
251
+ end
208
252
  end
209
253
  end
210
254
  end
255
+ puts "DONE witn #{driver.to_s}" if @print_progress
211
256
  end
257
+ # puts "RIGHT BEFORE I RETURN THE BIG HASH: #{@results_hash}"
212
258
  @results_hash
213
259
  end
214
260
 
@@ -230,7 +276,7 @@ module YahooFinance
230
276
  end
231
277
  end
232
278
  end
233
- end
279
+ end
234
280
  end
235
281
 
236
282
  class StockHistory
@@ -1,3 +1,3 @@
1
1
  module YahooFinance
2
- VERSION = "0.7.1"
2
+ VERSION = "0.7.9.8"
3
3
  end
@@ -10,13 +10,14 @@ describe YahooFinance::CompanyEvents do
10
10
  it "available stats should include things like :next_earnings_announcement_date" do
11
11
  YahooFinance::CompanyEvents.key_events_available.include?(:next_earnings_announcement_date).should == true
12
12
  end
13
- it "should fetch a stat like :next_earnings_announcement_date for AAPL and should be either Date or String" do
13
+ it "should fetch a stat like :next_earnings_announcement_date for AAPL and should be either Date, and Array of two dates, or String" do
14
14
  pg = YahooFinance::CompanyEvents::CompanyEventsPage.new('AAPL')
15
15
  pg.fetch
16
16
  aapl_next_earnings_date = pg.value_for :next_earnings_announcement_date
17
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"
18
+ when "Date" then aapl_next_earnings_date.class.name.should == "Date"
19
+ when "Array" then aapl_next_earnings_date.size.should == 2 && aapl_next_earnings_date[0].class.name.should == "Date" && aapl_next_earnings_date[1].class.name.should == "Date"
20
+ when "String" then aapl_next_earnings_date.class.name.should == "String"
20
21
  else
21
22
  aapl_next_earnings_date.class.name.should == "Date" #force a failure
22
23
  end
@@ -0,0 +1,124 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ # really more like a quick sample, more concerned about connectivity -- a better test needs to be developed to exhaustively
4
+ # test all fields to make sure Yahoo hasn't changed the format...
5
+ # %%%%%%
6
+ describe YahooFinance::FinancialStatement do
7
+ # before(:each) do
8
+ # @tsymbols = ['AAPL', 'DDD']
9
+ # @page = YahooFinance::Page.new(@tsymbols)
10
+ # end
11
+
12
+ describe "Financial Statement" do
13
+ it "Income Statement should include things like :next_earnings_announcement_date" do
14
+ YahooFinance::FinancialStatement::INCOME_STMT_FIELDS.include?(:operating_income_or_loss).should == true
15
+ end
16
+ it "Balance Sheet should include things like :intagible_assets" do
17
+ YahooFinance::FinancialStatement::BALANCE_SHEET_FIELDS.include?(:intagible_assets).should == true
18
+ end
19
+ it "Cash Flow Statement should include things like :total_cash_flow_from_operating_activities" do
20
+ YahooFinance::FinancialStatement::CASH_FLOW_STMT_FIELDS.include?(:total_cash_flow_from_operating_activities).should == true
21
+ end
22
+ it "should fetch all fields for e.g. AAPL quarterly Income statement and all values should be float (except for :statement_periods)-- valid values" do
23
+ is = YahooFinance::FinancialStatement::QuarterlyIncomeStatementPage.new 'AAPL'
24
+ rs = is.fetch
25
+ YahooFinance::FinancialStatement::INCOME_STMT_FIELDS.keys.each do |attr|
26
+ fields = rs[attr]
27
+ fields.class.name.should == "Array"
28
+ fields.size.should == 4
29
+ fields.each do |field|
30
+ attr == :statement_periods ? field.class.name.should == "Date" : field.class.name.should == "Float"
31
+ end
32
+ end
33
+ end
34
+ it "should fetch all fields for e.g. AAPL annual Income statement and all values should be float (except for :statement_periods)-- valid values" do
35
+ is = YahooFinance::FinancialStatement::AnnualIncomeStatementPage.new 'AAPL'
36
+ rs = is.fetch
37
+ YahooFinance::FinancialStatement::INCOME_STMT_FIELDS.keys.each do |attr|
38
+ fields = rs[attr]
39
+ fields.class.name.should == "Array"
40
+ fields.size.should == 3
41
+ fields.each do |field|
42
+ attr == :statement_periods ? field.class.name.should == "Date" : field.class.name.should == "Float"
43
+ end
44
+ end
45
+ end
46
+ it "should fetch all fields for e.g. AAPL quarterly Balance Sheet statement and all values should be float (except for :statement_periods)-- valid values" do
47
+ is = YahooFinance::FinancialStatement::QuarterlyBalanceSheetPage.new 'AAPL'
48
+ rs = is.fetch
49
+ YahooFinance::FinancialStatement::BALANCE_SHEET_FIELDS.keys.each do |attr|
50
+ fields = rs[attr]
51
+ fields.class.name.should == "Array"
52
+ fields.size.should == 4
53
+ fields.each do |field|
54
+ attr == :statement_periods ? field.class.name.should == "Date" : field.class.name.should == "Float"
55
+ end
56
+ end
57
+ end
58
+ it "should fetch all fields for e.g. AAPL annual Balance Sheet statement and all values should be float (except for :statement_periods)-- valid values" do
59
+ is = YahooFinance::FinancialStatement::AnnualBalanceSheetPage.new 'AAPL'
60
+ rs = is.fetch
61
+ YahooFinance::FinancialStatement::BALANCE_SHEET_FIELDS.keys.each do |attr|
62
+ fields = rs[attr]
63
+ fields.class.name.should == "Array"
64
+ fields.size.should == 3
65
+ fields.each do |field|
66
+ attr == :statement_periods ? field.class.name.should == "Date" : field.class.name.should == "Float"
67
+ end
68
+ end
69
+ end
70
+ it "should fetch all fields for e.g. AAPL quarterly Cash Flow statement and all values should be float (except for :statement_periods)-- valid values" do
71
+ is = YahooFinance::FinancialStatement::QuarterlyCashFlowStatementPage.new 'AAPL'
72
+ rs = is.fetch
73
+ YahooFinance::FinancialStatement::CASH_FLOW_STMT_FIELDS.keys.each do |attr|
74
+ fields = rs[attr]
75
+ fields.class.name.should == "Array"
76
+ fields.size.should == 4
77
+ fields.each do |field|
78
+ attr == :statement_periods ? field.class.name.should == "Date" : field.class.name.should == "Float"
79
+ end
80
+ end
81
+ end
82
+ it "should fetch all fields for e.g. AAPL annual Cash Flow statement and all values should be float (except for :statement_periods)-- valid values" do
83
+ is = YahooFinance::FinancialStatement::AnnualCashFlowStatementPage.new 'AAPL'
84
+ rs = is.fetch
85
+ YahooFinance::FinancialStatement::CASH_FLOW_STMT_FIELDS.keys.each do |attr|
86
+ fields = rs[attr]
87
+ fields.class.name.should == "Array"
88
+ fields.size.should == 3
89
+ fields.each do |field|
90
+ attr == :statement_periods ? field.class.name.should == "Date" : field.class.name.should == "Float"
91
+ end
92
+ end
93
+ end
94
+ it "should fetch all fields for e.g. ACT (3 quarters only on 11/19/14) quarterly Cash Flow statement and all values should be float (except for :statement_periods)-- valid values" do
95
+ is = YahooFinance::FinancialStatement::QuarterlyCashFlowStatementPage.new 'ACT'
96
+ rs = is.fetch
97
+ YahooFinance::FinancialStatement::CASH_FLOW_STMT_FIELDS.keys.each do |attr|
98
+ fields = rs[attr]
99
+ fields.class.name.should == "Array"
100
+ fields.size.should == 3
101
+ fields.each do |field|
102
+ attr == :statement_periods ? field.class.name.should == "Date" : field.class.name.should == "Float"
103
+ end
104
+ end
105
+ end
106
+ it "should return a nil hash if yahoo doesn't have the statement, e.g. for AMBR" do
107
+ is = YahooFinance::FinancialStatement::QuarterlyCashFlowStatementPage.new 'AMBR'
108
+ rs = is.fetch
109
+ rs.should == nil
110
+ end
111
+
112
+ # pg = YahooFinance::CompanyEvents::CompanyEventsPage.new('AAPL')
113
+ # pg.fetch
114
+ # aapl_next_earnings_date = pg.value_for :next_earnings_announcement_date
115
+ # case aapl_next_earnings_date.class.name
116
+ # when "Date" then aapl_next_earnings_date.class.name.should == "Date"
117
+ # when "Array" then aapl_next_earnings_date.size.should == 2 && aapl_next_earnings_date[0].class.name.should == "Date" && aapl_next_earnings_date[1].class.name.should == "Date"
118
+ # when "String" then aapl_next_earnings_date.class.name.should == "String"
119
+ # else
120
+ # aapl_next_earnings_date.class.name.should == "Date" #force a failure
121
+ # end
122
+ #
123
+ end
124
+ end
metadata CHANGED
@@ -1,101 +1,101 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yahoo_finance_lib
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.1
4
+ version: 0.7.9.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Takis Mercouris
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-13 00:00:00.000000000 Z
11
+ date: 2014-11-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nas-yahoo_stock
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: 1.0.8
20
- - - '>='
20
+ - - ">="
21
21
  - !ruby/object:Gem::Version
22
22
  version: 1.0.8
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
- - - ~>
27
+ - - "~>"
28
28
  - !ruby/object:Gem::Version
29
29
  version: 1.0.8
30
- - - '>='
30
+ - - ">="
31
31
  - !ruby/object:Gem::Version
32
32
  version: 1.0.8
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: nokogiri
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
- - - ~>
37
+ - - "~>"
38
38
  - !ruby/object:Gem::Version
39
39
  version: 1.6.1
40
- - - '>='
40
+ - - ">="
41
41
  - !ruby/object:Gem::Version
42
42
  version: 1.6.1
43
43
  type: :runtime
44
44
  prerelease: false
45
45
  version_requirements: !ruby/object:Gem::Requirement
46
46
  requirements:
47
- - - ~>
47
+ - - "~>"
48
48
  - !ruby/object:Gem::Version
49
49
  version: 1.6.1
50
- - - '>='
50
+ - - ">="
51
51
  - !ruby/object:Gem::Version
52
52
  version: 1.6.1
53
53
  - !ruby/object:Gem::Dependency
54
54
  name: bundler
55
55
  requirement: !ruby/object:Gem::Requirement
56
56
  requirements:
57
- - - ~>
57
+ - - "~>"
58
58
  - !ruby/object:Gem::Version
59
59
  version: '1.3'
60
60
  type: :development
61
61
  prerelease: false
62
62
  version_requirements: !ruby/object:Gem::Requirement
63
63
  requirements:
64
- - - ~>
64
+ - - "~>"
65
65
  - !ruby/object:Gem::Version
66
66
  version: '1.3'
67
67
  - !ruby/object:Gem::Dependency
68
68
  name: rake
69
69
  requirement: !ruby/object:Gem::Requirement
70
70
  requirements:
71
- - - ~>
71
+ - - "~>"
72
72
  - !ruby/object:Gem::Version
73
73
  version: '0'
74
74
  type: :development
75
75
  prerelease: false
76
76
  version_requirements: !ruby/object:Gem::Requirement
77
77
  requirements:
78
- - - ~>
78
+ - - "~>"
79
79
  - !ruby/object:Gem::Version
80
80
  version: '0'
81
81
  - !ruby/object:Gem::Dependency
82
82
  name: rspec
83
83
  requirement: !ruby/object:Gem::Requirement
84
84
  requirements:
85
- - - ~>
85
+ - - "~>"
86
86
  - !ruby/object:Gem::Version
87
87
  version: 2.14.1
88
- - - '>='
88
+ - - ">="
89
89
  - !ruby/object:Gem::Version
90
90
  version: 2.14.1
91
91
  type: :development
92
92
  prerelease: false
93
93
  version_requirements: !ruby/object:Gem::Requirement
94
94
  requirements:
95
- - - ~>
95
+ - - "~>"
96
96
  - !ruby/object:Gem::Version
97
97
  version: 2.14.1
98
- - - '>='
98
+ - - ">="
99
99
  - !ruby/object:Gem::Version
100
100
  version: 2.14.1
101
101
  description: Unified library to stock quotes and various pages in yahoo finance, including
@@ -106,7 +106,7 @@ executables: []
106
106
  extensions: []
107
107
  extra_rdoc_files: []
108
108
  files:
109
- - .gitignore
109
+ - ".gitignore"
110
110
  - Gemfile
111
111
  - History.md
112
112
  - LICENSE.txt
@@ -116,6 +116,7 @@ files:
116
116
  - lib/yahoo_finance/analyst_opinion.rb
117
117
  - lib/yahoo_finance/company_events.rb
118
118
  - lib/yahoo_finance/company_profile.rb
119
+ - lib/yahoo_finance/financial_statement.rb
119
120
  - lib/yahoo_finance/key_statistics.rb
120
121
  - lib/yahoo_finance/stock.rb
121
122
  - lib/yahoo_finance/version.rb
@@ -124,6 +125,7 @@ files:
124
125
  - spec/yahoo_company_events_spec.rb
125
126
  - spec/yahoo_company_profile_spec.rb
126
127
  - spec/yahoo_finance_spec.rb
128
+ - spec/yahoo_financial_statement_spec.rb
127
129
  - spec/yahoo_key_stats_spec.rb
128
130
  - yahoo_finance.gemspec
129
131
  homepage: http://polymechanus.com/yahoo_finance-ruby-gem-tutorial/
@@ -136,17 +138,17 @@ require_paths:
136
138
  - lib
137
139
  required_ruby_version: !ruby/object:Gem::Requirement
138
140
  requirements:
139
- - - '>='
141
+ - - ">="
140
142
  - !ruby/object:Gem::Version
141
143
  version: '0'
142
144
  required_rubygems_version: !ruby/object:Gem::Requirement
143
145
  requirements:
144
- - - '>='
146
+ - - ">="
145
147
  - !ruby/object:Gem::Version
146
148
  version: '0'
147
149
  requirements: []
148
150
  rubyforge_project:
149
- rubygems_version: 2.4.1
151
+ rubygems_version: 2.4.2
150
152
  signing_key:
151
153
  specification_version: 4
152
154
  summary: Fetch stock information from yahoo finance API and HTML pages
@@ -156,4 +158,5 @@ test_files:
156
158
  - spec/yahoo_company_events_spec.rb
157
159
  - spec/yahoo_company_profile_spec.rb
158
160
  - spec/yahoo_finance_spec.rb
161
+ - spec/yahoo_financial_statement_spec.rb
159
162
  - spec/yahoo_key_stats_spec.rb