securities 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.DS_Store +0 -0
- data/.travis.yml +0 -2
- data/Gemfile +4 -0
- data/Guardfile +10 -0
- data/README.md +46 -59
- data/lib/securities.rb +1 -0
- data/lib/securities/lookup.rb +29 -0
- data/lib/securities/scraper.rb +45 -35
- data/lib/securities/stock.rb +96 -136
- data/lib/securities/version.rb +1 -1
- data/securities.gemspec +1 -0
- data/spec/.DS_Store +0 -0
- data/spec/lookup_spec.rb +22 -0
- data/spec/stock_spec.rb +69 -6
- metadata +27 -4
data/.DS_Store
ADDED
Binary file
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/Guardfile
ADDED
data/README.md
CHANGED
@@ -23,83 +23,70 @@ Or install it yourself as:
|
|
23
23
|
|
24
24
|
You can get stock information with commands:
|
25
25
|
|
26
|
-
|
27
|
-
my_stocks = Securities::Stock.new('aapl', 'yhoo')
|
28
|
-
|
29
|
-
or
|
30
|
-
|
31
|
-
# Symbol array
|
32
|
-
my_stocks = Securities::Stock.new(["aapl", "yhoo"])
|
33
|
-
|
34
|
-
my_data = my_stocks.history(:start_date => '2012-01-01', :end_date => '2012-02-01', :periods => :weekly)
|
26
|
+
my_stocks = Securities::Stock.new(:symbol => 'AAPL', :start_date => '2012-01-01', :end_date => '2012-02-01', :type => :weekly)
|
35
27
|
|
36
|
-
Optional parameter :
|
28
|
+
Optional parameter :type accepts :daily, :weekly, :monthly, :dividend. If not specified, it defaults to :daily.
|
37
29
|
|
38
30
|
:end_date defaults to Date.today if not specified.
|
39
31
|
|
40
32
|
You can access hash for a single stock with:
|
41
33
|
|
42
|
-
|
43
|
-
|
44
|
-
|
34
|
+
my_stocks.output
|
35
|
+
my_stocks.symbol
|
36
|
+
my_stocks.start_date
|
37
|
+
my_stocks.end_date
|
38
|
+
my_stocks.type
|
45
39
|
|
46
40
|
Output is returned in a hash:
|
47
41
|
|
48
|
-
|
49
|
-
[{:date=>"2012-01-04",
|
50
|
-
:open=>"410.00",
|
51
|
-
:high=>"414.68",
|
52
|
-
:low=>"409.28",
|
53
|
-
:close=>"413.44",
|
54
|
-
:volume=>"9286500",
|
55
|
-
:adj_close=>"411.67"},
|
56
|
-
{:date=>"2012-01-03",
|
42
|
+
[{:date=>"2012-01-03",
|
57
43
|
:open=>"409.40",
|
58
44
|
:high=>"412.50",
|
59
45
|
:low=>"409.00",
|
60
46
|
:close=>"411.23",
|
61
47
|
:volume=>"10793600",
|
62
|
-
:adj_close=>"409.47"}
|
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
|
-
|
48
|
+
:adj_close=>"409.47"},
|
49
|
+
{:date=>"2012-01-04",
|
50
|
+
:open=>"410.00",
|
51
|
+
:high=>"414.68",
|
52
|
+
:low=>"409.28",
|
53
|
+
:close=>"413.44",
|
54
|
+
:volume=>"9286500",
|
55
|
+
:adj_close=>"411.67"}]
|
56
|
+
|
57
|
+
Stock symbol lookup:
|
58
|
+
|
59
|
+
my_lookup = Securities::Lookup.new('Google')
|
60
|
+
|
61
|
+
Returns a hash with matched symbol, name, last trade, type, industry/category, exchange.
|
62
|
+
|
63
|
+
my_lookup.output
|
64
|
+
|
65
|
+
[{:symbol=>"GOOG",
|
66
|
+
:name=>"Google Inc.",
|
67
|
+
:last_trade=>"726.31",
|
68
|
+
:type=>"Stock",
|
69
|
+
:industry_category=>"Internet Information Providers",
|
70
|
+
:exchange=>"NMS"},
|
71
|
+
{:symbol=>"GOOG.MX",
|
72
|
+
:name=>"Google Inc.",
|
73
|
+
:last_trade=>"9,306.00",
|
74
|
+
:type=>"Stock",
|
75
|
+
:industry_category=>"Internet Information Providers",
|
76
|
+
:exchange=>"MEX"},
|
77
|
+
...]
|
78
|
+
|
79
|
+
my_lookup.input returns 'Google'
|
80
|
+
|
81
|
+
# Version 2.0.0
|
82
|
+
|
83
|
+
* Adds support for stock symbol lookup.
|
84
|
+
|
85
|
+
* Removes support for multiple stock input cause it complects the gem code and its implementation without adding much to it.
|
97
86
|
|
98
87
|
## To do:
|
99
88
|
|
100
|
-
* Write specs.
|
101
89
|
* Add quote info (P/E, P/S, etc.)
|
102
|
-
* Add symbol from name lookup.
|
103
90
|
* Add options support.
|
104
91
|
|
105
92
|
## Contributing
|
data/lib/securities.rb
CHANGED
@@ -0,0 +1,29 @@
|
|
1
|
+
module Securities
|
2
|
+
#
|
3
|
+
# Symbol lookup with Securities::Lookup.new('apple')
|
4
|
+
#
|
5
|
+
class Lookup
|
6
|
+
|
7
|
+
attr_accessor :input, :output
|
8
|
+
# Error handling
|
9
|
+
class LookupException < StandardError ; end
|
10
|
+
|
11
|
+
def initialize parameters
|
12
|
+
@input = parameters.delete(' ')
|
13
|
+
if @input.empty?
|
14
|
+
raise LookupException, 'The lookup input was empty.'
|
15
|
+
end
|
16
|
+
url = generate_lookup_url
|
17
|
+
request = :lookup
|
18
|
+
@output = Securities::Scraper.get(request, url)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def generate_lookup_url
|
24
|
+
url = 'http://finance.yahoo.com/lookup?s=' + @input
|
25
|
+
return url
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
data/lib/securities/scraper.rb
CHANGED
@@ -2,6 +2,7 @@ require 'active_support/core_ext'
|
|
2
2
|
require 'uri'
|
3
3
|
require 'net/http'
|
4
4
|
require 'csv'
|
5
|
+
require 'nokogiri'
|
5
6
|
|
6
7
|
module Securities
|
7
8
|
#
|
@@ -10,64 +11,73 @@ module Securities
|
|
10
11
|
class Scraper
|
11
12
|
|
12
13
|
# Error handling
|
13
|
-
class ScraperException < StandardError
|
14
|
-
end
|
14
|
+
class ScraperException < StandardError ; end
|
15
15
|
|
16
|
-
def self.get type,
|
16
|
+
def self.get type, url
|
17
|
+
results = Array.new
|
18
|
+
|
19
|
+
# Encoding for bad characters in index names.
|
20
|
+
clean_url = URI::encode(url)
|
21
|
+
uri = URI.parse(clean_url)
|
17
22
|
|
18
|
-
#
|
19
|
-
|
20
|
-
|
21
|
-
|
23
|
+
# Check connection.
|
24
|
+
begin
|
25
|
+
get = Net::HTTP.get(uri)
|
26
|
+
rescue => error
|
27
|
+
raise ScraperException, "Connection error: #{error.message}"
|
22
28
|
end
|
23
29
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
parameters.each do |symbol, url|
|
30
|
+
case type
|
31
|
+
#
|
32
|
+
# Scraping for lookup.
|
33
|
+
when :lookup
|
34
|
+
doc = Nokogiri::HTML(get)
|
30
35
|
|
31
|
-
|
36
|
+
table = doc.at('div#yfi_sym_results tbody')
|
32
37
|
|
33
|
-
|
34
|
-
|
35
|
-
get = Net::HTTP.get(uri)
|
36
|
-
rescue => error
|
37
|
-
raise ScraperException, "Connection error: #{error.message}"
|
38
|
+
if table.nil?
|
39
|
+
raise ScraperException, 'There were no results for this lookup.'
|
38
40
|
end
|
39
41
|
|
40
|
-
#
|
42
|
+
# Symbol Name Last Trade Type Industry/Category Exchange
|
43
|
+
table.xpath('tr').each do |tr|
|
44
|
+
row = tr.xpath('td')
|
45
|
+
results << {:symbol => row[0].text,
|
46
|
+
:name => row[1].text,
|
47
|
+
:last_trade => row[2].text,
|
48
|
+
:type => row[3].text,
|
49
|
+
:industry_category => row[4].text,
|
50
|
+
:exchange => row[5].text}
|
51
|
+
end
|
52
|
+
#
|
53
|
+
# Scraping for history
|
54
|
+
when :history
|
55
|
+
# Skip first line because it contains headers with Date,Open,High,Low,Close,Volume,Adj Close.
|
41
56
|
# Check for errors during CSV parsing.
|
42
57
|
begin
|
43
58
|
csv = CSV.parse(get, :headers => true)
|
44
59
|
rescue => error
|
45
|
-
#
|
46
|
-
|
47
|
-
# bug => if exception occurs in one symbol, it will not give any results for any other.
|
48
|
-
# raise ScraperException, "Invalid symbol '#{symbol}' specified."
|
49
|
-
results[symbol] = []
|
50
|
-
next
|
60
|
+
# Probably an invalid symbol specified or there was some other way the parser couldn't read a CSV.
|
61
|
+
raise ScraperException, 'Stock symbol does not exist.'
|
51
62
|
end
|
52
63
|
|
53
|
-
data = Array.new
|
54
64
|
csv.each_with_index do |row, index|
|
55
65
|
line = Hash.new
|
56
66
|
csv.headers.each_with_index do |header, i|
|
57
|
-
# Set headers as keys for data hash.
|
67
|
+
# Set headers as keys for the data hash.
|
58
68
|
line[header.parameterize.underscore.to_sym] = row[i]
|
59
|
-
|
69
|
+
results[index] = line
|
60
70
|
end
|
61
71
|
end
|
62
72
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
# raise ScraperException, "There were no results for #{symbol}."
|
67
|
-
# end
|
73
|
+
if results.empty?
|
74
|
+
raise ScraperException, 'There were no results for this symbol.'
|
75
|
+
end
|
68
76
|
|
69
|
-
results
|
77
|
+
# Reversing results to return from the oldest to the newest.
|
78
|
+
results = results.reverse
|
70
79
|
end
|
80
|
+
|
71
81
|
return results
|
72
82
|
end
|
73
83
|
|
data/lib/securities/stock.rb
CHANGED
@@ -1,160 +1,120 @@
|
|
1
1
|
module Securities
|
2
2
|
|
3
|
-
# Usage: my_stocks = Securities::Stock.new('aapl', 'yhoo').history(:start_date => '2012-01-01', :end_date => '2012-02-01', :periods => :weekly)
|
4
|
-
# :periods accepts :daily, :weekly, :monthly, :dividend. If not specified, it defaults to :daily.
|
5
|
-
#
|
6
|
-
# You can access hash for a single stock with:
|
7
|
-
# my_stocks["yhoo"]
|
8
|
-
|
9
3
|
class Stock
|
10
4
|
|
11
|
-
attr_accessor :
|
5
|
+
attr_accessor :symbol, :output, :start_date, :end_date, :type
|
12
6
|
# REGEX for YYYY-MM-DD
|
13
7
|
DATE_REGEX = /^[0-9]{4}\-(0[1-9]|1[012])\-(0[1-9]|[12][0-9]|3[01])/
|
14
|
-
|
8
|
+
TYPE_CODES_ARRAY = {:daily => 'd', :weekly => 'w', :monthly => 'm', :dividends => 'v'}
|
15
9
|
|
16
10
|
# Error handling
|
17
|
-
class StockException < StandardError
|
18
|
-
end
|
19
|
-
|
20
|
-
def initialize *parameters
|
21
|
-
validate_symbols(parameters)
|
22
|
-
end
|
11
|
+
class StockException < StandardError ; end
|
23
12
|
|
24
|
-
def
|
25
|
-
|
26
|
-
parameters[:
|
27
|
-
validate_history(parameters)
|
28
|
-
urls = generate_history_url(parameters)
|
13
|
+
def initialize parameters
|
14
|
+
validate_input(parameters)
|
15
|
+
@symbol = parameters[:symbol]
|
29
16
|
@start_date = parameters[:start_date]
|
30
17
|
@end_date = parameters[:end_date]
|
31
|
-
@
|
32
|
-
|
18
|
+
@type = parameters[:type]
|
19
|
+
url = generate_history_url
|
20
|
+
request = :history
|
21
|
+
@output = Securities::Scraper.get(request, url)
|
33
22
|
end
|
34
23
|
|
35
24
|
private
|
36
25
|
|
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
|
-
start_date = parameters[:start_date].to_date
|
63
|
-
end_date = parameters[:end_date].to_date
|
64
|
-
|
65
|
-
periods = case parameters[:periods]
|
66
|
-
when :daily then 'd'
|
67
|
-
when :weekly then 'w'
|
68
|
-
when :monthly then 'm'
|
69
|
-
when :dividends then 'v'
|
70
|
-
end
|
71
|
-
|
72
|
-
results = Hash.new
|
73
|
-
@symbols.each do |symbol|
|
26
|
+
#
|
27
|
+
# History URL generator
|
28
|
+
#
|
29
|
+
# Generating URL for various types of requests within stocks.
|
30
|
+
#
|
31
|
+
# Using Yahoo Finance
|
32
|
+
# http://ichart.finance.yahoo.com/table.csv?s=%s&a=%s&b=%s&c=%s&d=%s&e=%s&f=%s&g=%s&ignore=.csv
|
33
|
+
#
|
34
|
+
# s = stock symbol
|
35
|
+
#
|
36
|
+
# Period start date
|
37
|
+
# a = start month
|
38
|
+
# b = start day
|
39
|
+
# c = start year
|
40
|
+
#
|
41
|
+
# Period end date
|
42
|
+
# d = end month
|
43
|
+
# e = end day
|
44
|
+
# f = end year
|
45
|
+
#
|
46
|
+
# g = type
|
47
|
+
# Type values allowed: 'd' for daily (the default), 'w' for weekly, 'm' for monthly and 'v' for dividends.
|
48
|
+
def generate_history_url
|
49
|
+
|
74
50
|
url = 'http://ichart.finance.yahoo.com/table.csv?s=%s&a=%s&b=%s&c=%s&d=%s&e=%s&f=%s&g=%s&ignore=.csv' % [
|
75
|
-
symbol,
|
76
|
-
start_date.month - 1,
|
77
|
-
start_date.day,
|
78
|
-
start_date.year,
|
79
|
-
end_date.month - 1,
|
80
|
-
end_date.day,
|
81
|
-
end_date.year,
|
82
|
-
|
51
|
+
@symbol,
|
52
|
+
@start_date.to_date.month - 1,
|
53
|
+
@start_date.to_date.day,
|
54
|
+
@start_date.to_date.year,
|
55
|
+
@end_date.to_date.month - 1,
|
56
|
+
@end_date.to_date.day,
|
57
|
+
@end_date.to_date.year,
|
58
|
+
TYPE_CODES_ARRAY[@type]
|
83
59
|
]
|
84
|
-
results[symbol] = url
|
85
|
-
end
|
86
|
-
|
87
|
-
# Returns a hash {'symbol' => 'url'}
|
88
|
-
return results
|
89
|
-
end
|
90
|
-
|
91
|
-
#
|
92
|
-
# Input parameters validation
|
93
|
-
#
|
94
|
-
def validate_symbols parameters
|
95
|
-
|
96
|
-
# Reject empty symbol hashes.
|
97
|
-
@symbols = parameters.reject(&:empty?)
|
98
|
-
|
99
|
-
if @symbols.nil?
|
100
|
-
raise StockException, 'You must specify at least one stock symbol.'
|
101
|
-
end
|
102
|
-
|
103
|
-
# FIXME: A kinda hacky way to check if parameters are a nested array (when accepting an array as a symbols argument).
|
104
|
-
# Unnesting an array.
|
105
|
-
@symbols[0].is_a?(Array) ? @symbols = @symbols[0] : nil
|
106
|
-
|
107
|
-
unless @symbols.uniq.length == @symbols.length
|
108
|
-
raise StockException, 'Duplicate stock symbols given.'
|
109
|
-
end
|
110
|
-
end
|
111
60
|
|
112
|
-
|
113
|
-
unless parameters.is_a?(Hash)
|
114
|
-
raise StockException, 'Given parameters have to be a hash.'
|
61
|
+
return url
|
115
62
|
end
|
116
63
|
|
117
|
-
|
118
|
-
|
64
|
+
#
|
65
|
+
# Input parameters validation
|
66
|
+
#
|
67
|
+
|
68
|
+
def validate_input parameters
|
69
|
+
unless parameters.is_a?(Hash)
|
70
|
+
raise StockException, 'Given parameters have to be a hash.'
|
71
|
+
end
|
72
|
+
|
73
|
+
# Check if stock symbol is specified.
|
74
|
+
unless parameters.has_key?(:symbol)
|
75
|
+
raise StockException, 'No stock symbol specified.'
|
76
|
+
end
|
77
|
+
# Check if stock symbol is a string.
|
78
|
+
unless parameters[:symbol].is_a?(String)
|
79
|
+
raise StockException, 'Stock symbol must be a string.'
|
80
|
+
end
|
81
|
+
# Check if stock symbol is valid.
|
82
|
+
unless parameters[:symbol].match('^[a-zA-Z0-9]+$')
|
83
|
+
raise StockException, 'Invalid stock symbol specified.'
|
84
|
+
end
|
85
|
+
|
86
|
+
# Use today date if :end_date is not specified.
|
87
|
+
unless parameters.has_key?(:end_date)
|
88
|
+
parameters[:end_date] = Date.today.strftime("%Y-%m-%d")
|
89
|
+
end
|
90
|
+
|
91
|
+
unless parameters.has_key?(:start_date)
|
92
|
+
raise StockException, 'Start date must be specified.'
|
93
|
+
end
|
94
|
+
|
95
|
+
unless DATE_REGEX.match(parameters[:start_date])
|
96
|
+
raise StockException, 'Invalid start date specified. Format YYYY-MM-DD.'
|
97
|
+
end
|
98
|
+
|
99
|
+
unless DATE_REGEX.match(parameters[:end_date])
|
100
|
+
raise StockException, 'Invalid end date specified. Format YYYY-MM-DD.'
|
101
|
+
end
|
102
|
+
|
103
|
+
unless parameters[:start_date].to_date < parameters[:end_date].to_date
|
104
|
+
raise StockException, 'End date must be greater than the start date.'
|
105
|
+
end
|
106
|
+
|
107
|
+
unless parameters[:start_date].to_date < Date.today
|
108
|
+
raise StockException, 'Start date must not be in the future.'
|
109
|
+
end
|
110
|
+
|
111
|
+
# Set to default :type if key isn't specified.
|
112
|
+
parameters[:type] = :daily if !parameters.has_key?(:type)
|
113
|
+
|
114
|
+
unless TYPE_CODES_ARRAY.has_key?(parameters[:type])
|
115
|
+
raise StockException, 'Invalid type specified.'
|
116
|
+
end
|
119
117
|
end
|
120
118
|
|
121
|
-
unless parameters.has_key?(:start_date)
|
122
|
-
raise StockException, 'Start date must be specified.'
|
123
|
-
end
|
124
|
-
|
125
|
-
unless DATE_REGEX.match(parameters[:start_date])
|
126
|
-
raise StockException, 'Invalid start date specified. Format YYYY-MM-DD.'
|
127
|
-
end
|
128
|
-
|
129
|
-
unless DATE_REGEX.match(parameters[:end_date])
|
130
|
-
raise StockException, 'Invalid end date specified. Format YYYY-MM-DD.'
|
131
|
-
end
|
132
|
-
|
133
|
-
unless parameters[:start_date].to_date < parameters[:end_date].to_date
|
134
|
-
raise StockException, 'End date must be greater than the start date.'
|
135
|
-
end
|
136
|
-
|
137
|
-
unless parameters[:start_date].to_date < Date.today
|
138
|
-
raise StockException, 'Start date must not be in the future.'
|
139
|
-
end
|
140
|
-
|
141
|
-
# Set to default :periods if key isn't specified.
|
142
|
-
parameters[:periods] = :daily if !parameters.has_key?(:periods)
|
143
|
-
|
144
|
-
unless PERIODS_ARRAY.include?(parameters[:periods])
|
145
|
-
raise StockException, 'Invalid periods value specified.'
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
#
|
153
|
-
# The missing hash function
|
154
|
-
# Returns true if all keys are present in hash.
|
155
|
-
#
|
156
|
-
class Hash
|
157
|
-
def has_keys?(*_keys)
|
158
|
-
(_keys - self.keys).empty?
|
159
119
|
end
|
160
120
|
end
|
data/lib/securities/version.rb
CHANGED
data/securities.gemspec
CHANGED
data/spec/.DS_Store
ADDED
Binary file
|
data/spec/lookup_spec.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Securities::Lookup do
|
4
|
+
describe "Lookup" do
|
5
|
+
|
6
|
+
context "should raise an exception if parameter" do
|
7
|
+
it "is empty" do
|
8
|
+
expect { Securities::Lookup.new(' ') }.to raise_error('The lookup input was empty.')
|
9
|
+
end
|
10
|
+
it "got no results" do
|
11
|
+
expect { Securities::Lookup.new('some invalid lookup string') }.to raise_error('There were no results for this lookup.')
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context "should not raise an exception if parameter" do
|
16
|
+
it "is not empty" do
|
17
|
+
expect { Securities::Lookup.new('Apple') }.not_to raise_error
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
data/spec/stock_spec.rb
CHANGED
@@ -1,11 +1,74 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Securities::Stock do
|
4
|
-
describe "
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
4
|
+
describe "Stock" do
|
5
|
+
context "should raise an exception if parameter" do
|
6
|
+
|
7
|
+
it "is a not a hash" do
|
8
|
+
expect { Securities::Stock.new('some values') }.to raise_error('Given parameters have to be a hash.')
|
9
|
+
end
|
10
|
+
|
11
|
+
context "Symbol" do
|
12
|
+
it "is not passed" do
|
13
|
+
expect { Securities::Stock.new(:start_date => '2012-01-01', :end_date => '2012-01-04') }.to raise_error('No stock symbol specified.')
|
14
|
+
end
|
15
|
+
it "is not a string" do
|
16
|
+
expect { Securities::Stock.new(:symbol => ["AAPL", "GOOG"], :start_date => '2012-01-01', :end_date => '2012-01-04') }.to raise_error('Stock symbol must be a string.')
|
17
|
+
end
|
18
|
+
it "is an invalid string" do
|
19
|
+
expect { Securities::Stock.new(:symbol => 'AAPL company', :start_date => '2012-01-01', :end_date => '2012-01-04') }.to raise_error('Invalid stock symbol specified.')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "History" do
|
24
|
+
|
25
|
+
it ":start_date not specified" do
|
26
|
+
expect { Securities::Stock.new(:symbol => 'AAPL', :end_date => '2012-01-01', :type => :weekly) }.to raise_error('Start date must be specified.')
|
27
|
+
end
|
28
|
+
it ":start_date or :end_date is invalid" do
|
29
|
+
expect { Securities::Stock.new(:symbol => 'AAPL', :start_date => '2012-01-50', :end_date => '2012-02-10', :type => :weekly) }.to raise_error('Invalid start date specified. Format YYYY-MM-DD.')
|
30
|
+
expect { Securities::Stock.new(:symbol => 'AAPL', :start_date => '2012-01-10', :end_date => '2012-02-50', :type => :weekly) }.to raise_error('Invalid end date specified. Format YYYY-MM-DD.')
|
31
|
+
end
|
32
|
+
it ":start_date greater than :end_date" do
|
33
|
+
expect { Securities::Stock.new(:symbol => 'AAPL', :start_date => '2012-01-10', :end_date => '2012-01-01', :type => :weekly) }.to raise_error('End date must be greater than the start date.')
|
34
|
+
end
|
35
|
+
it ":start_date is in the future" do
|
36
|
+
expect { Securities::Stock.new(:symbol => 'AAPL', :start_date => (Date.today+1).strftime("%Y-%m-%d"), :type => :weekly) }.to raise_error('End date must be greater than the start date.')
|
37
|
+
end
|
38
|
+
it ":type value is invalid" do
|
39
|
+
expect { Securities::Stock.new(:symbol => 'AAPL', :start_date => '2012-01-01', :type => :some_invalid_value) }.to raise_error('Invalid type specified.')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
context "should not raise an exception if parameter" do
|
46
|
+
|
47
|
+
it "is a string" do
|
48
|
+
expect { Securities::Stock.new(:symbol => 'AAPL', :start_date => '2012-01-01', :end_date => '2012-01-04') }.not_to raise_error
|
49
|
+
end
|
50
|
+
it ":end_date is not specified (should default to today)" do
|
51
|
+
expect { @my_stocks = Securities::Stock.new(:symbol => 'AAPL', :start_date => (Date.today-8).strftime("%Y-%m-%d"), :type => :weekly) }.not_to raise_error
|
52
|
+
@my_stocks.end_date.to_date.should eq(Date.today)
|
53
|
+
end
|
54
|
+
it ":type is not specified (defaults to :daily)" do
|
55
|
+
expect { @my_stocks = Securities::Stock.new(:symbol => 'AAPL', :start_date => (Date.today-8).strftime("%Y-%m-%d")) }.not_to raise_error
|
56
|
+
@my_stocks.type.should eq(:daily)
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "Scraper" do
|
63
|
+
context "should raise an exception if" do
|
64
|
+
|
65
|
+
it "symbol is invalid" do
|
66
|
+
expect { Securities::Stock.new(:symbol => 'invalidsymbol', :start_date => (Date.today-8).strftime("%Y-%m-%d")) }.to raise_error('Stock symbol does not exist.')
|
67
|
+
end
|
68
|
+
it "there are no results" do
|
69
|
+
expect { Securities::Stock.new(:symbol => 'AAPL', :start_date => '2012-08-01', :end_date => '2012-08-05', :type => :dividends) }.to raise_error('There were no results for this symbol.')
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
10
73
|
end
|
11
74
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: securities
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-09-
|
12
|
+
date: 2012-09-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -27,6 +27,22 @@ dependencies:
|
|
27
27
|
- - ! '>='
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: nokogiri
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
30
46
|
- !ruby/object:Gem::Dependency
|
31
47
|
name: rspec
|
32
48
|
requirement: !ruby/object:Gem::Requirement
|
@@ -50,18 +66,23 @@ executables: []
|
|
50
66
|
extensions: []
|
51
67
|
extra_rdoc_files: []
|
52
68
|
files:
|
69
|
+
- .DS_Store
|
53
70
|
- .gitignore
|
54
71
|
- .rspec
|
55
72
|
- .travis.yml
|
56
73
|
- Gemfile
|
74
|
+
- Guardfile
|
57
75
|
- LICENSE
|
58
76
|
- README.md
|
59
77
|
- Rakefile
|
60
78
|
- lib/securities.rb
|
79
|
+
- lib/securities/lookup.rb
|
61
80
|
- lib/securities/scraper.rb
|
62
81
|
- lib/securities/stock.rb
|
63
82
|
- lib/securities/version.rb
|
64
83
|
- securities.gemspec
|
84
|
+
- spec/.DS_Store
|
85
|
+
- spec/lookup_spec.rb
|
65
86
|
- spec/spec_helper.rb
|
66
87
|
- spec/stock_spec.rb
|
67
88
|
homepage: https://github.com/Nedomas/securities
|
@@ -78,7 +99,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
78
99
|
version: '0'
|
79
100
|
segments:
|
80
101
|
- 0
|
81
|
-
hash: -
|
102
|
+
hash: -994639684959431574
|
82
103
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
104
|
none: false
|
84
105
|
requirements:
|
@@ -87,7 +108,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
87
108
|
version: '0'
|
88
109
|
segments:
|
89
110
|
- 0
|
90
|
-
hash: -
|
111
|
+
hash: -994639684959431574
|
91
112
|
requirements: []
|
92
113
|
rubyforge_project:
|
93
114
|
rubygems_version: 1.8.24
|
@@ -95,5 +116,7 @@ signing_key:
|
|
95
116
|
specification_version: 3
|
96
117
|
summary: Financial information scraper gem. Uses Yahoo Finance API.
|
97
118
|
test_files:
|
119
|
+
- spec/.DS_Store
|
120
|
+
- spec/lookup_spec.rb
|
98
121
|
- spec/spec_helper.rb
|
99
122
|
- spec/stock_spec.rb
|