medici 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/lib/medici/errors.rb +13 -0
- data/lib/medici/historical.rb +52 -0
- data/lib/medici/stock.rb +92 -0
- data/lib/medici.rb +18 -0
- metadata +68 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module Medici
|
4
|
+
class Historical
|
5
|
+
API_BASE_URL = 'http://www.google.com/finance/historical?'
|
6
|
+
|
7
|
+
attr_reader :date, :open, :high, :low, :close, :volume
|
8
|
+
|
9
|
+
def self.quote(symbol, start_date = nil, end_date = nil)
|
10
|
+
request = self.build_request(symbol, start_date, end_date)
|
11
|
+
response = Net::HTTP.get_response(URI.parse(request))
|
12
|
+
raise HistoricalDataError if response.code != '200'
|
13
|
+
|
14
|
+
self.parse(response.body)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def self.build_request(symbol, start_date, end_date)
|
20
|
+
params = {
|
21
|
+
q: symbol,
|
22
|
+
startdate: start_date,
|
23
|
+
enddate: end_date,
|
24
|
+
output: 'csv'
|
25
|
+
}
|
26
|
+
|
27
|
+
API_BASE_URL + params.each_with_object([]) do |(p, v), acc|
|
28
|
+
acc << "#{p}=#{v}" if not v.nil?
|
29
|
+
end.join('&')
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.parse(response)
|
33
|
+
rows = response.split("\n")
|
34
|
+
rows.shift
|
35
|
+
rows.map do |row|
|
36
|
+
new(row.split(','))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private_class_method :new
|
41
|
+
|
42
|
+
def initialize(attributes)
|
43
|
+
@date = Date.parse(attributes[0])
|
44
|
+
@open = attributes[1].to_f
|
45
|
+
@high = attributes[2].to_f
|
46
|
+
@low = attributes[3].to_f
|
47
|
+
@close = attributes[4].to_f
|
48
|
+
@volume = attributes[5].to_i
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
data/lib/medici/stock.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'xmlsimple'
|
3
|
+
require 'date'
|
4
|
+
require 'time'
|
5
|
+
|
6
|
+
module Medici
|
7
|
+
class Stock
|
8
|
+
API_BASE_URL = 'http://www.google.com/ig/api?'
|
9
|
+
|
10
|
+
PARSERS = {
|
11
|
+
identity: lambda { |_| _ },
|
12
|
+
to_int: lambda { |s| s.to_i },
|
13
|
+
to_float: lambda { |s| s.to_f },
|
14
|
+
to_date: lambda { |s| s.empty? ? nil : Date.parse(s) },
|
15
|
+
to_time: lambda { |s| s.empty? ? nil : Time.parse(s.scan(/.{2}/).join(':')) },
|
16
|
+
to_bool: lambda { |s| s.match(/(true|t|yes|y|1)$/i) != nil }
|
17
|
+
}
|
18
|
+
|
19
|
+
QUOTE_ATTRIBUTES = [
|
20
|
+
:symbol, :pretty_symbol, :symbol_lookup_url, :company, :exchange, :exchange_timezone,
|
21
|
+
:exchange_utc_offset, :exchange_closing, :divisor, :currency, :last, :high, :low,
|
22
|
+
:volume, :avg_volume, :market_cap, :open, :y_close, :change, :perc_change, :delay,
|
23
|
+
:trade_timestamp, :trade_date_utc, :trade_time_utc, :current_date_utc, :current_time_utc,
|
24
|
+
:symbol_url, :chart_url, :disclaimer_url, :ecn_url, :isld_last, :isld_trade_date_utc,
|
25
|
+
:isld_trade_time_utc, :brut_last, :brut_trade_date_utc, :brut_trade_time_utc, :daylight_savings
|
26
|
+
]
|
27
|
+
|
28
|
+
PARSE_MAP = {
|
29
|
+
exchange_closing: PARSERS[:to_int],
|
30
|
+
divisor: PARSERS[:to_int],
|
31
|
+
last: PARSERS[:to_float],
|
32
|
+
high: PARSERS[:to_float],
|
33
|
+
low: PARSERS[:to_float],
|
34
|
+
volume: PARSERS[:to_int],
|
35
|
+
avg_volume: PARSERS[:to_int],
|
36
|
+
market_cap: PARSERS[:to_float],
|
37
|
+
open: PARSERS[:to_float],
|
38
|
+
y_close: PARSERS[:to_float],
|
39
|
+
change: PARSERS[:to_float],
|
40
|
+
perc_change: PARSERS[:to_float],
|
41
|
+
delay: PARSERS[:to_int],
|
42
|
+
isld_last: PARSERS[:to_float],
|
43
|
+
trade_date_utc: PARSERS[:to_date],
|
44
|
+
trade_time_utc: PARSERS[:to_time],
|
45
|
+
current_date_utc: PARSERS[:to_date],
|
46
|
+
current_time_utc: PARSERS[:to_time],
|
47
|
+
isld_trade_date_utc: PARSERS[:to_date],
|
48
|
+
isld_trade_time_utc: PARSERS[:to_time],
|
49
|
+
daylight_savings: PARSERS[:to_bool]
|
50
|
+
}
|
51
|
+
PARSE_MAP.default = PARSERS[:identity]
|
52
|
+
|
53
|
+
QUOTE_ATTRIBUTES.each do |attribute|
|
54
|
+
attr_reader attribute
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.quotes(symbols)
|
58
|
+
query = symbols.map { |symbol| "stock=#{symbol}" }.join('&')
|
59
|
+
response = Net::HTTP.get_response(URI.parse(API_BASE_URL + query))
|
60
|
+
|
61
|
+
self.parse(response.body)
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.quote(symbol)
|
65
|
+
self.quotes([symbol]).first
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def self.parse(xml_data)
|
71
|
+
doc = XmlSimple.xml_in(xml_data)
|
72
|
+
doc['finance'].map do |stock|
|
73
|
+
attributes = stock.each_with_object({}) do |(attribute, value), hash|
|
74
|
+
hash[attribute] = value.first['data'] if QUOTE_ATTRIBUTES.include? attribute.to_sym
|
75
|
+
end
|
76
|
+
raise StockSymbolError.new(attributes['symbol']) if attributes['exchange'] == 'UNKNOWN EXCHANGE'
|
77
|
+
|
78
|
+
new(attributes)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
private_class_method :new
|
83
|
+
|
84
|
+
def initialize(attributes)
|
85
|
+
attributes.each do |attribute, value|
|
86
|
+
parser = PARSE_MAP[attribute.to_sym]
|
87
|
+
instance_variable_set("@#{attribute}", parser.call(value))
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
data/lib/medici.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'medici/stock'
|
2
|
+
require 'medici/historical'
|
3
|
+
|
4
|
+
module Medici
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def quotes(symbols)
|
8
|
+
Stock.quotes(symbols)
|
9
|
+
end
|
10
|
+
|
11
|
+
def quote(symbol)
|
12
|
+
Stock.quote(symbol)
|
13
|
+
end
|
14
|
+
|
15
|
+
def historical(symbol, start_date = nil, end_date = nil)
|
16
|
+
Historical.quote(symbol, start_date, end_date)
|
17
|
+
end
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: medici
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Timothy Spratt
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-02-24 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: xml-simple
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
description: Medici is a library for obtaining stock quotes and historical stock data
|
31
|
+
using the undocumented Google Finance API.
|
32
|
+
email: tim@onlysix.co.uk
|
33
|
+
executables: []
|
34
|
+
extensions: []
|
35
|
+
extra_rdoc_files: []
|
36
|
+
files:
|
37
|
+
- lib/medici.rb
|
38
|
+
- lib/medici/errors.rb
|
39
|
+
- lib/medici/historical.rb
|
40
|
+
- lib/medici/stock.rb
|
41
|
+
homepage: http://github.com/timspratt/medici
|
42
|
+
licenses:
|
43
|
+
- MIT
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options: []
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ! '>='
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
requirements: []
|
61
|
+
rubyforge_project:
|
62
|
+
rubygems_version: 1.8.23
|
63
|
+
signing_key:
|
64
|
+
specification_version: 3
|
65
|
+
summary: ! '["Simple real-time stock quotes from the Google Finance API.", "Medici
|
66
|
+
is a library for obtaining stock quotes and historical stock data using the undocumented
|
67
|
+
Google Finance API.", ["Timothy Spratt"]]'
|
68
|
+
test_files: []
|