market_beat 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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