nas-yahoo_stock 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -36,4 +36,26 @@
36
36
  === 1.0.1 2009-09-10
37
37
 
38
38
  * 1 addition:
39
- * adds feature to retreive historical stock data
39
+ * adds feature to retreive historical stock data
40
+
41
+ === 1.0.2 2009-10-04
42
+ Major changes in the public API. Check the README.rdoc file and individual classes for more information
43
+
44
+ * 8 addition:
45
+ * adds YahooStock::Interface::History
46
+ * adds YahooStock::Interface::Quote
47
+ * adds YahooStock::Interface::ScripSymbol
48
+ * adds YahooStock::Result as a superclass for all formatter classes
49
+ * adds array format, Result::ArrayFormat, to generate results as an array
50
+ * adds hash format, Result::ArrayFormat, to generate results as key value pairs
51
+ * adds base class as parent for public facing classes
52
+ * adds observers to check when it is required to send request to yahoo again to get fresh data
53
+
54
+ * 7 changes:
55
+ * changes YahooStock::Interface to be the main interface class. Other interface classes will inherit from this
56
+ * changes YahooStock::ScripSymbol to use YahooStock::Interface::ScripSymbol
57
+ * removes all history interface logic from YahooStock::History and uses YahooStock::Interface::History
58
+ * changes YahooStock::Quote, YahooStock::History, YahooStock::ScripSymbol to extend from YahooStock::Base
59
+ * sorts url parameters before sending request to yahoo
60
+ * YahooInterface::Quote#realtime, extended, and standard methods should return self to allow calling different format output method calls
61
+ * do not discard any empty values when finding scrip symbol options for a company as this interferes with different format options
data/Manifest.txt CHANGED
@@ -2,12 +2,28 @@ History.txt
2
2
  Manifest.txt
3
3
  README.rdoc
4
4
  Rakefile
5
- lib/yahoo_stock.rb
6
- lib/yahoo_stock/quote.rb
5
+ lib/yahoo_stock/base.rb
6
+ lib/yahoo_stock/history.rb
7
7
  lib/yahoo_stock/interface.rb
8
+ lib/yahoo_stock/interface/history.rb
9
+ lib/yahoo_stock/interface/quote.rb
10
+ lib/yahoo_stock/interface/scrip_symbol.rb
11
+ lib/yahoo_stock/quote.rb
12
+ lib/yahoo_stock/result.rb
13
+ lib/yahoo_stock/result/array_format.rb
14
+ lib/yahoo_stock/result/hash_format.rb
8
15
  lib/yahoo_stock/scrip_symbol.rb
9
- lib/yahoo_stock/history.rb
16
+ lib/yahoo_stock.rb
10
17
  spec/spec_helper.rb
18
+ spec/yahoo_stock/base_spec.rb
19
+ spec/yahoo_stock/interface_spec.rb
20
+ spec/yahoo_stock/interface/history_spec.rb
21
+ spec/yahoo_stock/interface/quote_spec.rb
22
+ spec/yahoo_stock/interface/scrip_symbol_spec.rb
11
23
  spec/yahoo_stock/quote_spec.rb
12
- spec/yahoo_stock/scrip_symbol_spec.rb
13
24
  spec/yahoo_stock/history_spec.rb
25
+ spec/yahoo_stock/result_spec.rb
26
+ spec/yahoo_stock/result/array_format_spec.rb
27
+ spec/yahoo_stock/result/hash_format_spec.rb
28
+ spec/yahoo_stock/scrip_symbol_spec.rb
29
+
data/README.rdoc CHANGED
@@ -7,7 +7,7 @@
7
7
  Provides an interface to yahoo finance to get stock related data. For instance, latest trade related data, volume, 50 day moving average, market cap, etc, virtually any thing that yahoo finance provides.
8
8
 
9
9
  If you don't know the stock / scrip symbol of the company then you can find that out by using the YahooStock::ScripSymbol class. The methods are mentioned in the Usage section below. For instance, YHOO for yahoo, GOOG for google, etc.
10
- The kind of parameters can be passed can be found after initializing the
10
+ The kind of parameters to be passed can be found after initializing the
11
11
  YahooStock::Quote object and passing valid_parameters message, example is given below in the USAGE section.
12
12
 
13
13
  == USAGE:
@@ -40,22 +40,78 @@ quote.add_symbols('MSFT', 'AAPL')
40
40
 
