portfolio-maker 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []