google-finance-ruby-client 0.1.0 → 0.2.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.
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