-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstock_picker.rb
executable file
·163 lines (118 loc) · 3.96 KB
/
stock_picker.rb
1
2
3
4
5
6
7
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
#!/usr/bin/ruby
#
# stock_picker.rb
#
# Picks best day to buy and sell from a list of prices
#
# 20170202 GH Created
# 20170203 GH Solved
#
def min_hash(prices)
prices_copy = prices.dup
min_price = prices_copy.min
min_price_indices = prices_copy.each_index.select{|i| prices_copy[i] == min_price}
# Need to get min day that is not the last day
if min_price_indices.length == 1 && min_price_indices[0] == prices_copy.length - 1
prices_copy.pop
min_hash(prices_copy)
else
min_hash = { "min_price"=> min_price, "min_price_indices" => min_price_indices}
end
end
def max_hash(prices)
prices_copy = prices.dup
max_price = prices_copy.max
max_price_indices = prices_copy.each_index.select{|i| prices_copy[i] == max_price}
# Need to get max day that is not the first day
if max_price_indices.length == 1 && max_price_indices[0] == 0
prices_copy.shift
max_hash(prices_copy)
else
max_hash = { "max_price" => max_price, "max_price_indices" => max_price_indices}
end
end
def pair_days(prices, min_hash, max_hash)
optimal_days = []
other_days = []
optimal_diff = prices.length + 1
min_hash_arr = min_hash["min_price_indices"]
max_hash_arr = max_hash["max_price_indices"]
max_hash_arr_len = max_hash_arr.length
# pair up min/max days into optimal days (shortest time to profit)
# and other days where there are duplicate highs and lows
# otherwise there will be only one set of days where there is only
# one unique low and one unique high
min_hash_arr.each do |min_day|
found_sell_day = false
max_day_index = 0
while !found_sell_day && max_day_index < max_hash_arr.length do
max_day = max_hash_arr[max_day_index]
if max_day > min_day
day_diff = max_day - min_day
if day_diff < optimal_diff
optimal_diff = day_diff
if !optimal_days.empty?
other_days += optimal_days
optimal_days = []
end
optimal_days << min_day
optimal_days << max_day
else
other_days << min_day
other_days << max_day
end
found_sell_day = true
end
max_day_index += 1
end
end
pair_days_hash = {"optimal_days" => optimal_days, "other_days" => other_days}
end
def print_profit_days(prices, pair_days_hash)
optimal_days = pair_days_hash["optimal_days"]
other_days = pair_days_hash["other_days"]
if !optimal_days.empty? then
opt_buy_date = optimal_days[0]
opt_sell_date = optimal_days[1]
opt_buy_price = prices[opt_buy_date]
opt_sell_price = prices[opt_sell_date]
profit = opt_sell_price - opt_buy_price
puts "\n\nFor quickest profit buy on day #{opt_buy_date+1} at $#{opt_buy_price}/share and sell on day #{opt_sell_date+1} at $#{opt_sell_price}/share for a profit of $#{profit}"
if !other_days.empty?
puts "\nOther days to buy/sell"
index = 0
while index < other_days.length do
buy_date = other_days[index]
buy_price = prices[buy_date]
sell_date = other_days[index+1]
sell_price = prices[sell_date]
profit = sell_price - buy_price
puts "Buy on day #{buy_date+1} at $#{buy_price}/share and sell on day #{sell_date+1} at $#{sell_price}/share for a profit of $#{profit}"
index += 2
end
end
end
end
def stock_picker(prices)
min_hash = min_hash(prices)
max_hash = max_hash(prices)
if min_hash["min_price"] == max_hash["max_price"]
puts "The stock price does not rise in the given period."
else
pair_days_hash = pair_days(prices, min_hash, max_hash)
print_profit_days(prices, pair_days_hash)
end
end
def valid_stock_arr?(array)
# if any non-numeric characters except for ',' or a zero stock value then its not valid
!(array =~ /[^0-9 ,]/) && !(array =~ /[ ,]*0[^1-9]/) && !(array =~ /[,]0$/)
end
valid_input = false
while !valid_input do
puts "Enter a list of comma delimited stock prices:"
stock_prices = gets.chomp
valid_input = valid_stock_arr?(stock_prices)
puts "Please enter a comma delimited list of whole numbers.\n\n" if !valid_input
end
stock_prices = stock_prices.split(",").map(&:to_i)
stock_picker(stock_prices)