yahoo_quote 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ .rvmrc
4
+ Gemfile.lock
5
+ extras/
6
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in yahoo_quote.gemspec
4
+ gemspec
data/README ADDED
@@ -0,0 +1 @@
1
+ Gem to interact with Yahoo Finance API
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -0,0 +1,141 @@
1
+ require 'csv'
2
+ require 'open-uri'
3
+ require 'nokogiri'
4
+
5
+ # Gets company name, quote and stock graph
6
+ # See http://cliffngan.net/a/13
7
+ #
8
+ class YahooQuote
9
+ attr_accessor :name, :data, :csv, :command
10
+
11
+ def initialize(symbol)
12
+ @symbol = symbol.gsub(".", '') # yahoo csv expects no periods
13
+ parse_csv
14
+ end
15
+
16
+ def parse_csv
17
+ # abort if symbol has a weird character
18
+ if @symbol =~ /\W/
19
+ return
20
+ end
21
+ keys = "snl1j1s1wvre7e8r6r7"
22
+ @command = %Q|curl -sL "http://finance.yahoo.com/d/quotes.csv?s=#@symbol&f=#{keys}" |
23
+ @csv ||= %x[ #{@command} ]
24
+ @data = CSV.parse_line @csv
25
+ @symbol, @name = *@data
26
+ end
27
+
28
+ def valid?
29
+ return false unless @data
30
+ @data[3] != "N/A"
31
+ end
32
+
33
+ def company_name_orig
34
+ valid? ? name : nil
35
+ end
36
+
37
+ def company_name
38
+ return unless valid?
39
+ return @company_name if @company_name
40
+ res = `curl -Ls "http://query.yahooapis.com/v1/public/yql?q=select%20CompanyName%20from%20yahoo.finance.stocks%20where%20symbol%3D%22#{@symbol}%22&format=xml&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys"`
41
+ @company_name = Nokogiri::XML.parse(res).at("//CompanyName").inner_text
42
+ rescue
43
+ "No company name found"
44
+ end
45
+
46
+ def day_graph_url
47
+ return nil unless valid?
48
+ "http://ichart.finance.yahoo.com/t?s=#@symbol"
49
+ end
50
+
51
+ # the year graph
52
+ def graph_url
53
+ return nil unless valid?
54
+ "http://chart.finance.yahoo.com/z?s=#{@symbol}&t=1y&q=&l=&z=l&p=s&a=v&p=s&lang=en-US&region=US"
55
+ end
56
+
57
+ def graph_header
58
+ return nil unless valid?
59
+ url = "http://ichart.finance.yahoo.com/t?s=#@symbol"
60
+ url + "\n" + `curl -sI "#{url}"`
61
+ end
62
+ end
63
+
64
+ if __FILE__ == $0
65
+ puts ARGV.first
66
+ y = YahooQuote.new ARGV.first
67
+ puts y.name
68
+ puts y.company_name
69
+ puts y.csv.inspect
70
+ puts y.data.inspect
71
+
72
+ end
73
+
74
+
75
+ __END__
76
+
77
+
78
+ Usage
79
+
80
+ y = YahooQuote.new("AMZN")
81
+ puts y.name
82
+ puts y.data.inspect
83
+ puts y.valid?
84
+
85
+ exit
86
+
87
+ # try a nonexisting symbol
88
+
89
+ y = YahooQuote.new("Alwkjlkwadj")
90
+ puts y.name
91
+ puts y.data.inspect
92
+ puts y.valid?
93
+
94
+
95
+
96
+
97
+
98
+
99
+ url = "http://finance.yahoo.com/d/quotes.csv?s=AMZN&f=snd1l1yr"
100
+ puts `curl -sL '#{url}'`
101
+
102
+ url = "http://finance.yahoo.com/d/quotes.csv?s=AMZN&f=snd1l1yr"
103
+ puts `curl -sL '#{url}'`
104
+
105
+ url = "http://ichart.finance.yahoo.com/t?s=%5AMZN"
106
+ #puts `curl -sL '#{url}'` # => outputs a png file
107
+
108
+
109
+
110
+ "XOM","Exxon Mobil Corpo","7/8/2011",82.42,2.17,11.73
111
+ "BBD-B.TO","BOMBARDIER INC., ","7/8/2011",6.76,1.12,15.60
112
+ "JNJ","Johnson & Johnson","7/8/2011",67.57,3.22,15.40
113
+ "MSFT","Microsoft Corpora","7/8/2011",26.92,2.28,10.64
114
+
115
+
116
+ 2nd query:
117
+
118
+ "<img border=0 width=512 height=288
119
+ src="http://chart.yahoo.com/c/AMZN/a/amzn.gif" alt="Chart"><br><table><tr><td width=512 align=center><font face=arial size=-1></font></td></tr></table>"
120
+
121
+
122
+
123
+ "AMZN","Amazon.com, Inc.","7/8/2011",218.28,N/A,93.87
124
+ "MSFT","Microsoft Corpora","7/8/2011",26.92,2.28,10.64
125
+ "&nbsp;======&nbsp;"
126
+
127
+
128
+ "AMZN","Amazon.com, Inc.","7/8/2011",218.28,N/A,93.87
129
+ "MSFT","Microsoft Corpora","7/8/2011",26.92,2.28,10.64
130
+
131
+ "AMZN","Amazon.com, Inc.","7/8/2011",218.28,N/A,93.87
132
+ "MSFT","Microsoft Corpora","7/8/2011",26.92,2.28,10.64
133
+ "<img border=0 width=512 height=288
134
+ src="http://chart.yahoo.com/c//m/msft.gif" alt="Chart"><br><table><tr><td width=512 align=center><font face=arial size=-1></font></td></tr></table>","MSFT"
135
+
136
+ http://ichart.finance.yahoo.com/t?s=%5EHSI
137
+
138
+ "AMZN","Amazon.com, Inc.","7/8/2011",218.28,N/A,93.87
139
+ "MSFT","Microsoft Corpora","7/8/2011",26.92,2.28,10.64
140
+
141
+
@@ -0,0 +1,16 @@
1
+ module YahooQuote
2
+ class Configuration
3
+
4
+ def self.cache_dir=(path)
5
+ dir = path.to_s
6
+ Dir.mkdir(dir) unless File.directory?(dir)
7
+ @@cache_dir = dir
8
+ end
9
+
10
+ def self.cache_dir
11
+ @@cache_dir
12
+ end
13
+
14
+ @@cache_dir = nil
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ module YahooQuote
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,176 @@
1
+ require "yahoo_quote/version"
2
+ require "yahoo_quote/configuration"
3
+
4
+ require 'open-uri'
5
+ require 'csv'
6
+
7
+ class Hash
8
+ def self.csv_load( meta, headers, fields )
9
+ self[*headers.zip(fields).flatten.map { |e| eval(e) }]
10
+ end
11
+
12
+ def csv_headers
13
+ keys.map { |key| key.inspect }
14
+ end
15
+
16
+ def csv_dump( headers )
17
+ headers.map { |header| fetch(eval(header)).inspect }
18
+ end
19
+ end
20
+
21
+ module YahooQuote
22
+ class Quote
23
+ def initialize(symbol, fields)
24
+ @symbol = symbol.gsub(".", '') # yahoo csv expects no periods
25
+ @fields = fields
26
+ end
27
+
28
+ def field_mappings
29
+ {
30
+ "1 yr Target Price" => "t8",
31
+ "200-day Moving Average" => "m4",
32
+ "50-day Moving Average" => "m3",
33
+ "52-week High" => "k",
34
+ "52-week Low" => "j",
35
+ "52-week Range" => "w",
36
+ "After Hours Change (Real-time)" => "c8",
37
+ "Annualized Gain" => "g3",
38
+ "Ask (Real-time)" => "b2",
39
+ "Ask Size" => "a5",
40
+ "Ask" => "a",
41
+ "Average Daily Volume" => "a2",
42
+ "Bid (Real-time)" => "b3",
43
+ "Bid Size" => "b6",
44
+ "Bid" => "b",
45
+ "Book Value" => "b4",
46
+ "Change & Percent Change" => "c",
47
+ "Change (Real-time)" => "c6",
48
+ "Change From 200-day Moving Average" => "m5",
49
+ "Change From 50-day Moving Average" => "m7",
50
+ "Change From 52-week High" => "k4",
51
+ "Change From 52-week Low" => "j5",
52
+ "Change Percent (Real-time)" => "k2",
53
+ "Change in Percent" => "p2",
54
+ "Change" => "c1",
55
+ "Commission" => "c3",
56
+ "Day's High" => "h",
57
+ "Day's Low" => "g",
58
+ "Day's Range (Real-time)" => "m2",
59
+ "Day's Range" => "m",
60
+ "Day's Value Change (Real-time)" => "w4",
61
+ "Day's Value Change" => "w1",
62
+ "Dividend Pay Date" => "r1",
63
+ "Dividend Yield" => "y",
64
+ "Dividend/Share" => "d",
65
+ "EBITDA" => "j4",
66
+ "EPS Estimate Current Year" => "e7",
67
+ "EPS Estimate Next Quarter" => "e9",
68
+ "EPS Estimate Next Year" => "e8",
69
+ "Earnings/Share" => "e",
70
+ "Error Indication (returned for symbol changed / invalid)" => "e1",
71
+ "Ex-Dividend Date" => "q",
72
+ "Float Shares" => "f6",
73
+ "High Limit" => "l2",
74
+ "Holdings Gain (Real-time)" => "g6",
75
+ "Holdings Gain Percent (Real-time)" => "g5",
76
+ "Holdings Gain Percent" => "g1",
77
+ "Holdings Gain" => "g4",
78
+ "Holdings Value (Real-time)" => "v7",
79
+ "Holdings Value" => "v1",
80
+ "Last Trade (Price Only)" => "l1",
81
+ "Last Trade (Real-time) With Time" => "k1",
82
+ "Last Trade (With Time)" => "l",
83
+ "Last Trade Date" => "d1",
84
+ "Last Trade Size" => "k3",
85
+ "Last Trade Time" => "t1",
86
+ "Low Limit" => "l3",
87
+ "Market Cap (Real-time)" => "j3",
88
+ "Market Capitalization" => "j1",
89
+ "More Info" => "i",
90
+ "Name" => "n",
91
+ "Notes" => "n4",
92
+ "Open" => "o",
93
+ "Order Book (Real-time)" => "i5",
94
+ "P/E Ratio (Real-time)" => "r2",
95
+ "P/E Ratio" => "r",
96
+ "PEG Ratio" => "r5",
97
+ "Percebt Change From 52-week High" => "k5",
98
+ "Percent Change From 200-day Moving Average" => "m6",
99
+ "Percent Change From 50-day Moving Average" => "m8",
100
+ "Percent Change From 52-week Low" => "j6",
101
+ "Previous Close" => "p",
102
+ "Price Paid" => "p1",
103
+ "Price/Book" => "p6",
104
+ "Price/EPS Estimate Current Year" => "r6",
105
+ "Price/EPS Estimate Next Year" => "r7",
106
+ "Price/Sales" => "p5",
107
+ "Shares Owned" => "s1",
108
+ "Short Ratio" => "s7",
109
+ "Stock Exchange" => "x",
110
+ "Symbol" => "s",
111
+ "Ticker Trend" => "t7",
112
+ "Trade Date" => "d2",
113
+ "Trade Links" => "t6",
114
+ "Volume" => "v",
115
+ }
116
+ end
117
+
118
+ def parse_csv(csv)
119
+ values = CSV.parse_line(csv)
120
+ # TODO check result.size == fields.size
121
+ response = {}
122
+ values.each_with_index {|value, i| response[@fields[i]] = value}
123
+ response
124
+ end
125
+
126
+ def validate(response)
127
+ # Yahoo returns company name even if ticker symbol is invalid, other
128
+ # fields are also populated.
129
+ response["Market Capitalization"] == 'N/A' ? {} : response
130
+ end
131
+
132
+ # def company_name_url
133
+ # "http://query.yahooapis.com/v1/public/yql?q=select%20CompanyName%20from%20yahoo.finance.stocks%20where%20symbol%3D%22#{@symbol}%22&format=xml&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys"
134
+ # end
135
+
136
+ def quote_url
137
+ tags = @fields.map{|x| field_mappings[x]}.join
138
+ "http://download.finance.yahoo.com/d/quotes.csv?s=#{@symbol}&f=#{tags}"
139
+ end
140
+
141
+ def graph_url
142
+ return nil unless valid?
143
+ "http://chart.finance.yahoo.com/z?s=#{@symbol}&t=1y&q=&l=&z=l&p=s&a=v&p=s&lang=en-US&region=US"
144
+ end
145
+
146
+ def data
147
+ return @data if @data
148
+
149
+ io = URI.parse(quote_url)
150
+ begin
151
+ csv = io.read
152
+ rescue
153
+ csv = ''
154
+ end
155
+ @data = validate(parse_csv(csv))
156
+ if cache_response? && valid?
157
+ File.open(filename_quote, 'w') {|f| CSV.dump([@data], f) }
158
+ elsif cache_response? && File.file?(filename_quote)
159
+ @data = (File.open(filename_quote, 'r') {|f| CSV.load(f)}).first
160
+ end
161
+ @data
162
+ end
163
+
164
+ def filename_quote
165
+ YahooQuote::Configuration.cache_dir + "/#{@symbol}.csv"
166
+ end
167
+
168
+ def valid?
169
+ @data && @data.size > 0
170
+ end
171
+
172
+ def cache_response?
173
+ YahooQuote::Configuration.cache_dir
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,9 @@
1
+ HTTP/1.1 200 OK
2
+ Date: Mon, 06 Feb 2012 22:33:15 GMT
3
+ P3P: policyref="http://p3p.yahoo.com/w3c/p3p.xml", CP="CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE GOV"
4
+ Cache-Control: private
5
+ Connection: close
6
+ Transfer-Encoding: chunked
7
+ Content-Type: application/octet-stream
8
+
9
+ "AAPL","Apple Inc.",463.97,432.6B
@@ -0,0 +1,9 @@
1
+ HTTP/1.1 200 OK
2
+ Date: Mon, 06 Feb 2012 20:40:18 GMT
3
+ P3P: policyref="http://p3p.yahoo.com/w3c/p3p.xml", CP="CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE GOV"
4
+ Cache-Control: private
5
+ Connection: close
6
+ Transfer-Encoding: chunked
7
+ Content-Type: application/octet-stream
8
+
9
+
@@ -0,0 +1,58 @@
1
+ $LOAD_PATH << File.join(File.dirname(__FILE__), '../lib')
2
+
3
+ require 'yahoo_quote'
4
+ require 'minitest/autorun'
5
+ require 'fakeweb'
6
+
7
+ # test error: YahooQuote::Configuration.cache_dir = "/etc/kk"
8
+ #
9
+ class TestYahooQuote < MiniTest::Unit::TestCase
10
+ def setup
11
+ # TODO make gem method to clear the cache
12
+ `rm -f /tmp/*csv`
13
+ end
14
+
15
+ def test_live_quote
16
+ quote = YahooQuote::Quote.new('CSCO', ['Symbol', 'Name'])
17
+ assert_equal "CSCO", quote.data["Symbol"]
18
+ assert_equal "Cisco Systems, In", quote.data["Name"]
19
+ end
20
+
21
+ def test_invalid_ticker_symbol
22
+ quote = YahooQuote::Quote.new('ECOMMERCE', ['Symbol', 'Name', 'Market Capitalization'])
23
+ assert_nil quote.data["Name"]
24
+ assert_equal false, quote.valid?
25
+ # TODO check no cached
26
+ end
27
+
28
+ def test_get_quote
29
+ quote = YahooQuote::Quote.new('AAPL', ['Symbol', 'Name', 'Last Trade (Price Only)', 'Market Capitalization'])
30
+ FakeWeb.register_uri(:get, quote.quote_url, :response => File.read('test/fakeweb/aapl.csv'))
31
+ assert_equal "Apple Inc.", quote.data["Name"]
32
+ assert_equal 463.97, quote.data["Last Trade (Price Only)"].to_f
33
+ assert_equal '432.6B', quote.data["Market Capitalization"]
34
+ end
35
+
36
+ def test_graph_url
37
+ quote = YahooQuote::Quote.new('AAPL', ['Symbol', 'Name', 'Last Trade (Price Only)', 'Market Capitalization'])
38
+ FakeWeb.register_uri(:get, quote.quote_url, :response => File.read('test/fakeweb/aapl.csv'))
39
+ # TODO this should not be necessary
40
+ assert_equal "Apple Inc.", quote.data["Name"]
41
+ assert_match %r(^http://chart.finance.yahoo.com/z\?s=AAPL), quote.graph_url
42
+ end
43
+
44
+ def test_get_quote_from_cache
45
+ YahooQuote::Configuration.cache_dir = "/tmp"
46
+ quote = YahooQuote::Quote.new('AAPL', ['Symbol', 'Name', 'Last Trade (Price Only)'])
47
+ FakeWeb.register_uri(:get, quote.quote_url, :response => File.read('test/fakeweb/aapl.csv'))
48
+ assert_equal "Apple Inc.", quote.data["Name"]
49
+ assert_equal 463.97, quote.data["Last Trade (Price Only)"].to_f
50
+ # the quote should be cached now
51
+ quote2 = YahooQuote::Quote.new('AAPL', ['Symbol', 'Name', 'Last Trade (Price Only)'])
52
+ response = File.read('test/fakeweb/aapl_bad.csv')
53
+ refute_match /Apple/i, response
54
+ FakeWeb.register_uri(:get, quote2.quote_url, :response => response)
55
+ assert_equal "Apple Inc.", quote2.data["Name"]
56
+ assert_equal 463.97, quote2.data["Last Trade (Price Only)"].to_f
57
+ end
58
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "yahoo_quote/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "yahoo_quote"
7
+ s.version = YahooQuote::VERSION
8
+ s.authors = ["Braulio Carreno"]
9
+ s.email = ["bcarreno@yahoo.com"]
10
+ s.homepage = "https://github.com/bcarreno/yahoo_quote"
11
+ s.summary = %q{Yahoo Finance stock quotes}
12
+ s.description = %q{Facilitates querying Yahoo Finance stock API}
13
+
14
+ s.add_development_dependency "fakeweb"
15
+
16
+ s.rubyforge_project = "yahoo_quote"
17
+
18
+ s.files = `git ls-files`.split("\n")
19
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
+ s.require_paths = ["lib"]
22
+ end
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: yahoo_quote
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Braulio Carreno
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-08 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: fakeweb
16
+ requirement: &2157063640 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *2157063640
25
+ description: Facilitates querying Yahoo Finance stock API
26
+ email:
27
+ - bcarreno@yahoo.com
28
+ executables: []
29
+ extensions: []
30
+ extra_rdoc_files: []
31
+ files:
32
+ - .gitignore
33
+ - Gemfile
34
+ - README
35
+ - Rakefile
36
+ - lib/old_yahoo_quote.rb
37
+ - lib/yahoo_quote.rb
38
+ - lib/yahoo_quote/configuration.rb
39
+ - lib/yahoo_quote/version.rb
40
+ - test/fakeweb/aapl.csv
41
+ - test/fakeweb/aapl_bad.csv
42
+ - test/yahoo_quote_test.rb
43
+ - yahoo_quote.gemspec
44
+ homepage: https://github.com/bcarreno/yahoo_quote
45
+ licenses: []
46
+ post_install_message:
47
+ rdoc_options: []
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ requirements: []
63
+ rubyforge_project: yahoo_quote
64
+ rubygems_version: 1.8.15
65
+ signing_key:
66
+ specification_version: 3
67
+ summary: Yahoo Finance stock quotes
68
+ test_files:
69
+ - test/fakeweb/aapl.csv
70
+ - test/fakeweb/aapl_bad.csv
71
+ - test/yahoo_quote_test.rb