sp500_analyzer 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.DS_Store +0 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/README.md +24 -0
- data/Rakefile +6 -0
- data/bin/console +7 -0
- data/bin/setup +8 -0
- data/bin/sp500_analyzer +5 -0
- data/config/environment.rb +14 -0
- data/lib/sp500_analyzer.rb +5 -0
- data/lib/sp500_analyzer/analyze_data.rb +93 -0
- data/lib/sp500_analyzer/cli.rb +86 -0
- data/lib/sp500_analyzer/crash.rb +20 -0
- data/lib/sp500_analyzer/data_point.rb +65 -0
- data/lib/sp500_analyzer/display_data.rb +132 -0
- data/lib/sp500_analyzer/scraper.rb +66 -0
- data/lib/sp500_analyzer/version.rb +3 -0
- data/sp500_analyzer.gemspec +25 -0
- data/spec.md +14 -0
- metadata +136 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e206f3f760cb277612d0f6a1fc2948f701fca9b2
|
4
|
+
data.tar.gz: 4760b1105d1f5f59d5788deb6a07e68e8a21c4dc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a62d063179bf2fa18f9a52a858947939878c44418e4db8714a0c04f71d586f20ae083b346897447cda187db9ae451e16f3f15c9eec643e50b99478f0cc39f9e7
|
7
|
+
data.tar.gz: c454aea9024dda69c8a1503df2b68a613d7ce8abac4a21e0a27aa43a5cc15eeb82e3e74009b4ee66dca1b01416e203bbdd68aed1aae9cff4b92fe99d28b7c808
|
data/.DS_Store
ADDED
Binary file
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# S&P 500 Analyzer
|
2
|
+
|
3
|
+
This gem is created to analyze all monthly pricing data from the S&P 500. Each datapoint is pulled from multpl.com's "S&P 500 Historical Prices by Month" chart which includes opening prices from the first of each month as early as January, 1871. Since the S&P 500 did not come into existence until 1957, all price data before this point are estimates of what a similar index would project given historical market conditions. These estimates come from the research and writing of Robert Shiller, presented in his book "Irrational Exuberance." Using this data, users are able to analyze and compare market conditions and pricing over long periods of time.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Install using gem command: `gem install s_and_p_analyzer`
|
8
|
+
|
9
|
+
## Usage
|
10
|
+
|
11
|
+
After installation, run `sp500_analyzer` and choose from one of the seven menu options
|
12
|
+
|
13
|
+
** When using the "Market crash info" function, make sure to read through the given instructions in order to define your own stock market crash parameters
|
14
|
+
|
15
|
+
## Development
|
16
|
+
|
17
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
18
|
+
|
19
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
20
|
+
|
21
|
+
## Contributing
|
22
|
+
|
23
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/danpaulgo/s_and_p_analyzer.
|
24
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
data/bin/setup
ADDED
data/bin/sp500_analyzer
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require "bundler"
|
2
|
+
require "nokogiri"
|
3
|
+
require "open-uri"
|
4
|
+
require "date"
|
5
|
+
require "pry"
|
6
|
+
# Bundler.require
|
7
|
+
|
8
|
+
require_relative "../lib/sp500_analyzer/cli.rb"
|
9
|
+
require_relative "../lib/sp500_analyzer/version.rb"
|
10
|
+
require_relative "../lib/sp500_analyzer/data_point.rb"
|
11
|
+
require_relative "../lib/sp500_analyzer/display_data.rb"
|
12
|
+
require_relative "../lib/sp500_analyzer/scraper.rb"
|
13
|
+
require_relative "../lib/sp500_analyzer/analyze_data.rb"
|
14
|
+
require_relative "../lib/sp500_analyzer/crash.rb"
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require_relative "../../config/environment.rb"
|
2
|
+
|
3
|
+
class AnalyzeData
|
4
|
+
|
5
|
+
def self.annual_max(year)
|
6
|
+
array = DataPoint.find_by_year(year)
|
7
|
+
array.max_by do |datapoint|
|
8
|
+
datapoint.price
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.annual_min(year)
|
13
|
+
array = DataPoint.find_by_year(year)
|
14
|
+
array.min_by do |datapoint|
|
15
|
+
datapoint.price
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.market_crashes(fraction, time_in_months)
|
20
|
+
crashes = []
|
21
|
+
DataPoint.all.each do |datapoint|
|
22
|
+
# binding.pry
|
23
|
+
loop_id = datapoint.id
|
24
|
+
valid_crash = false
|
25
|
+
until loop_id + time_in_months < datapoint.id || loop_id < 0
|
26
|
+
valid_crash = true if datapoint.price <= DataPoint.all[loop_id].price * fraction
|
27
|
+
loop_id -= 1
|
28
|
+
end
|
29
|
+
crashes << datapoint if valid_crash
|
30
|
+
end
|
31
|
+
crashes
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.market_peaks(fraction, time_in_months)
|
35
|
+
peaks = []
|
36
|
+
market_crashes(fraction, time_in_months).each do |crash|
|
37
|
+
DataPoint.all.each do |datapoint|
|
38
|
+
peaks << datapoint if datapoint.price == crash.historical_max.price && datapoint.price == datapoint.historical_max.price && !peaks.include?(datapoint)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
peaks
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.find_recovery(datapoint)
|
45
|
+
loop_id = datapoint.id + 1
|
46
|
+
recovered = false
|
47
|
+
until recovered == true || loop_id > DataPoint.all.count
|
48
|
+
if DataPoint.all[loop_id].price >= datapoint.price
|
49
|
+
recovered = true
|
50
|
+
recovery_point = DataPoint.all[loop_id]
|
51
|
+
end
|
52
|
+
loop_id += 1
|
53
|
+
end
|
54
|
+
recovery_point
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.market_crash_mins(fraction, time_in_months)
|
58
|
+
peaks = market_peaks(fraction, time_in_months)
|
59
|
+
mins = []
|
60
|
+
peaks.each do |peak|
|
61
|
+
mins << min_within_period(peak, find_recovery(peak))
|
62
|
+
end
|
63
|
+
mins
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.price_difference(datapoint_1, datapoint_2)
|
67
|
+
datapoint_1.price - datapoint_2.price
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.timeframe(datapoint_1, datapoint_2)
|
71
|
+
data_spread = []
|
72
|
+
range = [datapoint_1.id, datapoint_2.id]
|
73
|
+
loop_id = range.min
|
74
|
+
while loop_id <= range.max
|
75
|
+
data_spread << DataPoint.all[loop_id]
|
76
|
+
loop_id += 1
|
77
|
+
end
|
78
|
+
data_spread
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.max_within_period(datapoint_1, datapoint_2)
|
82
|
+
timeframe(datapoint_1,datapoint_2).max_by do |datapoint|
|
83
|
+
datapoint.price
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.min_within_period(datapoint_1, datapoint_2)
|
88
|
+
timeframe(datapoint_1,datapoint_2).min_by do |datapoint|
|
89
|
+
datapoint.price
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require_relative "../../config/environment.rb"
|
2
|
+
|
3
|
+
class CLI
|
4
|
+
|
5
|
+
def call
|
6
|
+
Scraper.array_to_datapoints
|
7
|
+
DataPoint.set_historical_max
|
8
|
+
start
|
9
|
+
end
|
10
|
+
|
11
|
+
def menu
|
12
|
+
puts "1. See all prices by month"
|
13
|
+
puts "2. See all prices by year"
|
14
|
+
puts "3. See all prices for single year"
|
15
|
+
puts "4. Extended info for single date"
|
16
|
+
puts "5. Maximum prices by year"
|
17
|
+
puts "6. Market crash info"
|
18
|
+
puts "7. Compare two dates"
|
19
|
+
end
|
20
|
+
|
21
|
+
def start
|
22
|
+
puts "\nWelcome to S&P Analyzer. Please choose from the following menu options:"
|
23
|
+
puts "\n"
|
24
|
+
go = ""
|
25
|
+
until go.downcase == "n"
|
26
|
+
go = ""
|
27
|
+
menu
|
28
|
+
input = ""
|
29
|
+
puts "\n"
|
30
|
+
until input.downcase == "q" || (input.to_i >= 1 && input.to_i <= 7)
|
31
|
+
print "Item No. (or \"q\" to exit): "
|
32
|
+
input = gets.strip
|
33
|
+
end
|
34
|
+
puts "\n"
|
35
|
+
case input
|
36
|
+
when "1"
|
37
|
+
DisplayData.display_points(DataPoint.all)
|
38
|
+
when "2"
|
39
|
+
DisplayData.display_yearly
|
40
|
+
when "3"
|
41
|
+
valid_year = false
|
42
|
+
until valid_year == true
|
43
|
+
print "Enter year: "
|
44
|
+
year = gets.strip.to_i
|
45
|
+
valid_year = DisplayData.validate_year(year)
|
46
|
+
end
|
47
|
+
DisplayData.display_by_year(year)
|
48
|
+
when "4"
|
49
|
+
valid_date = false
|
50
|
+
until valid_date == true
|
51
|
+
print "Enter month (mm/yyyy): "
|
52
|
+
date = gets.strip
|
53
|
+
valid_date = DisplayData.validate_date(date)
|
54
|
+
end
|
55
|
+
DisplayData.extended_info(date)
|
56
|
+
when "5"
|
57
|
+
DisplayData.display_max_by_year
|
58
|
+
when "6"
|
59
|
+
DisplayData.display_market_crash_info
|
60
|
+
when "7"
|
61
|
+
valid_dates = [false,false]
|
62
|
+
until valid_dates[0] == true
|
63
|
+
print "Enter first month (mm/yyyy): "
|
64
|
+
date_1 = gets.strip
|
65
|
+
valid_dates[0] = DisplayData.validate_date(date_1)
|
66
|
+
end
|
67
|
+
until valid_dates[1] == true
|
68
|
+
print "Enter second month (mm/yyyy): "
|
69
|
+
date_2 = gets.strip
|
70
|
+
valid_dates[1] = DisplayData.validate_date(date_2)
|
71
|
+
end
|
72
|
+
DisplayData.display_comparison_info(date_1, date_2)
|
73
|
+
when "q"
|
74
|
+
go = "n"
|
75
|
+
end
|
76
|
+
if go != "n"
|
77
|
+
puts "\n"
|
78
|
+
until go.downcase == "y" || go.downcase == "n"
|
79
|
+
print "Would you like to complete another action? (y/n) "
|
80
|
+
go = gets.strip
|
81
|
+
end
|
82
|
+
puts "\n"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative "../../config/environment.rb"
|
2
|
+
|
3
|
+
class Crash
|
4
|
+
|
5
|
+
attr_reader :peak, :bottom, :crash_period
|
6
|
+
attr_accessor :recovery_point
|
7
|
+
|
8
|
+
def initialize(peak, bottom)
|
9
|
+
@peak = peak
|
10
|
+
@bottom = bottom
|
11
|
+
@crash_period = bottom.id - peak.id
|
12
|
+
set_recovery
|
13
|
+
end
|
14
|
+
|
15
|
+
def set_recovery
|
16
|
+
@recovery_point = AnalyzeData.find_recovery(peak)
|
17
|
+
# @recovery_point = "(Market has not recovered)" if @recovery_point.nil?
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require_relative "../../config/environment.rb"
|
2
|
+
|
3
|
+
class DataPoint
|
4
|
+
|
5
|
+
@@all = []
|
6
|
+
|
7
|
+
attr_accessor :id, :date, :price, :monthly_change, :yearly_change, :historical_max, :max_date
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@@all << self
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.all
|
14
|
+
@@all
|
15
|
+
end
|
16
|
+
|
17
|
+
def previous_month
|
18
|
+
year = date.year
|
19
|
+
month = date.month
|
20
|
+
day = date.day
|
21
|
+
if month == 1
|
22
|
+
prev_month = 12
|
23
|
+
else
|
24
|
+
prev_month = month-1
|
25
|
+
end
|
26
|
+
Date.new(year, prev_month, day)
|
27
|
+
end
|
28
|
+
|
29
|
+
def previous_year
|
30
|
+
year = date.year
|
31
|
+
month = date.month
|
32
|
+
day = date.day
|
33
|
+
prev_year = year-1
|
34
|
+
Date.new(year-1, month, day)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.set_historical_max
|
38
|
+
all.each_with_index do |datapoint, index|
|
39
|
+
loop_index = 0
|
40
|
+
maximum = datapoint
|
41
|
+
until loop_index >= index
|
42
|
+
if all[loop_index].price >= maximum.price
|
43
|
+
maximum = all[loop_index]
|
44
|
+
end
|
45
|
+
loop_index += 1
|
46
|
+
end
|
47
|
+
datapoint.historical_max = maximum
|
48
|
+
end
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.find_by_year(year)
|
53
|
+
all.select do |datapoint|
|
54
|
+
datapoint.date.year == year
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.find_by_date(date)
|
59
|
+
date_array = date.split("/")
|
60
|
+
all.detect do |datapoint|
|
61
|
+
datapoint.date.month == date_array[0].to_i && datapoint.date.year == date_array[1].to_i
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require_relative "../../config/environment.rb"
|
2
|
+
class DisplayData
|
3
|
+
|
4
|
+
def self.display_price(price)
|
5
|
+
if price > 0
|
6
|
+
price_string = "$" + sprintf('%.2f', price)
|
7
|
+
else
|
8
|
+
price_string = "-$" + sprintf('%.2f', price.abs)
|
9
|
+
end
|
10
|
+
price_string
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.validate_date(date)
|
14
|
+
valid = false
|
15
|
+
date_array = date.split("/")
|
16
|
+
valid = true if date_array[0].to_i >= 1 && date_array[0].to_i <= 12 && date_array[1].to_i >= 1871 && date_array[1].to_i <= DataPoint.all.last.date.year
|
17
|
+
valid
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.validate_year(year)
|
21
|
+
valid = false
|
22
|
+
valid = true if year >= 1871 && year <= DataPoint.all.last.date.year
|
23
|
+
valid
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.display_datapoint(datapoint)
|
27
|
+
"Date: #{datapoint.date.strftime('%b-%d-%Y')} Price: #{display_price(datapoint.price)}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.extended_info(date)
|
31
|
+
datapoint = DataPoint.find_by_date(date)
|
32
|
+
puts "-"*40
|
33
|
+
puts "Date: #{datapoint.date.strftime('%B %d, %Y')}\n\n"
|
34
|
+
puts "Price: #{display_price(datapoint.price)}"
|
35
|
+
puts "Change over previous month: #{'+' if datapoint.monthly_change > 0}#{display_price(datapoint.monthly_change)}"
|
36
|
+
puts "Change over previous year: #{'+' if datapoint.yearly_change > 0}#{display_price(datapoint.yearly_change)}"
|
37
|
+
puts "Historical maximum (#{datapoint.historical_max.date.strftime('%m/%d/%Y')}): #{display_price(datapoint.historical_max.price)}"
|
38
|
+
puts "-"*40
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.display_points(array)
|
42
|
+
puts "-"*40
|
43
|
+
array.each do |datapoint|
|
44
|
+
puts display_datapoint(datapoint)
|
45
|
+
puts "-"*40
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.display_yearly
|
50
|
+
array = DataPoint.all.select do |point|
|
51
|
+
point.date.month == 1
|
52
|
+
end
|
53
|
+
self.display_points(array)
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.display_by_year(year)
|
57
|
+
array = DataPoint.all.select do |point|
|
58
|
+
point.date.year == year
|
59
|
+
end
|
60
|
+
self.display_points(array)
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.display_max_by_year
|
64
|
+
year = ""
|
65
|
+
until year == "all" || (year.to_i >= 1871 && year.to_i <= DataPoint.all.last.date.year)
|
66
|
+
print "Enter year (or \"all\" to see maximums for all years): "
|
67
|
+
year = gets.strip
|
68
|
+
end
|
69
|
+
if year == "all"
|
70
|
+
all_years = (DataPoint.all.first.date.year..DataPoint.all.last.date.year).to_a
|
71
|
+
maximums = all_years.map do |year|
|
72
|
+
AnalyzeData.annual_max(year)
|
73
|
+
end
|
74
|
+
display_points(maximums)
|
75
|
+
else
|
76
|
+
puts "-"*40
|
77
|
+
puts display_datapoint(AnalyzeData.annual_max(year.to_i))
|
78
|
+
puts "-"*40
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.display_market_crash_info
|
83
|
+
puts "A stock market crash may be defined as \"a rapid and often unanticipated decrease in stock prices.\" In order to distinguish crash periods within our data, we must specify the variables of \"rapidness\" (timespan) and \"decrease in stock price\". The decrease in stock price will be set using a percentage value and the timespan will be set using a number of months.\n\n"
|
84
|
+
puts "Example: Setting a decline percentage of '20' and a timespan of '12' will provide information for all stock market crashes where the price of the S&P 500 decreased by 20% within 1 year (12 months).\n\n"
|
85
|
+
decline = 0
|
86
|
+
timespan = 0
|
87
|
+
until decline >=1 && decline < 100
|
88
|
+
print "Set decrease percentage(1-99): "
|
89
|
+
decline = gets.strip.to_i
|
90
|
+
end
|
91
|
+
until timespan >= 1
|
92
|
+
print "Set timespan in months: "
|
93
|
+
timespan = gets.strip.to_i
|
94
|
+
end
|
95
|
+
decline_float = 1.0 - (decline.to_f/100.0)
|
96
|
+
market_peaks = AnalyzeData.market_peaks(decline_float, timespan)
|
97
|
+
market_bottoms = AnalyzeData.market_crash_mins(decline_float, timespan)
|
98
|
+
market_peaks.each_with_index do |peak, index|
|
99
|
+
puts "-"*50
|
100
|
+
puts "CRASH #{index+1} INFO\n\n"
|
101
|
+
crash = Crash.new(peak, market_bottoms[index])
|
102
|
+
puts "Market Peak: " + crash.peak.date.strftime("%B %d, %Y") + " - " + display_price(crash.peak.price)
|
103
|
+
puts "Market Trough: " + crash.bottom.date.strftime("%B %d, %Y") + " - " + display_price(crash.bottom.price)
|
104
|
+
puts "Market Recovery: "+ crash.recovery_point.date.strftime("%B %d, %Y") + " - " + display_price(crash.recovery_point.price)
|
105
|
+
end
|
106
|
+
puts "-"*50
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.display_comparison_info(date_1, date_2)
|
110
|
+
datapoint1 = DataPoint.find_by_date(date_1)
|
111
|
+
datapoint2 = DataPoint.find_by_date(date_2)
|
112
|
+
if datapoint1.date > datapoint2.date
|
113
|
+
datapoint1 = DataPoint.find_by_date(date_2)
|
114
|
+
datapoint2 = DataPoint.find_by_date(date_1)
|
115
|
+
end
|
116
|
+
difference = datapoint2.price - datapoint1.price
|
117
|
+
string_difference = "#{'+' if difference > 0}#{display_price(difference)}"
|
118
|
+
high = AnalyzeData.max_within_period(datapoint1,datapoint2)
|
119
|
+
low = AnalyzeData.min_within_period(datapoint1,datapoint2)
|
120
|
+
puts "-"*50
|
121
|
+
puts "1. " + display_datapoint(datapoint1)
|
122
|
+
puts "2. " + display_datapoint(datapoint2)
|
123
|
+
puts "\nChange in price between #{datapoint1.date.strftime('%m/%d/%Y')} and #{datapoint2.date.strftime('%m/%d/%Y')}:"
|
124
|
+
puts "#{string_difference}"
|
125
|
+
puts "\nHighest point between #{datapoint1.date.strftime('%m/%d/%Y')} and #{datapoint2.date.strftime('%m/%d/%Y')}:"
|
126
|
+
puts display_datapoint(high)
|
127
|
+
puts "\nLowest point between #{datapoint1.date.strftime('%m/%d/%Y')} and #{datapoint2.date.strftime('%m/%d/%Y')}:"
|
128
|
+
puts display_datapoint(low)
|
129
|
+
puts "-"*50
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require_relative "../../config/environment.rb"
|
2
|
+
|
3
|
+
class Scraper
|
4
|
+
|
5
|
+
URL = "http://www.multpl.com/s-p-500-historical-prices/table/by-month"
|
6
|
+
|
7
|
+
def self.webpage_to_html
|
8
|
+
Nokogiri::HTML(open(URL)).css("table#datatable tr")
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.string_to_date(string)
|
12
|
+
# month = 0
|
13
|
+
data_array = string.split(/,|\s/).select{ |s| !s.strip.empty? }
|
14
|
+
case data_array[0]
|
15
|
+
when "Jan"
|
16
|
+
month = 1
|
17
|
+
when "Feb"
|
18
|
+
month = 2
|
19
|
+
when "Mar"
|
20
|
+
month = 3
|
21
|
+
when "Apr"
|
22
|
+
month = 4
|
23
|
+
when "May"
|
24
|
+
month = 5
|
25
|
+
when "Jun"
|
26
|
+
month = 6
|
27
|
+
when "Jul"
|
28
|
+
month = 7
|
29
|
+
when "Aug"
|
30
|
+
month = 8
|
31
|
+
when "Sep"
|
32
|
+
month = 9
|
33
|
+
when "Oct"
|
34
|
+
month = 10
|
35
|
+
when "Nov"
|
36
|
+
month = 11
|
37
|
+
when "Dec"
|
38
|
+
month = 12
|
39
|
+
end
|
40
|
+
Date.new(data_array[2].to_i, month, data_array[1].to_i)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.array_to_datapoints
|
44
|
+
# new_data_points = []
|
45
|
+
webpage_to_html.reverse.each_with_index do |row, index|
|
46
|
+
price = row.css("td.right").text.gsub(",","").to_f
|
47
|
+
date = row.css("td.left").text
|
48
|
+
# binding.pry
|
49
|
+
if price > 0.0 && !date.empty?
|
50
|
+
dp = DataPoint.new
|
51
|
+
dp.id = index
|
52
|
+
dp.date = string_to_date(date)
|
53
|
+
dp.price = price
|
54
|
+
if DataPoint.all[index-1].price > 0.0
|
55
|
+
dp.monthly_change = price - DataPoint.all[index-1].price
|
56
|
+
end
|
57
|
+
if !DataPoint.all[index-12].nil? && DataPoint.all[index-12].price > 0.0
|
58
|
+
dp.yearly_change = price - DataPoint.all[index-12].price
|
59
|
+
end
|
60
|
+
# new_data_points << dp
|
61
|
+
end
|
62
|
+
end
|
63
|
+
nil
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'sp500_analyzer/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "sp500_analyzer"
|
8
|
+
spec.version = SP500Analyzer::VERSION
|
9
|
+
spec.authors = ["Daniel Goldberg"]
|
10
|
+
spec.email = ["danpaulgo@aol.com"]
|
11
|
+
spec.summary = "This gem allows for basic analyzation of stock market conditions using S&P 500 historical price data."
|
12
|
+
# spec.description = %q{TODO: Write a longer description or delete this line.}
|
13
|
+
spec.homepage = "https://github.com/danpaulgo/s_and_p_analyzer"
|
14
|
+
spec.require_paths = ["lib"]
|
15
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
16
|
+
f.match(%r{^(test|spec|features)/})
|
17
|
+
end
|
18
|
+
spec.executables << "sp500_analyzer"
|
19
|
+
|
20
|
+
spec.add_development_dependency "bundler", "~> 1.13"
|
21
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
22
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
23
|
+
spec.add_development_dependency "nokogiri", "~> 1.0"
|
24
|
+
spec.add_development_dependency "pry", "~> 0"
|
25
|
+
end
|
data/spec.md
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# Specifications for the CLI Assessment
|
2
|
+
|
3
|
+
Specs:
|
4
|
+
- [x] Have a CLI for interfacing with the application
|
5
|
+
|
6
|
+
The bin/analyzer file opens up to a CLI application. Upon opening, the user is presented with a menu of 7 options to view or analyze stock market data. The user is able to choose an option and enter additional information in order to retrieve information.
|
7
|
+
|
8
|
+
- [x] Pull data from an external source
|
9
|
+
|
10
|
+
All data is pulled from "www.multpl.com" which includes a table that provides S&P 500 pricing data dating back to 1871. Each row of data is scraped from the webpage and then used to create a "datapoint" object.
|
11
|
+
|
12
|
+
- [x] Implement both list and detail views
|
13
|
+
|
14
|
+
The main menu contains a list of 7 options. The user may choose from these options in order to retrieve more detailed information on individual dates, ranges, or the market in its entirety.
|
metadata
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sp500_analyzer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Daniel Goldberg
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-02-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.13'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.13'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: nokogiri
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description:
|
84
|
+
email:
|
85
|
+
- danpaulgo@aol.com
|
86
|
+
executables:
|
87
|
+
- sp500_analyzer
|
88
|
+
extensions: []
|
89
|
+
extra_rdoc_files: []
|
90
|
+
files:
|
91
|
+
- ".DS_Store"
|
92
|
+
- ".gitignore"
|
93
|
+
- ".rspec"
|
94
|
+
- ".travis.yml"
|
95
|
+
- Gemfile
|
96
|
+
- README.md
|
97
|
+
- Rakefile
|
98
|
+
- bin/console
|
99
|
+
- bin/setup
|
100
|
+
- bin/sp500_analyzer
|
101
|
+
- config/environment.rb
|
102
|
+
- lib/sp500_analyzer.rb
|
103
|
+
- lib/sp500_analyzer/analyze_data.rb
|
104
|
+
- lib/sp500_analyzer/cli.rb
|
105
|
+
- lib/sp500_analyzer/crash.rb
|
106
|
+
- lib/sp500_analyzer/data_point.rb
|
107
|
+
- lib/sp500_analyzer/display_data.rb
|
108
|
+
- lib/sp500_analyzer/scraper.rb
|
109
|
+
- lib/sp500_analyzer/version.rb
|
110
|
+
- sp500_analyzer.gemspec
|
111
|
+
- spec.md
|
112
|
+
homepage: https://github.com/danpaulgo/s_and_p_analyzer
|
113
|
+
licenses: []
|
114
|
+
metadata: {}
|
115
|
+
post_install_message:
|
116
|
+
rdoc_options: []
|
117
|
+
require_paths:
|
118
|
+
- lib
|
119
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '0'
|
124
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
125
|
+
requirements:
|
126
|
+
- - ">="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '0'
|
129
|
+
requirements: []
|
130
|
+
rubyforge_project:
|
131
|
+
rubygems_version: 2.5.1
|
132
|
+
signing_key:
|
133
|
+
specification_version: 4
|
134
|
+
summary: This gem allows for basic analyzation of stock market conditions using S&P
|
135
|
+
500 historical price data.
|
136
|
+
test_files: []
|