41
41
  quote.remove_symbols('MSFT', 'AAPL')
42
42
 
43
- * To get data for all stocks
43
+ * To find data for all stocks, this will give a string containing all values
44
44
 
45
- quote.get
45
+ quote.results
46
+
47
+ to return an array instead of a string use:
48
+
49
+ quote.results(:to_array).output
50
+
51
+ to return a hash, use:
52
+
53
+ quote.results(:to_hash).output
54
+
55
+ to store results in a file, use:
56
+
57
+ quote.results.store('filename')
46
58
 
47
59
  * To find out the stock symbol for a company
48
60
 
49
- YahooStock::ScripSymbol.print_options('Yahoo', 'Company1', 'Company3')
61
+ symbol = YahooStock::ScripSymbol.new('Yahoo')
62
+
63
+ * To find all symbols for that company, this will give a string containing all values
64
+
65
+ symbol.results
66
+
67
+ to return an array instead of a string use:
68
+
69
+ symbol.results(:to_array).output
70
+
71
+ to return a hash, use:
72
+
73
+ symbol.results(:to_hash).output
74
+
75
+ to store results in a file, use:
76
+
77
+ symbol.results.store('filename')
78
+
79
+ * To find symbols for multiple companies
80
+
81
+ symbols = YahooStock::ScripSymbol.results('Yahoo', 'Company1', 'Company3')
50
82
 
51
- * To store the output in a file
83
+ * to get the result in a string
52
84
 
53
- YahooStock::ScripSymbol.save_options_to_file('path/to/filename','Yahoo', 'Company1', 'Company3')
85
+ symbols.output
86
+
87
+ * to store output in a file
88
+
89
+ symbols.store('filename')
90
+
91
+ * To find historical data for a stock
92
+
93
+ history = YahooStock::History.new(:stock_symbols => 'yhoo', :start_date => Date.today-20, end_date => Date.today -2)
94
+
95
+ to return an array instead of a string use:
96
+
97
+ history.results(:to_array).output
98
+
99
+ to return a hash, use:
100
+
101
+ history.results(:to_hash).output
102
+
103
+ to store results in a file, use:
104
+
105
+ history.results.store('filename')
54
106
 
55
107
  == INSTALL:
56
108
 
57
109
  gem sources -a http://gems.github.com
58
110
 
111
+ OR
112
+
113
+ gem sources -a http://gemcutter.org
114
+
59
115
  sudo gem install nas-yahoo_stock
60
116
 
61
117
  == TESTS:
@@ -0,0 +1,37 @@
1
+ # YahooStock::Base class to find and output results.
2
+ module YahooStock
3
+
4
+ class Base
5
+
6
+ def initialize(interface)
7
+ @interface = interface
8
+ end
9
+
10
+ # Uses the values method of the interface used to show results .
11
+ def find
12
+ @interface.values
13
+ end
14
+
15
+ # Method takes one parameter type or a block
16
+ # type uses the existing format classes namespaced in Result::ClassName.output
17
+ # e.g. * results :to_array
18
+ # * results {YourSpecialFormat.method_name}
19
+ # to store
20
+ def results(type=nil, &block)
21
+ if block_given?
22
+ yield
23
+ else
24
+ return YahooStock::Result.new(find) if !type || type.to_s.empty?
25
+ format_type = type.to_s.sub(/^to_/,'').strip
26
+ return YahooStock::Result.const_get("#{format_type.capitalize}Format").new(find){data_attributes}
27
+ end
28
+ end
29
+
30
+ # Abstract method
31
+ def data_attributes
32
+ raise 'Abstract method called. Use the subclass data_attributes method'
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -1,184 +1,30 @@
1
- =begin
2
-
3
- base url = http://ichart.finance.yahoo.com/table.csv
4
-
5
- params:
6
- s=stock symbol
7
- a=month-1
8
- b=date
9
- c=year
10
- d=month-1
11
- e=date
12
- f=year
13
- g=d or w or m
14
- ignore=.csv
15
-
16
- =end
17
- require 'net/http'
18
1
  module YahooStock
19
- class History
2
+ class History < Base
20
3
 
21
4
  class HistoryError < RuntimeError; end
22
5
 
23
- attr_reader :stock_symbol, :start_date, :end_date, :interval
24
-
25
6
  def initialize(options)
26
7
  unless options.is_a?(Hash)
27
8
  raise HistoryError, 'A hash of start_date, end_date and stock_symbol is expected as parameters'
28
9
  end
29
- validate_keys(options)
30
-
31
- @stock_symbol = options[:stock_symbol]
32
- @start_date = options[:start_date]
33
- @end_date = options[:end_date]
34
- @interval = options[:interval].nil? ? :daily : options[:interval].to_sym
35
-
36
- validate_interval_values(options[:interval]) if options[:interval]
37
- validate_stock_symbol(options)
38
- validate_start_date(options)
39
- validate_end_date(options)
40
- validate_history_range
41
- @base_url = "http://ichart.finance.yahoo.com/table.csv"
42
- end
43
-
44
- def stock_symbol=(stock_symbol)
45
- validate_stock_symbol_attribute(stock_symbol)
46
- @stock_symbol = stock_symbol
47
- end
48
-
49
- def start_date=(start_date)
50
- validate_start_date_type(start_date)
51
- validate_history_range(start_date)
52
- @start_date = start_date
53
- end
54
-
55
- def end_date=(end_date)
56
- validate_end_date_type(end_date)
57
- validate_history_range(start_date, end_date)
58
- @end_date = end_date
59
- end
60
-
61
- def interval=(interval)
62
- validate_interval_values(interval)
63
- @interval = interval
64
- end
65
-
66
- def send_request
67
- if stock_symbol.nil? || start_date.nil? || end_date.nil?
68
- raise HistoryError, 'Cannot send request unless all parameters, ie stock_symbol, start and end date are present'
69
- end
70
- if !(start_date.is_a?(Date) || end_date.is_a?(Date))
71
- raise HistoryError, 'Start and end date should be of type Date'
72
- end
73
- response = Net::HTTP.get_response(URI.parse(uri))
74
- if response.code == '200'
75
- return response.body
76
- else
77
- raise "No historical data found for the stock symbol: #{stock_symbol}"
78
- end
79
- end
80
-
81
- # Return an array of hashes with latest date first
82
- # Each array element contains a hash of date, volume traded, opening, closing, high and low prices
83
- def results
84
- values = send_request.split(/\n/)
85
- headers, daily_data = values.partition{|h| h==values[0]}
86
- headings = headers.first.split(',')
87
- data = []
88
- daily_data.each do |datum|
89
- daily_values = {}
90
- datum.split(',').each_with_index do |item, i|
91
- daily_values[headings[i]] = item
92
- end
93
- data << daily_values
94
- end
95
- data
96
- end
97
-
98
- private
99
-
100
- def uri
101
- frequency = case interval
102
- when :daily : 'd'
103
- when :weekly : 'w'
104
- when :monthly : 'm'
105
- end
106
- start_date_param = "a=#{sprintf("%02d", start_date.month-1)}&b=#{start_date.day}&c=#{start_date.year}"
107
- end_date_param = "d=#{sprintf("%02d", end_date.month-1)}&e=#{end_date.day}&f=#{end_date.year}"
108
- params="s=#{stock_symbol}&#{start_date_param}&#{end_date_param}&g=#{frequency}&ignore=.csv"
109
- "#{@base_url}?#{params}"
110
- end
111
-
112
- def validate_interval_values(interval_value=interval)
113
- valid_values = [:daily, :weekly, :monthly]
114
- unless valid_values.include?(interval_value)
115
- raise HistoryError, "Allowed values for interval are #{valid_values.join(', ')}"
116
- end
117
- end
118
-
119
- def validate_stock_symbol(options)
120
- unless options.keys.include?(:stock_symbol)
121
- raise HistoryError, ':stock_symbol key is not present in the parameter hash'
122
- end
123
- validate_stock_symbol_attribute
124
- end
125
-
126
- def validate_stock_symbol_attribute(symbol=stock_symbol)
127
- if symbol.nil? || symbol.empty?
128
- raise HistoryError, ':stock_symbol value cannot be nil or blank'
129
- end
130
- end
131
-
132
- def validate_start_date(options)
133
- validate_date_options(:start_date, options)
134
- validate_start_date_type
135
- end
136
-
137
- def validate_end_date(options)
138
- validate_date_options(:end_date, options)
139
- validate_end_date_type
140
- end
141
-
142
- def validate_history_range(s_date = start_date, e_date = end_date)
143
- if s_date >= Date.today
144
- raise HistoryError, "Start date must be in the past"
145
- end
146
- if e_date >= Date.today
147
- raise HistoryError, "End date must be in the past"
148
- end
149
- if e_date < s_date
150
- raise HistoryError, "End date must be greater than the start date"
151
- end
10
+ @interface = YahooStock::Interface::History.new(options)
152
11
  end
