bloomberg_quote 0.0.1
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 +4 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +61 -0
- data/Rakefile +1 -0
- data/bloomberg_quote.gemspec +28 -0
- data/lib/bloomberg_quote/version.rb +3 -0
- data/lib/bloomberg_quote.rb +95 -0
- data/spec/bloomberg_quote_spec.rb +165 -0
- data/spec/fakeweb/1557:JP.html +2456 -0
- data/spec/fakeweb/9437:JP.html +2497 -0
- data/spec/fakeweb/tpx.html +2374 -0
- data/spec/fakeweb/usdjpy.html +3625 -0
- data/spec/spec_helper.rb +11 -0
- metadata +99 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 MATSUI Shinsuke
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# BloombergQuote
|
2
|
+
|
3
|
+
BloombergQuote is Ruby module which getting quotes form Bloomberg site
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'bloomberg_quote'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install bloomberg_quote
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
require 'bloomberg_quote'
|
23
|
+
# Fetching TOPIX Index quote
|
24
|
+
quote = BloombergQuote::Quote.new('TPX:IND')
|
25
|
+
quote.valid?
|
26
|
+
# => true
|
27
|
+
quote.data['Price']
|
28
|
+
# => 813.33
|
29
|
+
quote.data['Previous Close']
|
30
|
+
# => 819.27
|
31
|
+
quote.data['Open']
|
32
|
+
# => 814.37
|
33
|
+
# Fetching DUMMY currency
|
34
|
+
quote = BloombergQuote::Quote.new('DUMMY:CUR')
|
35
|
+
quote.valid?
|
36
|
+
# => false
|
37
|
+
```
|
38
|
+
|
39
|
+
## Supported fields
|
40
|
+
|
41
|
+
- Price
|
42
|
+
- Previous Close
|
43
|
+
- Open
|
44
|
+
|
45
|
+
## FAQ
|
46
|
+
|
47
|
+
### Where can I find ticker symbols?
|
48
|
+
|
49
|
+
You can search them at [Bloomberg](http://www.bloomberg.com/)
|
50
|
+
|
51
|
+
## Contributing
|
52
|
+
|
53
|
+
1. Fork it
|
54
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
55
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
56
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
57
|
+
5. Create new Pull Request
|
58
|
+
|
59
|
+
## Acknowledgement
|
60
|
+
|
61
|
+
Code and APIs are inspired by [yahoo_quote](https://github.com/bcarreno/yahoo_quote).
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "bloomberg_quote/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "bloomberg_quote"
|
7
|
+
s.version = BloombergQuote::VERSION
|
8
|
+
s.authors = ["MATSUI Shinsuke"]
|
9
|
+
s.email = ["poppen.jp@gmail.com"]
|
10
|
+
s.homepage = "https://github.com/poppen/bloomberg_quote"
|
11
|
+
s.summary = "getting quotes form Bloomberg site"
|
12
|
+
s.description = "BloombergQuote is a module which getting quotes form Bloomberg site"
|
13
|
+
|
14
|
+
s.rubyforge_project = "bloomberg_quote"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_development_dependency "rspec"
|
22
|
+
s.add_development_dependency "fakeweb"
|
23
|
+
s.add_runtime_dependency "nokogiri"
|
24
|
+
|
25
|
+
# specify any dependencies here; for example:
|
26
|
+
# s.add_development_dependency "rspec"
|
27
|
+
# s.add_runtime_dependency "rest-client"
|
28
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require "bloomberg_quote/version"
|
2
|
+
require "open-uri"
|
3
|
+
require "nokogiri"
|
4
|
+
require "json"
|
5
|
+
|
6
|
+
module BloombergQuote
|
7
|
+
class Quote
|
8
|
+
def initialize(symbol)
|
9
|
+
@symbol = symbol
|
10
|
+
pull_data
|
11
|
+
end
|
12
|
+
|
13
|
+
def valid?
|
14
|
+
return false unless @data
|
15
|
+
@data.size > 0
|
16
|
+
end
|
17
|
+
|
18
|
+
def data
|
19
|
+
@data.nil? ? {} : @data
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
def pull_data
|
24
|
+
return if @symbol.empty?
|
25
|
+
|
26
|
+
case @symbol
|
27
|
+
when /:IND\Z/
|
28
|
+
pull_index_data
|
29
|
+
when /:CUR\Z/
|
30
|
+
pull_currency_data
|
31
|
+
else
|
32
|
+
pull_stock
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def pull_index_data
|
37
|
+
@data = {}
|
38
|
+
|
39
|
+
fetch_page
|
40
|
+
return unless @doc.css('div.ticker_header > span.price').text.
|
41
|
+
gsub(',','') =~ /(\d+\.?(?:\d+))/
|
42
|
+
@data["Price"] = $1.to_f
|
43
|
+
|
44
|
+
json = parse_json
|
45
|
+
@data["Previous Close"] = json["prev_close"].to_f
|
46
|
+
@data["Open"] = json["data_values"][0][1].to_f
|
47
|
+
end
|
48
|
+
|
49
|
+
def pull_currency_data
|
50
|
+
@data = {}
|
51
|
+
|
52
|
+
fetch_page
|
53
|
+
return unless @doc.css('div.ticker_header_currency > span.price').
|
54
|
+
text =~ /(\d+\.?(?:\d+))/
|
55
|
+
@data["Price"] = $1.to_f
|
56
|
+
|
57
|
+
json = parse_json
|
58
|
+
@data["Previous Close"] = json["prev_close"].to_f
|
59
|
+
@data["Open"] = json["data_values"][0][1].to_f
|
60
|
+
end
|
61
|
+
|
62
|
+
def pull_stock
|
63
|
+
@data = {}
|
64
|
+
|
65
|
+
fetch_page
|
66
|
+
return unless @doc.css('div.ticker_header > span.price').text.
|
67
|
+
gsub(',','') =~ /(\d+\.?(?:\d+))/
|
68
|
+
@data["Price"] = $1.to_f
|
69
|
+
|
70
|
+
json = parse_json
|
71
|
+
@data["Previous Close"] = json["prev_close"].to_f
|
72
|
+
@data["Open"] = json["data_values"][0][1].to_f
|
73
|
+
end
|
74
|
+
|
75
|
+
def parse_json
|
76
|
+
inline_scripts = @doc.xpath('//script[not(@src)]').map(&:text)
|
77
|
+
inline_scripts.find do |js|
|
78
|
+
js =~ /BLOOMBERG\.global_var\.chartData\s+?=\s+?(.+?);/
|
79
|
+
end
|
80
|
+
JSON.parse($1)
|
81
|
+
end
|
82
|
+
|
83
|
+
def fetch_page
|
84
|
+
@doc = Nokogiri::HTML(open_url)
|
85
|
+
end
|
86
|
+
|
87
|
+
def open_url
|
88
|
+
open(quote_url)
|
89
|
+
end
|
90
|
+
|
91
|
+
def quote_url
|
92
|
+
"http://www.bloomberg.com/quote/#{@symbol}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'bloomberg_quote'
|
3
|
+
require 'fakeweb'
|
4
|
+
|
5
|
+
describe BloombergQuote do
|
6
|
+
describe 'Indexes' do
|
7
|
+
describe '#valid?' do
|
8
|
+
context 'when wrong symbol' do
|
9
|
+
it { BloombergQuote::Quote.new('DUMMY:IND').valid?.should be_false }
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'when right symbol' do
|
13
|
+
it { BloombergQuote::Quote.new('TPX:IND').valid?.should be_true }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#data' do
|
18
|
+
context 'when wrong symbol' do
|
19
|
+
it { BloombergQuote::Quote.new('DUMMY:IND').data.should be_empty }
|
20
|
+
end
|
21
|
+
|
22
|
+
shared_examples_for 'TPX:IND data' do |options|
|
23
|
+
subject { BloombergQuote::Quote.new('TPX:IND').data }
|
24
|
+
it { should_not be_empty }
|
25
|
+
it { should have_key('Price') }
|
26
|
+
it { should have_key('Previous Close') }
|
27
|
+
it { should have_key('Open') }
|
28
|
+
its(['Price']) { should be_instance_of(Float) }
|
29
|
+
its(['Previous Close']) { should be_instance_of(Float) }
|
30
|
+
its(['Open']) { should be_instance_of(Float) }
|
31
|
+
|
32
|
+
if options.has_key?(:use_fakeweb) && options[:use_fakeweb]
|
33
|
+
its(['Price']) { should == 813.33 }
|
34
|
+
its(['Previous Close']) { should == 819.27 }
|
35
|
+
its(['Open']) { should == 814.37 }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'when right symbol' do
|
40
|
+
it_should_behave_like 'TPX:IND data', {}
|
41
|
+
|
42
|
+
describe 'values' do
|
43
|
+
before(:all) do
|
44
|
+
FakeWeb.register_uri(
|
45
|
+
:get, "http://www.bloomberg.com/quote/TPX:IND",
|
46
|
+
:body => File.read('spec/fakeweb/tpx.html'))
|
47
|
+
end
|
48
|
+
|
49
|
+
after(:all) do
|
50
|
+
FakeWeb.clean_registry
|
51
|
+
end
|
52
|
+
|
53
|
+
it_should_behave_like 'TPX:IND data', :use_fakeweb => true
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe 'Currency' do
|
60
|
+
describe '#valid?' do
|
61
|
+
context 'when wrong symbol' do
|
62
|
+
it { BloombergQuote::Quote.new('DUMMY:CUR').valid?.should be_false }
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'when right symbol' do
|
66
|
+
it { BloombergQuote::Quote.new('USDJPY:CUR').valid?.should be_true }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe '#data' do
|
71
|
+
context 'when wrong symbol' do
|
72
|
+
it { BloombergQuote::Quote.new('DUMMY:CUR').data.should be_empty }
|
73
|
+
end
|
74
|
+
|
75
|
+
shared_examples_for 'USDJPY:CUR data' do |options|
|
76
|
+
subject { BloombergQuote::Quote.new('USDJPY:CUR').data }
|
77
|
+
it { should_not be_empty }
|
78
|
+
it { should have_key('Price') }
|
79
|
+
it { should have_key('Previous Close') }
|
80
|
+
it { should have_key('Open') }
|
81
|
+
its(['Price']) { should be_instance_of(Float) }
|
82
|
+
its(['Previous Close']) { should be_instance_of(Float) }
|
83
|
+
its(['Open']) { should be_instance_of(Float) }
|
84
|
+
|
85
|
+
if options.has_key?(:use_fakeweb) && options[:use_fakeweb]
|
86
|
+
its(['Price']) { should == 81.68 }
|
87
|
+
its(['Previous Close']) { should == 81.26 }
|
88
|
+
its(['Open']) { should == 81.26 }
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'when right symbol' do
|
93
|
+
it_should_behave_like 'USDJPY:CUR data', {}
|
94
|
+
|
95
|
+
describe 'values' do
|
96
|
+
before(:all) do
|
97
|
+
FakeWeb.register_uri(
|
98
|
+
:get, "http://www.bloomberg.com/quote/USDJPY:CUR",
|
99
|
+
:body => File.read('spec/fakeweb/usdjpy.html'))
|
100
|
+
end
|
101
|
+
|
102
|
+
after(:all) do
|
103
|
+
FakeWeb.clean_registry
|
104
|
+
end
|
105
|
+
|
106
|
+
it_should_behave_like 'USDJPY:CUR data', :use_fakeweb => true
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe 'Stock' do
|
113
|
+
describe '#valid?' do
|
114
|
+
context 'when wrong symbol' do
|
115
|
+
it { BloombergQuote::Quote.new('DUMMY:JP').valid?.should be_false }
|
116
|
+
end
|
117
|
+
|
118
|
+
context 'when right symbol' do
|
119
|
+
it { BloombergQuote::Quote.new('9437:JP').valid?.should be_true }
|
120
|
+
it { BloombergQuote::Quote.new('1557:JP').valid?.should be_true }
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe '#data' do
|
125
|
+
context 'when wrong symbol' do
|
126
|
+
it { BloombergQuote::Quote.new('DUMMY:JP').data.should be_empty }
|
127
|
+
end
|
128
|
+
|
129
|
+
shared_examples_for 'stock data' do |symbol, data|
|
130
|
+
subject { BloombergQuote::Quote.new(symbol).data }
|
131
|
+
it { should_not be_empty }
|
132
|
+
it { should have_key('Price') }
|
133
|
+
it { should have_key('Previous Close') }
|
134
|
+
it { should have_key('Open') }
|
135
|
+
its(['Price']) { should be_instance_of(Float) }
|
136
|
+
its(['Previous Close']) { should be_instance_of(Float) }
|
137
|
+
its(['Open']) { should be_instance_of(Float) }
|
138
|
+
|
139
|
+
describe 'values' do
|
140
|
+
before(:all) do
|
141
|
+
FakeWeb.register_uri(
|
142
|
+
:get, "http://www.bloomberg.com/quote/#{symbol}",
|
143
|
+
:body => File.read("spec/fakeweb/#{symbol}.html"))
|
144
|
+
end
|
145
|
+
|
146
|
+
after(:all) do
|
147
|
+
FakeWeb.clean_registry
|
148
|
+
end
|
149
|
+
|
150
|
+
its(['Price']) { should == data[:price] }
|
151
|
+
its(['Previous Close']) { should == data[:previous_close] }
|
152
|
+
its(['Open']) { should == data[:open] }
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
context 'when right symbol' do
|
157
|
+
it_should_behave_like 'stock data', '9437:JP',
|
158
|
+
{:price => 136300, :previous_close => 136600, :open => 137100}
|
159
|
+
|
160
|
+
it_should_behave_like 'stock data', '1557:JP',
|
161
|
+
{:price => 11270, :previous_close => 11290, :open => 11420 }
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|