portfolio-maker 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/config/environment.rb +10 -0
- data/lib/portfolio_maker.rb +15 -0
- data/lib/portfolio_maker/command_line_interface.rb +196 -0
- data/lib/portfolio_maker/portfolio.rb +83 -0
- data/lib/portfolio_maker/scraper.rb +60 -0
- data/lib/portfolio_maker/stock.rb +118 -0
- metadata +122 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d3329b493b1cb3aa4bb30c974ec26c5864df25a24c19111650fea50ba5c45665
|
4
|
+
data.tar.gz: fda677c74e000dad5226cbd718e610eab6aff8db03d0dad71887f22a0d4e65aa
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5fcf3da5253920b730101ee5a4b3980174c056867da01518aa82279c55775bb9ab6f5506be80397c2985087f919d04af882c583146efa4b6dccd102c833d963e
|
7
|
+
data.tar.gz: 54401d1175591fb54cc9c9e397ce3c79ea276bc2ee6167206b820ebef895b75c77484c7348a53a4e39ed9d8cb0dfc4e129f3feecdb0602093ccd56d6e23dfc49
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'pry'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'open-uri'
|
4
|
+
require 'colorize'
|
5
|
+
|
6
|
+
require_relative '../lib/portfolio_maker/scraper'
|
7
|
+
require_relative '../lib/portfolio_maker/portfolio'
|
8
|
+
require_relative '../lib/portfolio_maker/command_line_interface'
|
9
|
+
require_relative '../lib/portfolio_maker/stock'
|
10
|
+
require_relative '../lib/portfolio_maker/version'
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module PortfolioMaker
|
2
|
+
end
|
3
|
+
|
4
|
+
require 'pry'
|
5
|
+
require 'nokogiri'
|
6
|
+
require 'open-uri'
|
7
|
+
require 'colorize'
|
8
|
+
|
9
|
+
require_relative '../lib/portfolio_maker/scraper'
|
10
|
+
require_relative '../lib/portfolio_maker/portfolio'
|
11
|
+
require_relative '../lib/portfolio_maker/command_line_interface'
|
12
|
+
require_relative '../lib/portfolio_maker/stock'
|
13
|
+
require_relative '../lib/portfolio_maker/version'
|
14
|
+
|
15
|
+
|
@@ -0,0 +1,196 @@
|
|
1
|
+
class PortfolioMaker::CommandLineInterface
|
2
|
+
|
3
|
+
attr_accessor :user_portfolio
|
4
|
+
|
5
|
+
def run
|
6
|
+
capital = 0
|
7
|
+
loop do
|
8
|
+
puts "How much would you like to deposit into your account?"
|
9
|
+
capital = gets.chomp
|
10
|
+
break if ((capital.to_i > 0) && capital.scan(/\D/).empty?)
|
11
|
+
puts "Please enter correct value"
|
12
|
+
end
|
13
|
+
@user_portfolio = Portfolio.new(capital.to_i)
|
14
|
+
main_menu
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
def user_portfolio
|
19
|
+
|
20
|
+
@user_portfolio
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
def main_menu
|
26
|
+
|
27
|
+
main_menu_loop_bool = true
|
28
|
+
loop do
|
29
|
+
puts "Select one of the following options by entering one of the below option number in your ternminal:"
|
30
|
+
puts "1. Manually enter stock ticker"
|
31
|
+
puts "2. Top 10 most active stocks"
|
32
|
+
puts "3. My portfolio"
|
33
|
+
puts "4. Quit"
|
34
|
+
|
35
|
+
input = menu_input(4)
|
36
|
+
|
37
|
+
case input
|
38
|
+
when 1
|
39
|
+
stock_find
|
40
|
+
when 2
|
41
|
+
most_active
|
42
|
+
when 3
|
43
|
+
portfolio_menu
|
44
|
+
when 4
|
45
|
+
main_menu_loop_bool = false
|
46
|
+
end
|
47
|
+
break if !(main_menu_loop_bool)
|
48
|
+
end
|
49
|
+
abort("Thank you for using this portfolio interface!")
|
50
|
+
end
|
51
|
+
|
52
|
+
def stock_find
|
53
|
+
|
54
|
+
stock_info = {}
|
55
|
+
#binding.pry
|
56
|
+
loop do
|
57
|
+
puts "Please enter stock ticker"
|
58
|
+
|
59
|
+
ticker = gets.chomp
|
60
|
+
|
61
|
+
if (!Stock.find_stock_with_ticker(ticker))
|
62
|
+
stock_info = Scraper.scrape_stock_page("https://finance.yahoo.com/quote/" + ticker)
|
63
|
+
else
|
64
|
+
stock_info = Stock.find_stock_with_ticker(ticker).make_hash_from_stock
|
65
|
+
end
|
66
|
+
break if (stock_info != nil)
|
67
|
+
puts "Please enter a proper stock ticker (No ETFs, Crypto, Indexes etc.)"
|
68
|
+
end
|
69
|
+
|
70
|
+
puts "Name: #{stock_info[:name]}, Price: $#{stock_info[:price]}, market cap: $#{stock_info[:market_cap]}"
|
71
|
+
puts "Select one of the following options by entering one of the below option number in your ternminal:"
|
72
|
+
puts "1. Add stock to portfolio or buy more"
|
73
|
+
puts "2. Return to main menu"
|
74
|
+
|
75
|
+
input = menu_input(2)
|
76
|
+
#binding.pry
|
77
|
+
case input
|
78
|
+
when 1
|
79
|
+
amount = 0
|
80
|
+
loop do
|
81
|
+
puts "Select what amount would you like to buy?\n current cash = $#{user_portfolio.cash}"
|
82
|
+
amount = gets.chomp
|
83
|
+
|
84
|
+
break if (!(amount.to_i > user_portfolio.cash || amount.to_i <= 0) && amount.scan(/\D/).empty?)
|
85
|
+
|
86
|
+
puts "Please enter valid amount within availible cash"
|
87
|
+
|
88
|
+
end
|
89
|
+
#binding.pry
|
90
|
+
user_portfolio.create_or_buy_more(stock_info, amount.to_i)
|
91
|
+
#binding.pry
|
92
|
+
puts "Bought #{amount.to_i / stock_info[:price].gsub(/[\s,]/ ,"").to_f} shares of #{stock_info[:name]}"
|
93
|
+
when 2
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def most_active
|
99
|
+
|
100
|
+
puts "Printing top 10 trending stocks"
|
101
|
+
|
102
|
+
Scraper.scrap_trending_stocks_page
|
103
|
+
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
def portfolio_menu
|
108
|
+
|
109
|
+
portfolio_loop_bool = true
|
110
|
+
loop do
|
111
|
+
puts "Manage your portfolio by entering one of the below option number in your ternminal:"
|
112
|
+
puts "1. View my portfolio"
|
113
|
+
puts "2. Sell stock"
|
114
|
+
puts "3. Add funds"
|
115
|
+
puts "4. Withdraw funds"
|
116
|
+
puts "5. Return to main menu"
|
117
|
+
|
118
|
+
input = menu_input(5)
|
119
|
+
|
120
|
+
case input
|
121
|
+
when 1
|
122
|
+
user_portfolio.display
|
123
|
+
when 2
|
124
|
+
if(user_portfolio.stocks.empty?)
|
125
|
+
puts "Porfolio is empty"
|
126
|
+
else
|
127
|
+
ticker = ""
|
128
|
+
amount = 0
|
129
|
+
loop do
|
130
|
+
break if (Stock.find_stock_with_ticker(ticker))
|
131
|
+
puts "Enter ticker for stock you want to sell or enter ""cancel"" to return to portfolio menu"
|
132
|
+
puts "Tickers:"
|
133
|
+
Stock.display_tickers
|
134
|
+
ticker = gets.chomp
|
135
|
+
|
136
|
+
break if ((Stock.find_stock_with_ticker(ticker)) || ticker == "cancel")
|
137
|
+
|
138
|
+
puts "Please enter a valid ticker"
|
139
|
+
|
140
|
+
end
|
141
|
+
if !(ticker == "cancel")
|
142
|
+
loop do
|
143
|
+
#binding.pry
|
144
|
+
puts "How much would you like to sell of $#{Stock.find_stock_with_ticker(ticker).update_stock_price} of the stock or enter ""cancel"" to return to portfolio menu"
|
145
|
+
amount = gets.chomp
|
146
|
+
|
147
|
+
break if ((!(amount.to_i <= 0 || amount.to_i > Stock.find_stock_with_ticker(ticker).equity.to_f) && amount.scan(/\D/).empty?) || amount == "cancel")
|
148
|
+
|
149
|
+
puts "Please input correct value"
|
150
|
+
end
|
151
|
+
user_portfolio.sell_stock(ticker, amount.to_i) if !(amount == "cancel" )
|
152
|
+
puts "Sold $#{amount} of #{ticker}" if !(amount == "cancel")
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
when 3
|
157
|
+
amount = ""
|
158
|
+
loop do
|
159
|
+
puts "How much would you like to deposit or enter ""cancel"" to return to portfolio menu"
|
160
|
+
amount = gets.chomp
|
161
|
+
|
162
|
+
break if ((!(amount.to_i <= 0) && amount.scan(/\D/).empty?) || amount == "cancel")
|
163
|
+
|
164
|
+
puts "Please input correct value"
|
165
|
+
end
|
166
|
+
user_portfolio.deposit(amount.to_i) if !(amount == "cancel")
|
167
|
+
when 4
|
168
|
+
loop do
|
169
|
+
puts "How much would you like to withdraw or enter ""cancel"" to return to portfolio menu \n current cash = $#{user_portfolio.cash}"
|
170
|
+
amount = gets.chomp
|
171
|
+
|
172
|
+
break if ((!(amount.to_i <= 0 || amount.to_i > user_portfolio.cash) && amount.scan(/\D/).empty?) || amount == "cancel")
|
173
|
+
|
174
|
+
puts "Please input correct value"
|
175
|
+
end
|
176
|
+
user_portfolio.withdraw(amount.to_i) if !(amount == "cancel")
|
177
|
+
when 5
|
178
|
+
portfolio_loop_bool = false
|
179
|
+
end
|
180
|
+
break if !(portfolio_loop_bool)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
|
185
|
+
def menu_input(max)
|
186
|
+
input = ""
|
187
|
+
loop do
|
188
|
+
input = gets.chomp
|
189
|
+
break if (input.to_i.between?(1,max) && input.scan(/\D/).empty?)
|
190
|
+
puts "Please enter an integer number between than 1 and #{max}:"
|
191
|
+
end
|
192
|
+
|
193
|
+
return input.to_i
|
194
|
+
|
195
|
+
end
|
196
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
class Portfolio
|
2
|
+
|
3
|
+
attr_accessor :capital, :cash, :invested
|
4
|
+
|
5
|
+
def initialize(capital)
|
6
|
+
|
7
|
+
@capital = capital
|
8
|
+
@cash = capital
|
9
|
+
invested
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
def stocks
|
14
|
+
|
15
|
+
Stock.all
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
def update
|
20
|
+
|
21
|
+
if !(stocks.empty?)
|
22
|
+
value_change = Stock.update_stocks
|
23
|
+
@invested += value_change
|
24
|
+
@capital += value_change
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
def invested
|
31
|
+
|
32
|
+
@invested = 0
|
33
|
+
stocks.each{|stock| @invested+=stock.equity}
|
34
|
+
@invested
|
35
|
+
end
|
36
|
+
|
37
|
+
def display
|
38
|
+
|
39
|
+
self.update
|
40
|
+
puts "Amount invested = $#{self.invested}"
|
41
|
+
puts "Cash availible = $#{self.cash}"
|
42
|
+
if (!stocks.empty?)
|
43
|
+
|
44
|
+
stocks.each{|stock|stock.display}
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
end
|
50
|
+
def create_or_buy_more(stock_hash, amount)
|
51
|
+
|
52
|
+
@cash -= amount
|
53
|
+
@invested += amount
|
54
|
+
Stock.create_or_buy_more(stock_hash, amount)
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
def sell_stock(ticker, amount)
|
59
|
+
|
60
|
+
|
61
|
+
Stock.sell(ticker, amount)
|
62
|
+
@invested -= amount
|
63
|
+
@cash += amount
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
def deposit(amount)
|
68
|
+
|
69
|
+
@cash += amount
|
70
|
+
@capital += amount
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
def withdraw(amount)
|
75
|
+
|
76
|
+
@cash -= amount
|
77
|
+
@capital -= amount
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'pry'
|
3
|
+
|
4
|
+
class Scraper
|
5
|
+
|
6
|
+
def self.scrap_trending_stocks_page
|
7
|
+
|
8
|
+
stock_hash_arr = []
|
9
|
+
|
10
|
+
doc = Nokogiri::HTML(open("https://www.investing.com/equities/trending-stocks"))
|
11
|
+
|
12
|
+
|
13
|
+
|
14
|
+
top_stocks = doc.css('table tbody tr td a')
|
15
|
+
|
16
|
+
i = 0
|
17
|
+
|
18
|
+
10.times do |index|
|
19
|
+
|
20
|
+
stock_url = "https://www.investing.com" + top_stocks[i].attribute("href").value
|
21
|
+
stock_ticker = Nokogiri::HTML(open(stock_url)).css(".instrumentHeader h2").text.split(" ")[0]
|
22
|
+
|
23
|
+
#puts {:ticker => stock_ticker, :name => top_stocks[i].text}
|
24
|
+
puts "#{index + 1}. #{stock_ticker} #{top_stocks[i].text}"
|
25
|
+
|
26
|
+
i+=1
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
#binding.pry
|
31
|
+
stock_hash_arr
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.scrape_stock_page(stock_url)
|
36
|
+
|
37
|
+
begin
|
38
|
+
doc = Nokogiri::HTML(open(stock_url))
|
39
|
+
|
40
|
+
rescue
|
41
|
+
return nil
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
#binding.pry
|
46
|
+
stock = {}
|
47
|
+
|
48
|
+
stock_name = doc.css("#quote-header-info div div div")[0].text.split(" ")
|
49
|
+
stock[:ticker] = stock_name.pop.split(/[()]/)[1]
|
50
|
+
stock[:name] = stock_name.join(" ")
|
51
|
+
stock[:price] = doc.css("#quote-header-info div div div span")[3].text
|
52
|
+
stock[:market_cap] = doc.css("#quote-summary div span")[13].text
|
53
|
+
|
54
|
+
stock
|
55
|
+
#binding.pry
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
@@ -0,0 +1,118 @@
|
|
1
|
+
class Stock
|
2
|
+
|
3
|
+
attr_accessor :name, :ticker, :price, :quantity, :equity, :market_cap
|
4
|
+
|
5
|
+
@@all = []
|
6
|
+
|
7
|
+
def initialize(stock_hash)
|
8
|
+
|
9
|
+
stock_hash.each {|key, value| self.send(("#{key}="), value)}
|
10
|
+
@quantity = self.update_quantity
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.sell(ticker, amount)
|
15
|
+
|
16
|
+
self.find_stock_with_ticker(ticker).equity -= amount
|
17
|
+
if (self.find_stock_with_ticker(ticker).equity == 0)
|
18
|
+
@@all.delete(self.find_stock_with_ticker(ticker))
|
19
|
+
else
|
20
|
+
self.find_stock_with_ticker(ticker).update_quantity
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
def make_hash_from_stock
|
26
|
+
|
27
|
+
stock_info = {}
|
28
|
+
|
29
|
+
stock_info[:name] = self.name
|
30
|
+
stock_info[:price] = self.price
|
31
|
+
stock_info[:market_cap] = self.market_cap
|
32
|
+
|
33
|
+
stock_info
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
def update_quantity
|
38
|
+
|
39
|
+
@quantity = @equity/(self.price.to_f)
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
def curr_equity
|
44
|
+
|
45
|
+
@equity = @quantity * (self.price.to_f)
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
def update_stock_price
|
50
|
+
|
51
|
+
self.price = Scraper.scrape_stock_page("https://finance.yahoo.com/quote/" + self.ticker)[:price]
|
52
|
+
self.curr_equity
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
def price=(price)
|
57
|
+
|
58
|
+
@price = price
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
def self.find_stock_with_ticker(ticker)
|
64
|
+
|
65
|
+
stock = @@all.select{|stock|stock.ticker == ticker}
|
66
|
+
|
67
|
+
stock == [] ? nil : stock[0]
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
def price
|
72
|
+
|
73
|
+
@price.gsub(/[\s,]/ ,"").to_f
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
def display
|
78
|
+
|
79
|
+
puts "#{@name} (#{@ticker}): stocks: #{@quantity} shares, equity: $#{self.curr_equity}"
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.display_tickers
|
84
|
+
|
85
|
+
@@all.each {|stock| print "|#{stock.ticker}"}
|
86
|
+
puts "|"
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.update_stocks
|
90
|
+
|
91
|
+
#binding.pry
|
92
|
+
price_update = 0
|
93
|
+
@@all.each do |stock|
|
94
|
+
curr_price = stock.price
|
95
|
+
stock.update_stock_price
|
96
|
+
price_update = stock.price - price_update
|
97
|
+
end
|
98
|
+
price_update
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.create_or_buy_more(stock_hash, amount)
|
102
|
+
|
103
|
+
if (!self.find_stock_with_ticker(stock_hash[:ticker]))
|
104
|
+
stock_hash[:equity] = amount
|
105
|
+
new_stock = self.new(stock_hash)
|
106
|
+
@@all << new_stock
|
107
|
+
else
|
108
|
+
updating_stock = self.find_stock_with_ticker(stock_hash[:ticker])
|
109
|
+
updating_stock.update_stock_price
|
110
|
+
updating_stock.quantity += (amount/updating_stock.price)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.all
|
115
|
+
@@all
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
metadata
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: portfolio-maker
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- "'Sumer Gaikwad'"
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-04-30 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: '2.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
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: pry
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '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: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.2'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.2'
|
83
|
+
description: portfolio simulator
|
84
|
+
email:
|
85
|
+
- "'sg.comparch@gmail.com'"
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- config/environment.rb
|
91
|
+
- lib/portfolio_maker.rb
|
92
|
+
- lib/portfolio_maker/command_line_interface.rb
|
93
|
+
- lib/portfolio_maker/portfolio.rb
|
94
|
+
- lib/portfolio_maker/scraper.rb
|
95
|
+
- lib/portfolio_maker/stock.rb
|
96
|
+
homepage: http://rubygems.org
|
97
|
+
licenses:
|
98
|
+
- MIT
|
99
|
+
metadata:
|
100
|
+
allowed_push_host: https://rubygems.org
|
101
|
+
homepage_uri: http://rubygems.org
|
102
|
+
source_code_uri: https://github.com/RSigmaSG/portfolio_maker
|
103
|
+
post_install_message:
|
104
|
+
rdoc_options: []
|
105
|
+
require_paths:
|
106
|
+
- lib
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: 2.3.0
|
112
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
requirements: []
|
118
|
+
rubygems_version: 3.0.8
|
119
|
+
signing_key:
|
120
|
+
specification_version: 4
|
121
|
+
summary: portfolio simulator
|
122
|
+
test_files: []
|