yquotes 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/yquotes/version.rb +1 -1
- data/lib/yquotes/yahoo.rb +89 -96
- data/lib/yquotes.rb +41 -47
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f0ff917e8a8aac81d7a0cda637da8e8501c71c76
|
4
|
+
data.tar.gz: 1f20150900e8382b0c9e9800615d81b5db7729b5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 044d0ecf6eff1d86b792c12cfb718a823a57eb138f71b69f6dd5a36875b6b262b0a33a7f963f75ab9e1fc2388fdc9b2ba8e4a8f2fa346f2fc02f1654408abc50
|
7
|
+
data.tar.gz: 4173e954a531ce60e70004dcfcdaa9d1671b117d5f26408f224452bb47c68e17623c60648ad9da021132d55e152c03ecef8eb84213f578bcfd0d404893acb192
|
data/lib/yquotes/version.rb
CHANGED
data/lib/yquotes/yahoo.rb
CHANGED
@@ -3,100 +3,93 @@ require 'csv'
|
|
3
3
|
require 'date'
|
4
4
|
require 'nokogiri'
|
5
5
|
|
6
|
-
|
7
6
|
module YQuotes
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
end
|
101
|
-
|
102
|
-
end
|
7
|
+
COOKIE_URL = 'https://finance.yahoo.com/quote/AAPL/history?p=AAPL'.freeze
|
8
|
+
CRUMB_PATTERN = /\"CrumbStore\":{\"crumb\":\"(?<crumb>[^"]+)/
|
9
|
+
QUOTE_ENDPOINT = 'https://query1.finance.yahoo.com/v7/finance/download/%{symbol}?'.freeze
|
10
|
+
|
11
|
+
class Yahoo
|
12
|
+
# Get cookie and crumb
|
13
|
+
def initialize
|
14
|
+
fetch_credentials
|
15
|
+
end
|
16
|
+
|
17
|
+
# fetch_csv: fetch historical quotes in csv format
|
18
|
+
def fetch_csv(ticker, start_date = nil, end_date = nil, period = 'd')
|
19
|
+
connection = nil
|
20
|
+
|
21
|
+
# retry 3-times in case it sends unauthorized
|
22
|
+
3.times do |_i|
|
23
|
+
begin
|
24
|
+
url = build_url(ticker, start_date, end_date, period)
|
25
|
+
connection = open(url, 'Cookie' => @cookie)
|
26
|
+
break
|
27
|
+
rescue OpenURI::HTTPError => e
|
28
|
+
fetch_credentials
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
data = CSV.parse(connection.read, converters: :numeric)
|
33
|
+
|
34
|
+
raise 'Yahoo.fetch_csv unable to fetch data' unless data.is_a? Array
|
35
|
+
data
|
36
|
+
end
|
37
|
+
|
38
|
+
alias get_csv fetch_csv
|
39
|
+
alias get_data fetch_csv
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def fetch_credentials
|
44
|
+
# get cookie
|
45
|
+
page = open(COOKIE_URL)
|
46
|
+
@cookie = page.meta['set-cookie'].split('; ', 2).first
|
47
|
+
|
48
|
+
# get crumb
|
49
|
+
scripts = Nokogiri::HTML(page).css('script')
|
50
|
+
scripts.each do |s|
|
51
|
+
next unless s.text.include? 'CrumbStore'
|
52
|
+
pattern = s.text.match(CRUMB_PATTERN)
|
53
|
+
@crumb = pattern['crumb']
|
54
|
+
break
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# build_params: build parameters for get query
|
59
|
+
def build_url(ticker, start_date = nil, end_date = nil, period = 'd')
|
60
|
+
url = QUOTE_ENDPOINT
|
61
|
+
url = format(url, symbol: URI.escape(ticker.upcase))
|
62
|
+
|
63
|
+
params = {
|
64
|
+
crumb: URI.escape(@crumb),
|
65
|
+
events: 'history',
|
66
|
+
interval: '1d'
|
67
|
+
}
|
68
|
+
|
69
|
+
# sanitize date
|
70
|
+
params[:period1] = get_date(start_date).to_i unless start_date.nil?
|
71
|
+
params[:period2] = get_date(end_date).to_i unless end_date.nil?
|
72
|
+
|
73
|
+
params[:interval] = '1d' if period == 'd'
|
74
|
+
params[:interval] = '1mo' if period == 'm'
|
75
|
+
params[:interval] = '1wk' if period == 'w'
|
76
|
+
|
77
|
+
url + params.map { |k, v| "#{k}=#{v}" }.join('&').to_s
|
78
|
+
end
|
79
|
+
|
80
|
+
# get_date: get date from String
|
81
|
+
def get_date(d)
|
82
|
+
return nil if d.nil?
|
83
|
+
return d.to_time if d.is_a? DateTime
|
84
|
+
|
85
|
+
if d.is_a? String
|
86
|
+
|
87
|
+
begin
|
88
|
+
dt = DateTime.parse(d).to_time
|
89
|
+
rescue Exception => e
|
90
|
+
raise "invalid param #{d} - date should be in yyyy-mm-dd format"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
data/lib/yquotes.rb
CHANGED
@@ -1,65 +1,59 @@
|
|
1
1
|
require 'csv'
|
2
2
|
require 'daru'
|
3
3
|
require 'tmpdir'
|
4
|
-
require
|
5
|
-
require
|
4
|
+
require 'yquotes/version'
|
5
|
+
require 'yquotes/yahoo'
|
6
6
|
|
7
7
|
module YQuotes
|
8
|
+
class Client
|
9
|
+
def initialize
|
10
|
+
@yahoo_client = Yahoo.new
|
11
|
+
end
|
8
12
|
|
9
|
-
|
13
|
+
# get_quote: returns Daru::DataFrame of the quote with volume and close
|
14
|
+
def get_quote(ticker, args = {})
|
15
|
+
if args.is_a? Hash
|
16
|
+
start_date = args[:start_date] if args[:start_date]
|
17
|
+
start_date ||= args[:s] if args[:s]
|
10
18
|
|
11
|
-
|
12
|
-
|
13
|
-
end
|
19
|
+
end_date = args[:end_date] if args[:end_date]
|
20
|
+
end_date ||= args[:e] if args[:e]
|
14
21
|
|
15
|
-
|
16
|
-
|
22
|
+
period = args[:period] if args[:period]
|
23
|
+
period ||= args[:p] if args[:p]
|
24
|
+
end
|
17
25
|
|
18
|
-
|
19
|
-
|
20
|
-
|
26
|
+
csv = @yahoo_client.get_csv(ticker, start_date, end_date, period)
|
27
|
+
create_from_csv(csv)
|
28
|
+
end
|
21
29
|
|
22
|
-
|
23
|
-
|
30
|
+
alias historical_data get_quote
|
31
|
+
alias historical_quote get_quote
|
24
32
|
|
25
|
-
|
26
|
-
period ||= args[:p] if args[:p]
|
27
|
-
end
|
33
|
+
private
|
28
34
|
|
29
|
-
|
30
|
-
|
31
|
-
end
|
35
|
+
def create_from_csv(data)
|
36
|
+
file_path = Dir.tmpdir + "/#{Time.now.to_i}"
|
32
37
|
|
33
|
-
|
34
|
-
|
38
|
+
CSV.open(file_path, 'w') do |out|
|
39
|
+
data.each do |row|
|
40
|
+
out << row
|
41
|
+
end
|
42
|
+
end
|
35
43
|
|
36
|
-
|
44
|
+
df = nil
|
37
45
|
|
38
|
-
|
46
|
+
df = Daru::DataFrame.from_csv(file_path, converters: :numeric)
|
47
|
+
File.delete(file_path) if File.exist?(file_path)
|
39
48
|
|
40
|
-
|
41
|
-
|
42
|
-
CSV.open(file_path, 'w') do |out|
|
43
|
-
data.each do |row|
|
44
|
-
out << row
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
df = nil
|
49
|
-
|
50
|
-
df = Daru::DataFrame.from_csv(file_path, :converters => :numeric)
|
51
|
-
File.delete(file_path) if File.exists?(file_path)
|
52
|
-
|
53
|
-
#sort from earlier to latest
|
54
|
-
df = df.sort ['Date']
|
55
|
-
|
56
|
-
# strip columns and create index
|
57
|
-
df.index = Daru::Index.new(df['Date'].to_a)
|
58
|
-
df.rename_vectors 'Volume' => :volume, 'Adj Close' => :adj_close, 'Open' => :open, 'Close' => :close, 'High' => :high, 'Low' => :low
|
59
|
-
df.delete_vector 'Date'
|
60
|
-
d = df.filter(:row) { |row| row[:volume] > 0}
|
61
|
-
end
|
62
|
-
|
63
|
-
end
|
49
|
+
# sort from earlier to latest
|
50
|
+
df = df.sort ['Date']
|
64
51
|
|
52
|
+
# strip columns and create index
|
53
|
+
df.index = Daru::Index.new(df['Date'].to_a)
|
54
|
+
df.rename_vectors 'Volume' => :volume, 'Adj Close' => :adj_close, 'Open' => :open, 'Close' => :close, 'High' => :high, 'Low' => :low
|
55
|
+
df.delete_vector 'Date'
|
56
|
+
d = df.filter(:row) { |row| row[:volume] > 0 }
|
57
|
+
end
|
58
|
+
end
|
65
59
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: yquotes
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- P Choudhary
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-05-
|
11
|
+
date: 2017-05-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|