153
12
 
154
- def validate_end_date_type(e_date=end_date)
155
- unless e_date.is_a?(Date)
156
- raise HistoryError, "End date must be of type Date"
157
- end
13
+ def find
14
+ @find_values = super()
15
+ @find_values.sub(/Date.*\s/,'') if @find_values
158
16
  end
159
17
 
160
- def validate_start_date_type(s_date=start_date)
161
- unless s_date.is_a?(Date)
162
- raise HistoryError, "Start date must be of type Date"
163
- end
164
- end
165
-
166
- def validate_date_options(date_type, options)
167
- unless options.keys.include?(date_type)
168
- raise HistoryError, ":#{date_type} key is not present in the parameter hash"
169
- end
170
-
171
- if options[date_type].nil? || options[date_type].to_s.empty?
172
- raise HistoryError, ":#{date_type} value cannot be blank"
173
- end
18
+ def values_with_header
19
+ @interface.values
174
20
  end
175
21
 
176
- def validate_keys(options)
177
- valid_keys = [:stock_symbol, :start_date, :end_date, :interval]
178
- invalid_keys = []
179
- options.keys.each{|key| invalid_keys << key unless valid_keys.include?(key) }
180
- unless invalid_keys.length.zero?
181
- raise HistoryError, "An invalid key '#{invalid_keys.join(',')}' is passed in the parameters. Allowed keys are #{valid_keys.join(', ')}"
22
+ def data_attributes
23
+ return unless values_with_header
24
+ data_attributes = /Date.*\s/.match(values_with_header)
25
+ unless data_attributes.nil?
26
+ @data_attributes = data_attributes[0].sub(/\s*$/,'').split(',')
27
+ @data_attributes.map &:strip
182
28
  end
183
29
  end
184
30
 
@@ -0,0 +1,186 @@
1
+ =begin
2
+ params:
3
+ s=stock symbol
4
+ a=month-1
5
+ b=date
6
+ c=year
7
+ d=month-1
8
+ e=date
9
+ f=year
10
+ g=d or w or m
11
+ ignore=.csv
12
+
13
+ =end
14
+
15
+ module YahooStock
16
+ # ==DESCRIPTION:
17
+ #
18
+ # Class to generate the right url and interface with yahoo to get history of a stock
19
+ #
20
+ class Interface::History < Interface
21
+
22
+ class HistoryError < RuntimeError ; end
23
+
24
+ attr_reader :stock_symbol, :start_date, :end_date, :interval
25
+
26
+ # The options parameter expects a hash with 4 key value pairs
27
+ #
28
+ # :stock_symbol => 'goog', :start_date => Date.today-30,
29
+ # :end_date => Date.today-10, :interval => :weekly
30
+ #
31
+ # The interval option accepts :daily, :monthly and :weekly values
32
+ #
33
+ # The interval key is optional, if not used then by default uses :daily
34
+ #
35
+ # and provides daily history for the specified date range
36
+ #
37
+ def initialize(options)
38
+ @base_url = BASE_URLS[:history]
39
+ validate_keys(options)
40
+
41
+ @stock_symbol = options[:stock_symbol]
42
+ @start_date = options[:start_date]
43
+ @end_date = options[:end_date]
44
+ @interval = options[:interval].nil? ? :daily : options[:interval].to_sym
45
+
46
+ validate_interval_values(options[:interval]) if options[:interval]
47
+ validate_stock_symbol(options)
48
+ validate_start_date(options)
49
+ validate_end_date(options)
50
+ validate_history_range
51
+ end
52
+
53
+ # Make sure that stock symbol is not nil or an empty string before setting it
54
+ def stock_symbol=(stock_symbol)
55
+ validate_stock_symbol_attribute(stock_symbol)
56
+ @stock_symbol = stock_symbol
57
+ end
58
+
59
+ # Before setting start date
60
+ # - Make sure that it is of type date
61
+ # - Make sure that it is before the end date
62
+ def start_date=(start_date)
63
+ validate_start_date_type(start_date)
64
+ validate_history_range(start_date)
65
+ @start_date = start_date
66
+ end
67
+
68
+ # Before setting end date
69
+ # - Make sure that it is of type date
70
+ # - Make sure that it is after the start date
71
+ def end_date=(end_date)
72
+ validate_end_date_type(end_date)
73
+ validate_history_range(start_date, end_date)
74
+ @end_date = end_date
75
+ end
76
+
77
+ # Set interval to specify whether daily, weekly or monthly histroy required
78
+ def interval=(interval)
79
+ validate_interval_values(interval)
80
+ @interval = interval
81
+ end
82
+
83
+ # Generate full uri with the help of uri method of the superclass
84
+ def uri
85
+ frequency = case interval
86
+ when :daily : 'd'
87
+ when :weekly : 'w'
88
+ when :monthly : 'm'
89
+ end
90
+ @uri_parameters = {:a => sprintf("%02d", start_date.month-1), :b => start_date.day,
91
+ :c => start_date.year, :d => sprintf("%02d", end_date.month-1),
92
+ :e => end_date.day, :f => end_date.year, :s => stock_symbol,
93
+ :g => frequency, :ignore => '.csv'}
94
+ super()
95
+ end
96
+
97
+ # Get uri content with the help of get method of the super class
98
+ def get
99
+ if stock_symbol.nil? || start_date.nil? || end_date.nil?
100
+ raise HistoryError, 'Cannot send request unless all parameters, ie stock_symbol, start and end date are present'
101
+ end
102
+ if !(start_date.is_a?(Date) || end_date.is_a?(Date))
103
+ raise HistoryError, 'Start and end date should be of type Date'
104
+ end
105
+ uri
106
+ super()
107
+ end
108
+
109
+ private
110
+
111
+ def validate_interval_values(interval_value=interval)
112
+ valid_values = [:daily, :weekly, :monthly]
113
+ unless valid_values.include?(interval_value)
114
+ raise HistoryError, "Allowed values for interval are #{valid_values.join(', ')}"
115
+ end
116
+ end
117
+
118
+ def validate_stock_symbol(options)
119
+ unless options.keys.include?(:stock_symbol)
120
+ raise HistoryError, ':stock_symbol key is not present in the parameter hash'
121
+ end
122
+ validate_stock_symbol_attribute
123
+ end
124
+
125
+ def validate_stock_symbol_attribute(symbol=stock_symbol)
126
+ if symbol.nil? || symbol.empty?
127
+ raise HistoryError, ':stock_symbol value cannot be nil or blank'
128
+ end
129
+ end
130
+
131
+ def validate_start_date(options)
132
+ validate_date_options(:start_date, options)
133
+ validate_start_date_type
134
+ end
135
+
136
+ def validate_end_date(options)
137
+ validate_date_options(:end_date, options)
138
+ validate_end_date_type
139
+ end
140
+
141
+ def validate_history_range(s_date = start_date, e_date = end_date)
142
+ if s_date >= Date.today
143
+ raise HistoryError, "Start date must be in the past"
144
+ end
145
+ if e_date >= Date.today
146
+ raise HistoryError, "End date must be in the past"
147
+ end
148
+ if e_date < s_date
149
+ raise HistoryError, "End date must be greater than the start date"
150
+ end
151
+ end
152
+
153
+ def validate_end_date_type(e_date=end_date)
154
+ unless e_date.is_a?(Date)
155
+ raise HistoryError, "End date must be of type Date"
156
+ end
157
+ end
158
+
159
+ def validate_start_date_type(s_date=start_date)
160
+ unless s_date.is_a?(Date)
161
+ raise HistoryError, "Start date must be of type Date"
162
+ end
163
+ end
164
+
165
+ def validate_date_options(date_type, options)
166
+ unless options.keys.include?(date_type)
167
+ raise HistoryError, ":#{date_type} key is not present in the parameter hash"
168
+ end
169
+
170
+ if options[date_type].nil? || options[date_type].to_s.empty?
171
+ raise HistoryError, ":#{date_type} value cannot be blank"
172
+ end
173
+ end
174
+
175
+ def validate_keys(options)
176
+ valid_keys = [:stock_symbol, :start_date, :end_date, :interval]
177
+ invalid_keys = []
178
+ options.keys.each{|key| invalid_keys << key unless valid_keys.include?(key) }
179
+ unless invalid_keys.length.zero?
180
+ raise HistoryError, "An invalid key '#{invalid_keys.join(',')}' is passed in the parameters. Allowed keys are #{valid_keys.join(', ')}"
181
+ end
182
+ end
183
+
184
+ end
185
+
186
+ end