portfolio-maker 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []