market_beat 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
22
+ .rvmrc
data/CHANGELOG ADDED
@@ -0,0 +1,2 @@
1
+ 0.1.0
2
+ - Initial Release.
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "http://rubygems.org"
2
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,24 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ market_beat (0.1.0)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.1.2)
10
+ rspec (2.6.0)
11
+ rspec-core (~> 2.6.0)
12
+ rspec-expectations (~> 2.6.0)
13
+ rspec-mocks (~> 2.6.0)
14
+ rspec-core (2.6.0)
15
+ rspec-expectations (2.6.0)
16
+ diff-lcs (~> 1.1.2)
17
+ rspec-mocks (2.6.0)
18
+
19
+ PLATFORMS
20
+ ruby
21
+
22
+ DEPENDENCIES
23
+ market_beat!
24
+ rspec (>= 2.6.0)
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2011 Michael Dvorkin
2
+ twitter.com/mid
3
+ %w(mike dvorkin.net) * "@" || %w(mike fatfreecrm.com) * "@"
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,173 @@
1
+ ## MarketBeat ##
2
+ Market Beat is a Ruby library that fetches stock quotes and other financial
3
+ and market indicators from publicly available sources. Currently Google and
4
+ Yahoo market reporting services are supported.
5
+
6
+ ### Installation ###
7
+ # Installing as Ruby gem
8
+ $ gem install market_beat
9
+
10
+ # Cloning the repository
11
+ $ git clone git://github.com/michaeldv/market_beat.git
12
+
13
+ ### Usage ###
14
+ Require "market_beat" gem, then invoke one of the MarketBeat's methods passing
15
+ stock symbol as a parameter. Stock symbols are case insensitive and could be
16
+ passed as a symbol or a string.
17
+
18
+ For example, to fetch today's opening price and the very latest trade price
19
+ for the Apple stock (NASDAQ symbol 'AAPL') use the following:
20
+
21
+ >> require "market_beat"
22
+ >> MarketBeat.opening_price :AAPL
23
+ "396.69"
24
+ >> MarketBeat.last_trade_real_time :AAPL
25
+ "397.07"
26
+
27
+ Some methods return multiple values:
28
+
29
+ >> MarketBeat.change_and_percent_change :IBM
30
+ ["-5.77", "-3.08%"]
31
+ >> MarketBeat.last_trade_with_time :IBM
32
+ ["4:02pm", "181.47"]
33
+
34
+ ### Available Methods (Real Time Data) ###
35
+
36
+ MarketBeat.after_hours_change_real_time
37
+ MarketBeat.ask_real_time
38
+ MarketBeat.bid_real_time
39
+ MarketBeat.change_percent_real_time
40
+ MarketBeat.change_real_time
41
+ MarketBeat.color_code_real_time
42
+ MarketBeat.days_range_real_time
43
+ MarketBeat.days_value_change_real_time
44
+ MarketBeat.holdings_gain_percent_real_time
45
+ MarketBeat.holdings_gain_real_time
46
+ MarketBeat.holdings_value_real_time
47
+ MarketBeat.last_trade_datetime_real_time
48
+ MarketBeat.last_trade_real_time
49
+ MarketBeat.last_trade_real_time_with_time
50
+ MarketBeat.last_trade_time_real_time
51
+ MarketBeat.market_cap_real_time
52
+ MarketBeat.order_book_real_time
53
+ MarketBeat.pe_ratio_real_time
54
+ MarketBeat.percent_change_real_time
55
+
56
+ ### Available Methods (Delayed Data) ###
57
+
58
+ MarketBeat.annualized_gain
59
+ MarketBeat.ask
60
+ MarketBeat.ask_size
61
+ MarketBeat.average_daily_volume
62
+ MarketBeat.bid
63
+ MarketBeat.bid_size
64
+ MarketBeat.book_value
65
+ MarketBeat.brut_last
66
+ MarketBeat.brut_trade_date_utc
67
+ MarketBeat.brut_trade_time_utc
68
+ MarketBeat.change
69
+ MarketBeat.change_and_percent_change
70
+ MarketBeat.change_from_200_day_moving_average
71
+ MarketBeat.change_from_50_day_moving_average
72
+ MarketBeat.change_from_52_week_high
73
+ MarketBeat.change_from_52_week_low
74
+ MarketBeat.chart_url
75
+ MarketBeat.commission
76
+ MarketBeat.company
77
+ MarketBeat.currency
78
+ MarketBeat.current_date_utc
79
+ MarketBeat.current_time_utc
80
+ MarketBeat.daylight_savings
81
+ MarketBeat.days_high
82
+ MarketBeat.days_low
83
+ MarketBeat.days_range
84
+ MarketBeat.days_value_change
85
+ MarketBeat.delay
86
+ MarketBeat.disclaimer_url
87
+ MarketBeat.dividend_pay_date
88
+ MarketBeat.dividend_to_share
89
+ MarketBeat.dividend_yield
90
+ MarketBeat.divisor
91
+ MarketBeat.earnings_to_share
92
+ MarketBeat.ebitda
93
+ MarketBeat.ecn_url
94
+ MarketBeat.eps_estimate_current_year
95
+ MarketBeat.eps_estimate_next_quarter
96
+ MarketBeat.eps_estimate_next_year
97
+ MarketBeat.error_indication
98
+ MarketBeat.ex_dividend_date
99
+ MarketBeat.float_shares
100
+ MarketBeat.high_52_week
101
+ MarketBeat.high_limit
102
+ MarketBeat.holdings_gain
103
+ MarketBeat.holdings_gain_percent
104
+ MarketBeat.holdings_value
105
+ MarketBeat.isld_last
106
+ MarketBeat.isld_trade_date_utc
107
+ MarketBeat.isld_trade_time_utc
108
+ MarketBeat.last_trade
109
+ MarketBeat.last_trade_date
110
+ MarketBeat.last_trade_size
111
+ MarketBeat.last_trade_time
112
+ MarketBeat.last_trade_with_time
113
+ MarketBeat.low_52_week
114
+ MarketBeat.low_limit
115
+ MarketBeat.market_capitalization
116
+ MarketBeat.more_info
117
+ MarketBeat.moving_average_200_day
118
+ MarketBeat.moving_average_50_day
119
+ MarketBeat.notes
120
+ MarketBeat.one_year_target_price
121
+ MarketBeat.opening_price
122
+ MarketBeat.pe_ratio
123
+ MarketBeat.peg_ratio
124
+ MarketBeat.percent_change
125
+ MarketBeat.percent_change_from_200_day_moving_average
126
+ MarketBeat.percent_change_from_50_day_moving_average
127
+ MarketBeat.percent_change_from_52_week_high
128
+ MarketBeat.percent_change_from_52_week_low
129
+ MarketBeat.pretty_symbol
130
+ MarketBeat.previous_close
131
+ MarketBeat.price_paid
132
+ MarketBeat.price_to_book
133
+ MarketBeat.price_to_eps_estimate_current_year
134
+ MarketBeat.price_to_eps_estimate_next_year
135
+ MarketBeat.price_to_sales
136
+ MarketBeat.range_52_week
137
+ MarketBeat.shares_owned
138
+ MarketBeat.short_ratio
139
+ MarketBeat.stock_exchange
140
+ MarketBeat.stock_exchange_closing
141
+ MarketBeat.stock_exchange_timezone
142
+ MarketBeat.stock_exchange_utc_offset
143
+ MarketBeat.symbol
144
+ MarketBeat.symbol_lookup_url
145
+ MarketBeat.symbol_url
146
+ MarketBeat.ticker_trend
147
+ MarketBeat.trade_date
148
+ MarketBeat.trade_date_utc
149
+ MarketBeat.trade_time_utc
150
+ MarketBeat.trade_timestamp
151
+ MarketBeat.volume
152
+
153
+ ### Running Specs ###
154
+
155
+ $ gem install rspec # RSpec 2.x is the requirement.
156
+ $ rake spec # Run the entire spec suite.
157
+ $ rspec spec/google_spec.rb # Run individual spec file.
158
+
159
+ ### Note on Patches/Pull Requests ###
160
+ * Fork the project on Github.
161
+ * Make your feature addition or bug fix.
162
+ * Add specs for it, making sure $ rake spec is all green.
163
+ * Commit, do not mess with Rakefile, version, or history.
164
+ * Send me a pull request.
165
+
166
+ ### License ###
167
+ Copyright (c) 2011 Michael Dvorkin
168
+
169
+ twitter.com/mid
170
+
171
+ %w(mike dvorkin.net) * "@" || %w(mike fatfreecrm.com) * "@"
172
+
173
+ Released under the MIT license. See LICENSE file for details.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,48 @@
1
+ # Copyright (c) 2011 Michael Dvorkin
2
+ #
3
+ # Market Beat is freely distributable under the terms of MIT license.
4
+ # See LICENSE file or http://www.opensource.org/licenses/mit-license.php
5
+ #------------------------------------------------------------------------------
6
+ require 'rexml/document'
7
+ require 'net/http'
8
+ require 'uri'
9
+ require 'yaml'
10
+
11
+ module MarketBeat
12
+ class Google
13
+ URL = 'http://www.google.com/ig/api?stock='.freeze
14
+ REAL_TIME_URL = 'http://finance.google.com/finance/info?client=ig&q='.freeze
15
+
16
+ class << self
17
+ metrics = YAML.load_file(File.dirname(__FILE__) + '/google.yml')
18
+ metrics.each do |key, value|
19
+ if value.to_s !~ /real_time$/
20
+ define_method value do |ticker|
21
+ from_xml fetch(ticker), key
22
+ end
23
+ else
24
+ define_method value do |ticker|
25
+ from_json fetch(ticker, :real_time), key
26
+ end
27
+ end
28
+ end
29
+
30
+ private
31
+ def fetch(ticker, real_time = false)
32
+ uri = URI.parse("#{real_time ? REAL_TIME_URL : URL}#{ticker}")
33
+ response = Net::HTTP.get_response(uri)
34
+ response.body
35
+ end
36
+
37
+ def from_xml(xml, metric)
38
+ doc = REXML::Document.new(xml)
39
+ data = doc.elements["//finance/#{metric}"].attributes['data']
40
+ data.empty? ? nil : data
41
+ end
42
+
43
+ def from_json(json, metric)
44
+ json =~ /"#{metric}"\s*\:\s*"(.+?)"/ ? $1 : nil
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,10 @@
1
+ # Copyright (c) 2011 Michael Dvorkin
2
+ #
3
+ # Market Beat is freely distributable under the terms of MIT license.
4
+ # See LICENSE file or http://www.opensource.org/licenses/mit-license.php
5
+ #------------------------------------------------------------------------------
6
+ module MarketBeat
7
+ def self.version
8
+ '0.1.0'
9
+ end
10
+ end
@@ -0,0 +1,51 @@
1
+ # Copyright (c) 2011 Michael Dvorkin
2
+ #
3
+ # Market Beat is freely distributable under the terms of MIT license.
4
+ # See LICENSE file or http://www.opensource.org/licenses/mit-license.php
5
+ #------------------------------------------------------------------------------
6
+ require 'net/http'
7
+ require 'uri'
8
+ require 'yaml'
9
+
10
+ module MarketBeat
11
+ class Yahoo
12
+ URL = 'http://download.finance.yahoo.com/d/quotes.csv?s='.freeze
13
+ L2N = { 'M' => 1_000_000, 'B' => 1_000_000_000 }.freeze
14
+
15
+ class << self
16
+ metrics = YAML.load_file(File.dirname(__FILE__) + '/yahoo.yml')
17
+ metrics.each do |key, value|
18
+ define_method value do |ticker|
19
+ sanitize fetch(ticker, key)
20
+ end
21
+ end
22
+
23
+ private
24
+ def fetch(ticker, metric)
25
+ uri = URI.parse("#{URL}#{ticker}&f=#{metric}")
26
+ response = Net::HTTP.get_response(uri)
27
+ response.body
28
+ end
29
+
30
+ def sanitize(raw)
31
+ data = raw.strip.gsub(/\r|\n|"|<\/?b>|&nbsp;/, '')
32
+ min, max = data.split(/\s+\-\s+/)
33
+ min && max ? [scrub(min), scrub(max)] : scrub(min)
34
+ end
35
+
36
+ def scrub(data)
37
+ # Convert empty string, '-' or 'N/A' to nil
38
+ return nil if data =~ /^(?:\s*|N\/A|\-)$/
39
+ # Normalize numbers:
40
+ # '1,234.56' => '1234.56'
41
+ # '1,234.56M' => '1234560000'
42
+ # '1,234.56B' => '1234560000000'
43
+ if data =~ /^[\.\d,]+[MB]*$/
44
+ data.gsub!(',', '')
45
+ data = (data[0..-2].to_f * L2N[data[-1,1]]).to_i.to_s if L2N[data[-1,1]]
46
+ end
47
+ data
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,31 @@
1
+ # Copyright (c) 2011 Michael Dvorkin
2
+ #
3
+ # Market Beat is freely distributable under the terms of MIT license.
4
+ # See LICENSE file or http://www.opensource.org/licenses/mit-license.php
5
+ #------------------------------------------------------------------------------
6
+ require File.dirname(__FILE__) + '/market_beat/yahoo'
7
+ require File.dirname(__FILE__) + '/market_beat/google'
8
+ require File.dirname(__FILE__) + "/market_beat/version"
9
+
10
+ module MarketBeat
11
+ # Pre-cache available methods so we could use them within
12
+ # method_missing where respond_to? gives false positives.
13
+ if RUBY_VERSION < '1.9.0'
14
+ @@yahoo = (Yahoo.methods - Object.methods).map { |m| m.to_sym }
15
+ @@google = (Google.methods - Object.methods).map { |m| m.to_sym }
16
+ else
17
+ @@yahoo = Yahoo.methods - Object.methods
18
+ @@google = Google.methods - Object.methods
19
+ end
20
+
21
+ # Proxy MarketBeat methods to either Google or Yahoo providers.
22
+ def self.method_missing(method, *args, &blk)
23
+ if @@google.include?(method)
24
+ Google.send(method, *args)
25
+ elsif @@yahoo.include?(method)
26
+ Yahoo.send(method, *args)
27
+ else
28
+ super
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,202 @@
1
+ # Copyright (c) 2011 Michael Dvorkin
2
+ #
3
+ # Market Beat is freely distributable under the terms of MIT license.
4
+ # See LICENSE file or http://www.opensource.org/licenses/mit-license.php
5
+ #------------------------------------------------------------------------------
6
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
7
+
8
+ describe "MarketBeat::Google Delayed" do
9
+ before(:all) do
10
+ @ibm = File.read(File.dirname(__FILE__) + '/fixtures/IBM.xml')
11
+ end
12
+
13
+ def ibm(metric)
14
+ response = mock(:body => @ibm)
15
+ Net::HTTP.should_receive(:get_response).once.and_return(response)
16
+ MarketBeat::Google.send(metric, :ibm)
17
+ end
18
+
19
+ it "average_daily_volume" do
20
+ ibm(:average_daily_volume).should == '5084'
21
+ end
22
+
23
+ it "brut_last" do
24
+ ibm(:brut_last).should == nil
25
+ end
26
+
27
+ it "brut_trade_date_utc" do
28
+ ibm(:brut_trade_date_utc).should == nil
29
+ end
30
+
31
+ it "brut_trade_time_utc" do
32
+ ibm(:brut_trade_time_utc).should == nil
33
+ end
34
+
35
+ it "change" do
36
+ ibm(:change).should == '-2.32'
37
+ end
38
+
39
+ it "chart_url" do
40
+ ibm(:chart_url).should == '/finance/chart?q=NYSE:IBM&tlf=12'
41
+ end
42
+
43
+ it "currency" do
44
+ ibm(:currency).should == 'USD'
45
+ end
46
+
47
+ it "current_date_utc" do
48
+ ibm(:current_date_utc).should == '20110515'
49
+ end
50
+
51
+ it "current_time_utc" do
52
+ ibm(:current_time_utc).should == '001456'
53
+ end
54
+
55
+ it "daylight_savings" do
56
+ ibm(:daylight_savings).should == 'true'
57
+ end
58
+
59
+ it "days_high" do
60
+ ibm(:days_high).should == '172.15'
61
+ end
62
+
63
+ it "days_low" do
64
+ ibm(:days_low).should == '169.44'
65
+ end
66
+
67
+ it "delay" do
68
+ ibm(:delay).should == '0'
69
+ end
70
+
71
+ it "disclaimer_url" do
72
+ ibm(:disclaimer_url).should == '/help/stock_disclaimer.html'
73
+ end
74
+
75
+ it "divisor" do
76
+ ibm(:divisor).should == '2'
77
+ end
78
+
79
+ it "ecn_url" do
80
+ ibm(:ecn_url).should == nil
81
+ end
82
+
83
+ it "isld_last" do
84
+ ibm(:isld_last).should == nil
85
+ end
86
+
87
+ it "isld_trade_date_utc" do
88
+ ibm(:isld_trade_date_utc).should == nil
89
+ end
90
+
91
+ it "isld_trade_time_utc" do
92
+ ibm(:isld_trade_time_utc).should == nil
93
+ end
94
+
95
+ it "last_trade" do
96
+ ibm(:last_trade).should == '169.92'
97
+ end
98
+
99
+ it "market_capitalization" do
100
+ ibm(:market_capitalization).should == '207223.55'
101
+ end
102
+
103
+ it "company" do
104
+ ibm(:company).should == 'International Business Machines Corp.'
105
+ end
106
+
107
+ it "opening_price" do
108
+ ibm(:opening_price).should == '171.70'
109
+ end
110
+
111
+ it "percent_change" do
112
+ ibm(:percent_change).should == '-1.35'
113
+ end
114
+
115
+ it "pretty_symbol" do
116
+ ibm(:pretty_symbol).should == 'IBM'
117
+ end
118
+
119
+ it "previous_close" do
120
+ ibm(:previous_close).should == '172.24'
121
+ end
122
+
123
+ it "stock_exchange" do
124
+ ibm(:stock_exchange).should == 'NYSE'
125
+ end
126
+
127
+ it "stock_exchange_closing" do
128
+ ibm(:stock_exchange_closing).should == '960'
129
+ end
130
+
131
+ it "stock_exchange_timezone" do
132
+ ibm(:stock_exchange_timezone).should == 'ET'
133
+ end
134
+
135
+ it "stock_exchange_utc_offset" do
136
+ ibm(:stock_exchange_utc_offset).should == '+05:00'
137
+ end
138
+
139
+ it "symbol" do
140
+ ibm(:symbol).should == 'IBM'
141
+ end
142
+
143
+ it "symbol_lookup_url" do
144
+ ibm(:symbol_lookup_url).should == '/finance?client=ig&q=IBM'
145
+ end
146
+
147
+ it "symbol_url" do
148
+ ibm(:symbol_url).should == '/finance?client=ig&q=IBM'
149
+ end
150
+
151
+ it "trade_date_utc" do
152
+ ibm(:trade_date_utc).should == '20110513'
153
+ end
154
+
155
+ it "trade_time_utc" do
156
+ ibm(:trade_time_utc).should == '200138'
157
+ end
158
+
159
+ it "trade_timestamp" do
160
+ ibm(:trade_timestamp).should == 'May 13, 2011'
161
+ end
162
+
163
+ it "volume" do
164
+ ibm(:volume).should == '5168995'
165
+ end
166
+ end
167
+
168
+ describe "MarketBeat::Google Real Time" do
169
+ before(:all) do
170
+ @citibank = File.read(File.dirname(__FILE__) + '/fixtures/C.json')
171
+ end
172
+
173
+ def citibank(metric)
174
+ response = mock(:body => @citibank)
175
+ Net::HTTP.should_receive(:get_response).once.and_return(response)
176
+ MarketBeat::Google.send(metric, :c)
177
+ end
178
+
179
+ it "change_real_time" do
180
+ citibank(:change_real_time).should == '-0.89'
181
+ end
182
+
183
+ it "color_code" do
184
+ citibank(:color_code_real_time).should == 'chr'
185
+ end
186
+
187
+ it "last_trade_datetime_real_time" do
188
+ citibank(:last_trade_datetime_real_time).should == 'May 13, 4:00PM EDT'
189
+ end
190
+
191
+ it "last_trade_real_time" do
192
+ citibank(:last_trade_real_time).should == '41.53'
193
+ end
194
+
195
+ it "last_trade_time_real_time" do
196
+ citibank(:last_trade_time_real_time).should == '4:00PM EDT'
197
+ end
198
+
199
+ it "percent_change_real_time" do
200
+ citibank(:percent_change_real_time).should == '-2.10'
201
+ end
202
+ end
@@ -0,0 +1,42 @@
1
+ # Copyright (c) 2011 Michael Dvorkin
2
+ #
3
+ # Market Beat is freely distributable under the terms of MIT license.
4
+ # See LICENSE file or http://www.opensource.org/licenses/mit-license.php
5
+ #------------------------------------------------------------------------------
6
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
7
+ require 'yaml'
8
+
9
+ describe "MarketBeat Google Proxy" do
10
+ metrics = YAML.load_file(File.dirname(__FILE__) + '/../lib/market_beat/google.yml')
11
+ metrics.values.each do |method|
12
+ it "Google.#{method}" do
13
+ MarketBeat::Google.should_receive(method).once
14
+ MarketBeat::Yahoo.should_not_receive(method)
15
+ MarketBeat.send(method, :msft)
16
+ end
17
+ end
18
+ end
19
+
20
+ describe "MarketBeat Yahoo Proxy" do
21
+ yahoo_metrics = YAML.load_file(File.dirname(__FILE__) + '/../lib/market_beat/yahoo.yml')
22
+ google_metrics = YAML.load_file(File.dirname(__FILE__) + '/../lib/market_beat/google.yml')
23
+ yahoo_metrics.values.each do |method|
24
+ it "Yahoo.#{method}" do # If Yahoo and Google have the same method Google takes precedence.
25
+ if google_metrics.values.include?(method)
26
+ MarketBeat::Google.should_receive(method).once
27
+ MarketBeat::Yahoo.should_not_receive(method)
28
+ else
29
+ MarketBeat::Google.should_not_receive(method)
30
+ MarketBeat::Yahoo.should_receive(method).once
31
+ end
32
+ MarketBeat.send(method, :msft)
33
+ end
34
+ end
35
+ end
36
+
37
+ describe "MarketBeat super passthrough" do
38
+ it "unknown method invokes super" do
39
+ lambda { MarketBeat.hello_world! }.should raise_error(NoMethodError)
40
+ end
41
+ end
42
+
@@ -0,0 +1,8 @@
1
+ # Copyright (c) 2011 Michael Dvorkin
2
+ #
3
+ # Market Beat is freely distributable under the terms of MIT license.
4
+ # See LICENSE file or http://www.opensource.org/licenses/mit-license.php
5
+ #------------------------------------------------------------------------------
6
+ $:.unshift(File.dirname(__FILE__))
7
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
8
+ require 'market_beat'
@@ -0,0 +1,446 @@
1
+ # Copyright (c) 2011 Michael Dvorkin
2
+ #
3
+ # Market Beat is freely distributable under the terms of MIT license.
4
+ # See LICENSE file or http://www.opensource.org/licenses/mit-license.php
5
+ #------------------------------------------------------------------------------
6
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
7
+ require 'yaml'
8
+
9
+ describe "MarketBeat::Yahoo" do
10
+ before(:all) do
11
+ @apple = YAML.load_file(File.dirname(__FILE__) + '/fixtures/AAPL.yml')
12
+ @google = YAML.load_file(File.dirname(__FILE__) + '/fixtures/GOOG.yml')
13
+ end
14
+
15
+ def apple(metric)
16
+ response = mock(:body => @apple[metric])
17
+ Net::HTTP.should_receive(:get_response).once.and_return(response)
18
+ MarketBeat::Yahoo.send(metric, 'AAPL')
19
+ end
20
+
21
+ def google(metric)
22
+ response = mock(:body => @google[metric])
23
+ Net::HTTP.should_receive(:get_response).once.and_return(response)
24
+ MarketBeat::Yahoo.send(metric, 'GOOG')
25
+ end
26
+
27
+ it 'after_hours_change_real_time' do
28
+ apple(:after_hours_change_real_time).should == [ nil, nil ]
29
+ google(:after_hours_change_real_time).should == [ nil, nil ]
30
+ end
31
+
32
+ it 'annualized_gain' do
33
+ apple(:annualized_gain).should == nil
34
+ google(:annualized_gain).should == nil
35
+ end
36
+
37
+ it 'ask' do
38
+ apple(:ask).should == '340.50'
39
+ google(:ask).should == '529.98'
40
+ end
41
+
42
+ it 'ask_real_time' do
43
+ apple(:ask_real_time).should == '340.50'
44
+ google(:ask_real_time).should == '529.98'
45
+ end
46
+
47
+ it 'ask_size' do
48
+ apple(:ask_size).should == '100'
49
+ google(:ask_size).should == '100'
50
+ end
51
+
52
+ it 'average_daily_volume' do
53
+ apple(:average_daily_volume).should == '16464000'
54
+ google(:average_daily_volume).should == '2773170'
55
+ end
56
+
57
+ it 'bid' do
58
+ apple(:bid).should == '340.23'
59
+ google(:bid).should == nil
60
+ end
61
+
62
+ it 'bid_real_time' do
63
+ apple(:bid_real_time).should == '340.23'
64
+ google(:bid_real_time).should == '0.00'
65
+ end
66
+
67
+ it 'bid_size' do
68
+ apple(:bid_size).should == '200'
69
+ google(:bid_size).should == nil
70
+ end
71
+
72
+ it 'book_value' do
73
+ apple(:book_value).should == '66.485'
74
+ google(:book_value).should == '153.49'
75
+ end
76
+
77
+ it 'change' do
78
+ apple(:change).should == '-6.07'
79
+ google(:change).should == '-5.50'
80
+ end
81
+
82
+ it 'change_and_percent_change' do
83
+ apple(:change_and_percent_change).should == [ '-6.07', '-1.75%' ]
84
+ google(:change_and_percent_change).should == [ '-5.50', '-1.03%' ]
85
+ end
86
+
87
+ it 'change_from_200_day_moving_average' do
88
+ apple(:change_from_200_day_moving_average).should == '+5.811'
89
+ google(:change_from_200_day_moving_average).should == '-60.526'
90
+ end
91
+
92
+ it 'change_from_50_day_moving_average' do
93
+ apple(:change_from_50_day_moving_average).should == '-3.171'
94
+ google(:change_from_50_day_moving_average).should == '-24.389'
95
+ end
96
+
97
+ it 'change_from_52_week_high' do
98
+ apple(:change_from_52_week_high).should == '-24.40'
99
+ google(:change_from_52_week_high).should == '-113.41'
100
+ end
101
+
102
+ it 'change_from_52_week_low' do
103
+ apple(:change_from_52_week_low).should == '+109.15'
104
+ google(:change_from_52_week_low).should == '+95.92'
105
+ end
106
+
107
+ it 'percent_change' do
108
+ apple(:percent_change).should == '-1.75%'
109
+ google(:percent_change).should == '-1.03%'
110
+ end
111
+
112
+ it 'change_percent_real_time' do
113
+ apple(:change_percent_real_time).should == [ nil, '-1.75%' ]
114
+ google(:change_percent_real_time).should == [ nil, '-1.03%' ]
115
+ end
116
+
117
+ it 'change_real_time' do
118
+ apple(:change_real_time).should == '-6.07'
119
+ google(:change_real_time).should == '-5.50'
120
+ end
121
+
122
+ it 'commission' do
123
+ apple(:commission).should == nil
124
+ google(:commission).should == nil
125
+ end
126
+
127
+ it 'days_high' do
128
+ apple(:days_high).should == '346.25'
129
+ google(:days_high).should == '535.92'
130
+ end
131
+
132
+ it 'days_low' do
133
+ apple(:days_low).should == '340.35'
134
+ google(:days_low).should == '529.05'
135
+ end
136
+
137
+ it 'days_range' do
138
+ apple(:days_range).should == [ '340.35', '346.25' ]
139
+ google(:days_range).should == [ '529.05', '535.92' ]
140
+ end
141
+
142
+ it 'days_range_real_time' do
143
+ apple(:days_range_real_time).should == [ nil, nil ]
144
+ google(:days_range_real_time).should == [ nil, nil ]
145
+ end
146
+
147
+ it 'days_value_change' do
148
+ apple(:days_value_change).should == [ nil, '-1.75%' ]
149
+ google(:days_value_change).should == [ nil, '-1.03%' ]
150
+ end
151
+
152
+ it 'days_value_change_real_time' do
153
+ apple(:days_value_change_real_time).should == [ nil, nil ]
154
+ google(:days_value_change_real_time).should == [ nil, nil ]
155
+ end
156
+
157
+ it 'dividend_pay_date' do
158
+ apple(:dividend_pay_date).should == nil
159
+ google(:dividend_pay_date).should == nil
160
+ end
161
+
162
+ it 'dividend_to_share' do
163
+ apple(:dividend_to_share).should == '0.00'
164
+ google(:dividend_to_share).should == '0.00'
165
+ end
166
+
167
+ it 'dividend_yield' do
168
+ apple(:dividend_yield).should == nil
169
+ google(:dividend_yield).should == nil
170
+ end
171
+
172
+ it 'earnings_to_share' do
173
+ apple(:earnings_to_share).should == '20.992'
174
+ google(:earnings_to_share).should == '27.291'
175
+ end
176
+
177
+ it 'ebitda' do
178
+ apple(:ebitda).should == '26726000000' # '26.726B'
179
+ google(:ebitda).should == '12155000000' # '12.155B'
180
+ end
181
+
182
+ it 'eps_estimate_current_year' do
183
+ apple(:eps_estimate_current_year).should == '24.63'
184
+ google(:eps_estimate_current_year).should == '33.94'
185
+ end
186
+
187
+ it 'eps_estimate_next_quarter' do
188
+ apple(:eps_estimate_next_quarter).should == '6.36'
189
+ google(:eps_estimate_next_quarter).should == '8.31'
190
+ end
191
+
192
+ it 'eps_estimate_next_year' do
193
+ apple(:eps_estimate_next_year).should == '28.51'
194
+ google(:eps_estimate_next_year).should == '39.55'
195
+ end
196
+
197
+ it 'error_indication' do
198
+ apple(:error_indication).should == nil
199
+ google(:error_indication).should == nil
200
+ end
201
+
202
+ it 'ex_dividend_date' do
203
+ apple(:ex_dividend_date).should == '21-Nov-95'
204
+ google(:ex_dividend_date).should == nil
205
+ end
206
+
207
+ it 'float_shares' do
208
+ apple(:float_shares).should == '918706000'
209
+ google(:float_shares).should == '251385000'
210
+ end
211
+
212
+ it 'high_52_week' do
213
+ apple(:high_52_week).should == '364.90'
214
+ google(:high_52_week).should == '642.96'
215
+ end
216
+
217
+ it 'high_limit' do
218
+ apple(:high_limit).should == nil
219
+ google(:high_limit).should == nil
220
+ end
221
+
222
+ it 'holdings_gain' do
223
+ apple(:holdings_gain).should == nil
224
+ google(:holdings_gain).should == nil
225
+ end
226
+
227
+ it 'holdings_gain_percent' do
228
+ apple(:holdings_gain_percent).should == [ nil, nil ]
229
+ google(:holdings_gain_percent).should == [ nil, nil ]
230
+ end
231
+
232
+ it 'holdings_gain_percent_real_time' do
233
+ apple(:holdings_gain_percent_real_time).should == [ nil, nil ]
234
+ google(:holdings_gain_percent_real_time).should == [ nil, nil ]
235
+ end
236
+
237
+ it 'holdings_gain_real_time' do
238
+ apple(:holdings_gain_real_time).should == nil
239
+ google(:holdings_gain_real_time).should == nil
240
+ end
241
+
242
+ it 'holdings_value' do
243
+ apple(:holdings_value).should == nil
244
+ google(:holdings_value).should == nil
245
+ end
246
+
247
+ it 'holdings_value_real_time' do
248
+ apple(:holdings_value_real_time).should == nil
249
+ google(:holdings_value_real_time).should == nil
250
+ end
251
+
252
+ it 'last_trade_date' do
253
+ apple(:last_trade_date).should == '5/13/2011'
254
+ google(:last_trade_date).should == '5/13/2011'
255
+ end
256
+
257
+ it 'last_trade_price_only' do
258
+ apple(:last_trade).should == '340.50'
259
+ google(:last_trade).should == '529.55'
260
+ end
261
+
262
+ it 'last_trade_real_time_with_time' do
263
+ apple(:last_trade_real_time_with_time).should == [ nil, '340.50' ]
264
+ google(:last_trade_real_time_with_time).should == [ nil, '529.55' ]
265
+ end
266
+
267
+ it 'last_trade_size' do
268
+ apple(:last_trade_size).should == '3570'
269
+ google(:last_trade_size).should == '980'
270
+ end
271
+
272
+ it 'last_trade_time' do
273
+ apple(:last_trade_time).should == '4:00pm'
274
+ google(:last_trade_time).should == '4:00pm'
275
+ end
276
+
277
+ it 'last_trade_with_time' do
278
+ apple(:last_trade_with_time).should == [ 'May 13', '340.50' ]
279
+ google(:last_trade_with_time).should == [ 'May 13', '529.55' ]
280
+ end
281
+
282
+ it 'low_52_week' do
283
+ apple(:low_52_week).should == '231.35'
284
+ google(:low_52_week).should == '433.63'
285
+ end
286
+
287
+ it 'low_limit' do
288
+ apple(:low_limit).should == nil
289
+ google(:low_limit).should == nil
290
+ end
291
+
292
+ it 'market_cap_real_time' do
293
+ apple(:market_cap_real_time).should == nil
294
+ google(:market_cap_real_time).should == nil
295
+ end
296
+
297
+ it 'market_capitalization' do
298
+ apple(:market_capitalization).should == '314900000000' # '314.9B'
299
+ google(:market_capitalization).should == '170300000000' # '170.3B'
300
+ end
301
+
302
+ it 'more_info' do
303
+ apple(:more_info).should == 'cnsprmiIed'
304
+ google(:more_info).should == 'cnprmiIed'
305
+ end
306
+
307
+ it 'moving_average_200_day' do
308
+ apple(:moving_average_200_day).should == '334.689'
309
+ google(:moving_average_200_day).should == '590.076'
310
+ end
311
+
312
+ it 'moving_average_50_day' do
313
+ apple(:moving_average_50_day).should == '343.671'
314
+ google(:moving_average_50_day).should == '553.939'
315
+ end
316
+
317
+ it 'company' do
318
+ apple(:company).should == 'Apple Inc.'
319
+ google(:company).should == 'Google Inc.'
320
+ end
321
+
322
+ it 'notes' do
323
+ apple(:notes).should == nil
324
+ google(:notes).should == nil
325
+ end
326
+
327
+ it 'one_year_target_price' do
328
+ apple(:one_year_target_price).should == '446.87'
329
+ google(:one_year_target_price).should == '701.54'
330
+ end
331
+
332
+ it 'opening_price' do
333
+ apple(:opening_price).should == '345.79'
334
+ google(:opening_price).should == '534.51'
335
+ end
336
+
337
+ it 'order_book_real_time' do
338
+ apple(:order_book_real_time).should == nil
339
+ google(:order_book_real_time).should == nil
340
+ end
341
+
342
+ it 'pe_ratio' do
343
+ apple(:pe_ratio).should == '16.51'
344
+ google(:pe_ratio).should == '19.61'
345
+ end
346
+
347
+ it 'pe_ratio_real_time' do
348
+ apple(:pe_ratio_real_time).should == nil
349
+ google(:pe_ratio_real_time).should == nil
350
+ end
351
+
352
+ it 'peg_ratio' do
353
+ apple(:peg_ratio).should == '0.67'
354
+ google(:peg_ratio).should == '0.89'
355
+ end
356
+
357
+ it 'percent_change_from_200_day_moving_average' do
358
+ apple(:percent_change_from_200_day_moving_average).should == '+1.74%'
359
+ google(:percent_change_from_200_day_moving_average).should == '-10.26%'
360
+ end
361
+
362
+ it 'percent_change_from_50_day_moving_average' do
363
+ apple(:percent_change_from_50_day_moving_average).should == '-0.92%'
364
+ google(:percent_change_from_50_day_moving_average).should == '-4.40%'
365
+ end
366
+
367
+ it 'percent_change_from_52_week_high' do
368
+ apple(:percent_change_from_52_week_high).should == '-6.69%'
369
+ google(:percent_change_from_52_week_high).should == '-17.64%'
370
+ end
371
+
372
+ it 'percent_change_from_52_week_low' do
373
+ apple(:percent_change_from_52_week_low).should == '+47.18%'
374
+ google(:percent_change_from_52_week_low).should == '+22.12%'
375
+ end
376
+
377
+ it 'previous_close' do
378
+ apple(:previous_close).should == '346.57'
379
+ google(:previous_close).should == '535.05'
380
+ end
381
+
382
+ it 'price_paid' do
383
+ apple(:price_paid).should == nil
384
+ google(:price_paid).should == nil
385
+ end
386
+
387
+ it 'price_to_book' do
388
+ apple(:price_to_book).should == '5.21'
389
+ google(:price_to_book).should == '3.49'
390
+ end
391
+
392
+ it 'price_to_eps_estimate_current_year' do
393
+ apple(:price_to_eps_estimate_current_year).should == '14.07'
394
+ google(:price_to_eps_estimate_current_year).should == '15.76'
395
+ end
396
+
397
+ it 'price_to_eps_estimate_next_year' do
398
+ apple(:price_to_eps_estimate_next_year).should == '12.16'
399
+ google(:price_to_eps_estimate_next_year).should == '13.53'
400
+ end
401
+
402
+ it 'price_to_sales' do
403
+ apple(:price_to_sales).should == '3.66'
404
+ google(:price_to_sales).should == '5.53'
405
+ end
406
+
407
+ it 'range_52_week' do
408
+ apple(:range_52_week).should == [ '231.35', '364.90' ]
409
+ google(:range_52_week).should == [ '433.63', '642.96' ]
410
+ end
411
+
412
+ it 'shares_owned' do
413
+ apple(:shares_owned).should == nil
414
+ google(:shares_owned).should == nil
415
+ end
416
+
417
+ it 'short_ratio' do
418
+ apple(:short_ratio).should == '0.80'
419
+ google(:short_ratio).should == '1.20'
420
+ end
421
+
422
+ it 'stock_exchange' do
423
+ apple(:stock_exchange).should == 'NasdaqNM'
424
+ google(:stock_exchange).should == 'NasdaqNM'
425
+ end
426
+
427
+ it 'symbol' do
428
+ apple(:symbol).should == 'AAPL'
429
+ google(:symbol).should == 'GOOG'
430
+ end
431
+
432
+ it 'ticker_trend' do
433
+ apple(:ticker_trend).should == '======'
434
+ google(:ticker_trend).should == '======'
435
+ end
436
+
437
+ it 'trade_date' do
438
+ apple(:trade_date).should == nil
439
+ google(:trade_date).should == nil
440
+ end
441
+
442
+ it 'volume' do
443
+ apple(:volume).should == '11649708'
444
+ google(:volume).should == '2107641'
445
+ end
446
+ end
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: market_beat
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Michael Dvorkin
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-12-21 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &70263207751320 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 2.6.0
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70263207751320
25
+ description: Fetch real-time and delayed stock quotes and 100+ other financial and
26
+ market indicaors from publicly available sources.
27
+ email: mike@dvorkin.net
28
+ executables: []
29
+ extensions: []
30
+ extra_rdoc_files: []
31
+ files:
32
+ - CHANGELOG
33
+ - Gemfile
34
+ - Gemfile.lock
35
+ - LICENSE
36
+ - Rakefile
37
+ - README.md
38
+ - lib/market_beat/google.rb
39
+ - lib/market_beat/version.rb
40
+ - lib/market_beat/yahoo.rb
41
+ - lib/market_beat.rb
42
+ - spec/google_spec.rb
43
+ - spec/market_beat_spec.rb
44
+ - spec/spec_helper.rb
45
+ - spec/yahoo_spec.rb
46
+ - .gitignore
47
+ homepage: http://github.com/michaeldv/market_beat
48
+ licenses: []
49
+ post_install_message:
50
+ rdoc_options: []
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ! '>='
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ! '>='
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ requirements: []
66
+ rubyforge_project: market_beat
67
+ rubygems_version: 1.8.11
68
+ signing_key:
69
+ specification_version: 3
70
+ summary: Fetch up-to-date stock quotes and 100+ financial and market indicators.
71
+ test_files:
72
+ - spec/google_spec.rb
73
+ - spec/market_beat_spec.rb
74
+ - spec/spec_helper.rb
75
+ - spec/yahoo_spec.rb