google-finance-ruby-client 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e1e3de285dc0df2900f5f75481d521d70bd9c7d4
4
- data.tar.gz: 6d83ab892591d4def05596980920ec6a1691d6ae
3
+ metadata.gz: 463be825564f209ff8cec24ec5578bd4d9bdfcf2
4
+ data.tar.gz: aa5cf58b7ddbcf96b4e54c60c5a6ed08acc8840b
5
5
  SHA512:
6
- metadata.gz: f83a316a809c9e13019648aa51546a4f25c255a22a4e0b009b8c618daff3d6f50ec5425f8ca971c5862dc99364725908a75212dd92749af7248721dd5a62a2a4
7
- data.tar.gz: fb4fc664048cdd77f7ed0314dfcb60ac46115f2505a378c3f2e849d05e9674e14a0cd583a21d7faf3cd69ee584d9e9e71abc1b23150ec57a5a4bf92c403c28fb
6
+ metadata.gz: e062d6d86755af3769280155d65d81207417511a96d0cc19c6fe9c0252635c166a7d9ae91c8fb1bc31156115906d967d88e26b0f4948a4825c094fbf8449131e
7
+ data.tar.gz: 3a16a1a953473207ee55ce8149aeac605797d8af54013fb252094be6b1dc834c4c9a810fbeca6f5c257c34eeba0dc2483b1ccd2081901e00bcc0767c91087dd5
@@ -1,39 +1,61 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2017-12-02 20:20:41 -0500 using RuboCop version 0.51.0.
3
+ # on 2017-12-28 15:14:15 -0500 using RuboCop version 0.51.0.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
7
7
  # versions of RuboCop, may require this file to be generated again.
8
8
 
9
+ # Offense count: 8
10
+ Lint/AmbiguousOperator:
11
+ Exclude:
12
+ - 'spec/google-finance/prices_spec.rb'
13
+
9
14
  # Offense count: 1
10
15
  Lint/AmbiguousRegexpLiteral:
11
16
  Exclude:
12
17
  - 'lib/google-finance/faraday_middleware/preprocessor.rb'
13
18
 
14
- # Offense count: 2
19
+ # Offense count: 1
20
+ # Cop supports --auto-correct.
21
+ # Configuration parameters: EnforcedStyleAlignWith, SupportedStylesAlignWith, AutoCorrect.
22
+ # SupportedStylesAlignWith: keyword, variable, start_of_line
23
+ Lint/EndAlignment:
24
+ Exclude:
25
+ - 'lib/google-finance/prices.rb'
26
+
27
+ # Offense count: 3
15
28
  Metrics/AbcSize:
16
- Max: 20
29
+ Max: 77
17
30
 
18
- # Offense count: 2
31
+ # Offense count: 5
19
32
  # Configuration parameters: CountComments, ExcludedMethods.
20
33
  Metrics/BlockLength:
21
- Max: 35
34
+ Max: 83
35
+
36
+ # Offense count: 1
37
+ # Configuration parameters: CountBlocks.
38
+ Metrics/BlockNesting:
39
+ Max: 4
22
40
 
23
- # Offense count: 7
41
+ # Offense count: 1
42
+ Metrics/CyclomaticComplexity:
43
+ Max: 29
44
+
45
+ # Offense count: 26
24
46
  # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
25
47
  # URISchemes: http, https
26
48
  Metrics/LineLength:
27
- Max: 132
49
+ Max: 153
28
50
 
29
- # Offense count: 3
51
+ # Offense count: 4
30
52
  # Configuration parameters: CountComments.
31
53
  Metrics/MethodLength:
32
- Max: 14
54
+ Max: 77
33
55
 
34
- # Offense count: 1
56
+ # Offense count: 2
35
57
  Metrics/PerceivedComplexity:
36
- Max: 8
58
+ Max: 18
37
59
 
38
60
  # Offense count: 1
39
61
  # Configuration parameters: ExpectMatchingDefinition, Regex, IgnoreExecutableScripts, AllowedAcronyms.
@@ -42,17 +64,20 @@ Naming/FileName:
42
64
  Exclude:
43
65
  - 'lib/google-finance-ruby-client.rb'
44
66
 
45
- # Offense count: 6
67
+ # Offense count: 9
46
68
  Style/Documentation:
47
69
  Exclude:
48
70
  - 'spec/**/*'
49
71
  - 'test/**/*'
72
+ - 'lib/google-finance/api/get_prices.rb'
73
+ - 'lib/google-finance/api/index.rb'
50
74
  - 'lib/google-finance/errors/symbol_not_found_error.rb'
51
75
  - 'lib/google-finance/errors/symbols_not_found_error.rb'
52
76
  - 'lib/google-finance/faraday_middleware/preprocessor.rb'
77
+ - 'lib/google-finance/price.rb'
78
+ - 'lib/google-finance/prices.rb'
53
79
  - 'lib/google-finance/quote.rb'
54
80
  - 'lib/google-finance/quotes.rb'
55
- - 'lib/google-finance/resources.rb'
56
81
 
57
82
  # Offense count: 1
58
83
  # Configuration parameters: MinBodyLength.
@@ -60,6 +85,15 @@ Style/GuardClause:
60
85
  Exclude:
61
86
  - 'lib/google-finance/quote.rb'
62
87
 
88
+ # Offense count: 1
89
+ # Cop supports --auto-correct.
90
+ # Configuration parameters: AutoCorrect, EnforcedStyle, SupportedStyles.
91
+ # SupportedStyles: predicate, comparison
92
+ Style/NumericPredicate:
93
+ Exclude:
94
+ - 'spec/**/*'
95
+ - 'lib/google-finance/prices.rb'
96
+
63
97
  # Offense count: 1
64
98
  # Cop supports --auto-correct.
65
99
  # Configuration parameters: EnforcedStyle, SupportedStyles, AllowInnerSlashes.
@@ -1,3 +1,7 @@
1
+ ### 0.2.0 (2017/12/28)
2
+
3
+ * [#1](https://github.com/dblock/google-finance-ruby-client/issues/1): Retrieve history of prices - [@dblock](https://github.com/dblock).
4
+
1
5
  ### 0.1.0 (2017/12/02)
2
6
 
3
7
  * Initial public release - [@dblock](https://github.com/dblock).
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  Google Finance Ruby Client
2
2
  ==========================
3
3
 
4
- [![Gem Version](https://badge.fury.io/rb/google-finance-ruby-client.svg)](http://badge.fury.io/rb/google-finance-ruby-client)
4
+ [![Gem Version](https://badge.fury.io/rb/google-finance-ruby-client.svg)](https://badge.fury.io/rb/google-finance-ruby-client)
5
5
  [![Build Status](https://travis-ci.org/dblock/google-finance-ruby-client.svg?branch=master)](https://travis-ci.org/dblock/google-finance-ruby-client)
6
6
 
7
7
  A Ruby client for the undocumented Google Finance web API that attempts to make sense of the data.
@@ -33,7 +33,7 @@ quote.change_in_percent # 0.11
33
33
  quote.change_in_percent_s # "+0.11%"
34
34
  ```
35
35
 
36
- See [quote.rb](lib/google_finance/quote.rb) for more fields.
36
+ See [quote.rb](lib/google-finance/quote.rb) for returned fields.
37
37
 
38
38
  If a symbol cannot be found a [GoogleFinance::Errors::SymbolNotFound](lib/google-finance/errors/symbol_not_found_error.rb) is raised.
39
39
 
@@ -52,6 +52,56 @@ quotes[1] # GoogleFinance::Quote.get('AB')
52
52
 
53
53
  If one of the symbols cannot be found a [GoogleFinance::Errors::SymbolsNotFound](lib/google-finance/errors/symbols_not_found_error.rb) is raised.
54
54
 
55
+ ### Get Price History
56
+
57
+ Fetches price history for a ticker.
58
+
59
+ ```ruby
60
+ prices = GoogleFinance::Prices.get('MSFT')
61
+
62
+ prices.exchange # NASDAQ
63
+
64
+ # prices for the last month of open markets
65
+ prices.count # 21
66
+
67
+ # prices appear in reverse chronological order
68
+ prices.last #<GoogleFinance::Price close=85.71 date=2017-12-27 16:00:00 -0500 high=85.98 low=85.215 open=85.65 volume=14678025>
69
+ prices[-2] #<GoogleFinance::Price close=85.4 date=2017-12-26 16:00:00 -0500 high=85.5346 low=85.03 open=85.31 volume=9891237>
70
+ ```
71
+
72
+ See [price.rb](lib/google-finance/price.rb) for returned fields.
73
+
74
+ If a symbol cannot be found a [GoogleFinance::Errors::SymbolNotFound](lib/google-finance/errors/symbol_not_found_error.rb) is raised.
75
+
76
+ The following options are supported.
77
+
78
+ * `exchange`: stock exchange symbol on which stock is traded, eg. `NASDAQ`
79
+ * `interval`: interval size in seconds
80
+ * `period`: period, a number followed by `d` (days) or `Y` (years)
81
+ * `fields`: array of data to return
82
+ * `date`: timestamp
83
+ * `open`: price at market open
84
+ * `close`: price at market close
85
+ * `volume`: volume
86
+ * `low`: low price
87
+ * `high`: high price
88
+
89
+ Retrieve prices for a year in 1 hour intervals.
90
+
91
+ ```ruby
92
+ prices = GoogleFinance::Prices.get('GOOG', interval: 60 * 60, period: '1Y', fields: [:date, :close, :volume, :open, :high, :low])
93
+
94
+ prices.count # 1755
95
+ ```
96
+
97
+ Retrieve only prices at market close.
98
+
99
+ ```ruby
100
+ prices = GoogleFinance::Prices.get('GOOG', fields: [:days, :close])
101
+
102
+ prices.first # #<GoogleFinance::Price close=1047.41 date=2017-11-28 16:00:00 -0500>
103
+ ```
104
+
55
105
  ## Contributing
56
106
 
57
107
  See [CONTRIBUTING](CONTRIBUTING.md).
@@ -5,7 +5,9 @@ require 'hashie'
5
5
  require_relative 'google-finance/version'
6
6
  require_relative 'google-finance/faraday_middleware/preprocessor'
7
7
  require_relative 'google-finance/errors'
8
- require_relative 'google-finance/resources'
8
+ require_relative 'google-finance/api'
9
9
  require_relative 'google-finance/resource'
10
10
  require_relative 'google-finance/quote'
11
11
  require_relative 'google-finance/quotes'
12
+ require_relative 'google-finance/price'
13
+ require_relative 'google-finance/prices'
@@ -0,0 +1,2 @@
1
+ require_relative 'api/index'
2
+ require_relative 'api/get_prices'
@@ -0,0 +1,46 @@
1
+ module GoogleFinance
2
+ module Api
3
+ module GetPrices
4
+ #
5
+ # Retrieve prices.
6
+ #
7
+ # http://www.networkerror.org/component/content/article/1-technical-wootness/44-googles-undocumented-finance-api.html
8
+ # https://gist.github.com/lebedov/f09030b865c4cb142af1
9
+ #
10
+ # q: stock symbol
11
+ # x: stock exchange symbol on which stock is traded, eg. NASD
12
+ # i: interval size in seconds
13
+ # p: period, a number followed by d (days) or Y (years)
14
+ # f: data to return
15
+ # d: timestamp or interval
16
+ # c: close
17
+ # v: volume
18
+ # l: low
19
+ # o: open
20
+ # df: ?, eg. cpct
21
+ # auto: ?
22
+ # ei: ?
23
+ # ts: starting timetamp in unix format, default to today
24
+ #
25
+ # The output includes a header that describes the columns, timezone offset, and a few other interesting bits of information.
26
+ # The data rows are basically CSV format.
27
+ def self.fetch(params)
28
+ connection.get do |c|
29
+ c.params.merge!(params)
30
+ end.body
31
+ end
32
+
33
+ def self.connection
34
+ Faraday.new(
35
+ url: 'https://finance.google.com/finance/getprices',
36
+ request: {
37
+ params_encoder: Faraday::FlatParamsEncoder
38
+ }
39
+ ) do |c|
40
+ c.use Faraday::Response::RaiseError
41
+ c.use Faraday::Adapter::NetHttp
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,26 @@
1
+ module GoogleFinance
2
+ module Api
3
+ module Index
4
+ def self.get(params)
5
+ connection.get do |c|
6
+ c.params[:output] = :json
7
+ c.params.merge!(params)
8
+ end.body
9
+ end
10
+
11
+ def self.connection
12
+ Faraday.new(
13
+ url: 'https://finance.google.com/finance',
14
+ request: {
15
+ params_encoder: Faraday::FlatParamsEncoder
16
+ }
17
+ ) do |c|
18
+ c.use ::FaradayMiddleware::ParseJson
19
+ c.use GoogleFinance::FaradayMiddleware::Preprocessor
20
+ c.use Faraday::Response::RaiseError
21
+ c.use Faraday::Adapter::NetHttp
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,13 @@
1
+ module GoogleFinance
2
+ class Price < Resource
3
+ property 'timezone_offset'
4
+ property 'date'
5
+ property 'close'
6
+ property 'symbol'
7
+ property 'high'
8
+ property 'low'
9
+ property 'open'
10
+ property 'volume'
11
+ property 'cdays'
12
+ end
13
+ end
@@ -0,0 +1,97 @@
1
+ module GoogleFinance
2
+ class Prices < Array
3
+ extend Forwardable
4
+
5
+ def_delegators :@headers, :exchange, :market_open_minute, :market_close_minute, :interval, :columns, :timezone_offset
6
+
7
+ def initialize(headers, values = [])
8
+ @headers = OpenStruct.new(headers)
9
+ super values
10
+ end
11
+
12
+ def self.get(symbol, params = {})
13
+ query = {}
14
+ params.each_pair do |k, v|
15
+ case k
16
+ when :exchange, :x then
17
+ query[:x] = v
18
+ when :interval, :i then
19
+ query[:i] = v
20
+ when :period, :p then
21
+ query[:p] = v
22
+ when :df then
23
+ query[:df] = v
24
+ when :auto then
25
+ query[:auto] = v
26
+ when :ei then
27
+ query[:ei] = v
28
+ when :fields, :f then
29
+ query[:f] = (v.is_a?(String) ? v.split(',').map(&:to_sym) : v).map do |f|
30
+ case f
31
+ when :date, :d then :d
32
+ when :open, :o then :o
33
+ when :close, :c then :c
34
+ when :volume, :v then :v
35
+ when :low, :l then :l
36
+ when :high, :h then :h
37
+ when :k then :k
38
+ else
39
+ raise ArgumentError, "Invalid fields: #{v}."
40
+ end
41
+ end.join(',')
42
+ else
43
+ raise ArgumentError, "Invalid parameter: #{k}."
44
+ end
45
+ end
46
+ data = GoogleFinance::Api::GetPrices.fetch({ q: symbol }.merge(query))
47
+ headers = {}
48
+ rows = []
49
+ start_ts = Time.at(0)
50
+ timezone_offset = 0
51
+ data.each_line do |line|
52
+ line = CGI.unescape(line)
53
+ if line =~ /(?<k>.+)\=(?<v>.*)/
54
+ k = Regexp.last_match[:k].downcase.to_sym
55
+ case k
56
+ when :columns then
57
+ headers[k] = Regexp.last_match[:v].split(',').map(&:downcase)
58
+ when :market_open_minute, :market_close_minute, :interval then
59
+ headers[k] = Regexp.last_match[:v].to_i
60
+ when :timezone_offset then
61
+ timezone_offset = Regexp.last_match[:v].to_i
62
+ else
63
+ headers[k] = Regexp.last_match[:v]
64
+ end
65
+ else
66
+ values = line.split(',')
67
+ raise "Unexpected number of columns, #{values.count} vs. #{headers[:columns].size}." if (headers[:columns] || []).size != values.count
68
+ row = {}
69
+ if headers[:columns]
70
+ headers[:columns].each_with_index do |name, ndx|
71
+ row[name] = case name
72
+ when 'date' then
73
+ if values[ndx] =~ /^a(?<ts>.*)/
74
+ # https://stackoverflow.com/questions/45897894/convert-timestamps-in-google-finance-stock-data-to-proper-datetime
75
+ #
76
+ # The full timestamps are denoted by the leading 'a'. Like this: a1092945600. The number after the 'a' is a Unix timestamp.
77
+ # The numbers without a leading 'a' are "intervals".
78
+ #
79
+ start_ts = Time.at(Regexp.last_match[:ts].to_i)
80
+ else
81
+ start_ts + (headers[:interval] || 1) * values[ndx].to_i
82
+ end
83
+ when 'volume' then
84
+ values[ndx].to_i
85
+ else
86
+ values[ndx].to_f
87
+ end
88
+ end
89
+ end
90
+ rows << GoogleFinance::Price.new({ 'timezone_offset' => timezone_offset }.merge(row))
91
+ end
92
+ end
93
+ raise GoogleFinance::Errors::SymbolNotFoundError.new(symbol, data) if rows.count == 0 && headers[:exchange] == 'UNKNOWN EXCHANGE'
94
+ new headers, rows
95
+ end
96
+ end
97
+ end
@@ -55,7 +55,7 @@ module GoogleFinance
55
55
  end
56
56
 
57
57
  def self.get(symbol)
58
- data = Resources.fetch(q: symbol)
58
+ data = GoogleFinance::Api::Index.get(q: symbol)
59
59
  if data.is_a?(Hash) && data.key?('searchresults')
60
60
  if data['searchresults'].size >= 1
61
61
  get(data['searchresults'].first['symbol'])
@@ -1,7 +1,7 @@
1
1
  module GoogleFinance
2
2
  class Quotes
3
3
  def self.search(*symbols)
4
- results = Resources.fetch(q: symbols.join(','))
4
+ results = GoogleFinance::Api::Index.get(q: symbols.join(','))
5
5
  if results.is_a?(Hash) && results.key?('searchresults')
6
6
  searchresults = results['searchresults']
7
7
  raise GoogleFinance::Errors::SymbolsNotFoundError.new(symbols, results) if searchresults.empty?
@@ -1,3 +1,3 @@
1
1
  module GoogleFinance
2
- VERSION = '0.1.0'.freeze
2
+ VERSION = '0.2.0'.freeze
3
3
  end
@@ -0,0 +1,82 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://finance.google.com/finance/getprices?f=d,c&q=GOOG
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ User-Agent:
11
+ - Faraday v0.13.1
12
+ Accept-Encoding:
13
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
14
+ Accept:
15
+ - "*/*"
16
+ response:
17
+ status:
18
+ code: 200
19
+ message: OK
20
+ headers:
21
+ Content-Type:
22
+ - text/plain; charset=ISO-8859-1
23
+ Cache-Control:
24
+ - private,max-age=60
25
+ Vary:
26
+ - Accept-Encoding
27
+ X-Content-Type-Options:
28
+ - nosniff
29
+ P3p:
30
+ - CP="This is not a P3P policy! See g.co/p3phelp for more info."
31
+ Date:
32
+ - Thu, 28 Dec 2017 19:36:34 GMT
33
+ X-Frame-Options:
34
+ - SAMEORIGIN
35
+ X-Xss-Protection:
36
+ - 1; mode=block
37
+ Server:
38
+ - GSE
39
+ Set-Cookie:
40
+ - NID=120=X3WsZT74lIH_zdo1Lkc0541rYVv4eRGFlj6oboyOy7VxYskGi4EKJr8ib7Ff904epoVUiQeYF9y6EP33iY5jZUp1krI4kWkIWW-h0KafjC_5xd1WMArpTK9zHyRZSf2C;Domain=.google.com;Path=/;Expires=Fri,
41
+ 29-Jun-2018 19:36:34 GMT;HttpOnly
42
+ Alt-Svc:
43
+ - hq=":443"; ma=2592000; quic=51303431; quic=51303339; quic=51303338; quic=51303337;
44
+ quic=51303335,quic=":443"; ma=2592000; v="41,39,38,37,35"
45
+ Expires:
46
+ - Thu, 28 Dec 2017 19:36:34 GMT
47
+ Transfer-Encoding:
48
+ - chunked
49
+ body:
50
+ encoding: ASCII-8BIT
51
+ string: |
52
+ EXCHANGE%3DNASDAQ
53
+ MARKET_OPEN_MINUTE=570
54
+ MARKET_CLOSE_MINUTE=960
55
+ INTERVAL=86400
56
+ COLUMNS=DATE,CLOSE
57
+ DATA=
58
+ TIMEZONE_OFFSET=-300
59
+ a1511902800,1047.41
60
+ 1,1021.66
61
+ 2,1021.41
62
+ 3,1010.17
63
+ 6,998.68
64
+ 7,1005.15
65
+ 8,1018.38
66
+ 9,1030.93
67
+ 10,1037.05
68
+ 13,1041.1
69
+ 14,1040.48
70
+ 15,1040.61
71
+ 16,1049.15
72
+ 17,1064.19
73
+ 20,1077.14
74
+ 21,1070.68
75
+ 22,1064.95
76
+ 23,1063.63
77
+ 24,1060.12
78
+ 28,1056.74
79
+ 29,1049.37
80
+ http_version:
81
+ recorded_at: Thu, 28 Dec 2017 19:36:34 GMT
82
+ recorded_with: VCR 3.0.3