portfolio_stocks 1.0.0

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 80710f31ff25a31a585ce43341f8c465a763db14
4
+ data.tar.gz: 8d5fd582950fa0c00ae284c55d1f68d4ca4c861a
5
+ SHA512:
6
+ metadata.gz: 3193ff31d2a3d18af814e536e3dc91d5006eb72510ca1e10a0d19d691575944d7699cb9a02890a76d421126cb406270eed45e96cf3f98c296cfef8e09f4d33f9
7
+ data.tar.gz: 37e6e2e4af8fc6b087c84c955f964f6cfe0829ceb37296d2ffac48dd55ab5b535b0ad34c1062b5ac2f499d4af98f984deb46eaf94572ec41da1a84fe845e44c9
@@ -0,0 +1,25 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /test/tmp/
9
+ /test/version_tmp/
10
+ /tmp/
11
+
12
+ ## Specific to RubyMotion:
13
+ .dat*
14
+ .repl_history
15
+ build/
16
+
17
+ ## Documentation cache and generated files:
18
+ /.yardoc/
19
+ /_yardoc/
20
+ /doc/
21
+ /rdoc/
22
+
23
+ ## Environment normalisation:
24
+ /.bundle/
25
+ /lib/bundler/man/
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
@@ -0,0 +1,39 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ portfolio_stocks (1.0.0)
5
+ colored (~> 1.2)
6
+ stock_quote
7
+ terminal-table
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ colored (1.2)
13
+ diff-lcs (1.2.5)
14
+ json (1.8.1)
15
+ mime-types (2.3)
16
+ rake (10.3.2)
17
+ rest-client (1.6.7)
18
+ mime-types (>= 1.16)
19
+ rspec (2.99.0)
20
+ rspec-core (~> 2.99.0)
21
+ rspec-expectations (~> 2.99.0)
22
+ rspec-mocks (~> 2.99.0)
23
+ rspec-core (2.99.0)
24
+ rspec-expectations (2.99.0)
25
+ diff-lcs (>= 1.1.3, < 2.0)
26
+ rspec-mocks (2.99.0)
27
+ stock_quote (1.1.8)
28
+ json
29
+ rest-client (~> 1.6)
30
+ terminal-table (1.4.5)
31
+
32
+ PLATFORMS
33
+ ruby
34
+
35
+ DEPENDENCIES
36
+ bundler
37
+ portfolio_stocks!
38
+ rake (~> 10.3)
39
+ rspec (~> 2.14)
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Tim Davies
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,32 @@
1
+ ## Portfolio 💰
2
+
3
+ A command-line tool for managing your stocks.
4
+
5
+ ### Installation
6
+
7
+ gem install portfolio_stocks
8
+
9
+ ### Usage
10
+
11
+ Start watching a stock:
12
+
13
+ --watch <stock>
14
+
15
+ Stop watching a stock:
16
+
17
+ --ignore <stock>
18
+
19
+ Add investment to portfolio:
20
+
21
+ --invest <stock> <amount bought> <price paid per stock> <total commission paid>
22
+
23
+ Remove investment from portfolio:
24
+
25
+ --sell <stock>
26
+
27
+ ### Small disclaimer
28
+
29
+ I'm using this to monitor a few stocks and keep track of a few investments -
30
+ nothing heavy by any means. It has none of the features that a more seasoned
31
+ investor would require but if you just want to keep track of a bit of money,
32
+ hopefully you'll find it useful.
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'portfolio')
4
+
5
+ # Get the subcommand (and if there isn't one, default to 'show', which displays
6
+ # the portfolio):
7
+ if ARGV.length != 0
8
+ command = ARGV[0].gsub('--', '')
9
+ else
10
+ command = "show"
11
+ end
12
+
13
+ # Initialise commands object which stores the methods for each of the
14
+ # subcommands:
15
+ commands = Portfolio::Commands.new
16
+
17
+ # Check to see if the subcommand is defined:
18
+ if commands.methods.include? command.to_sym
19
+ # Execute command:
20
+ commands.send command
21
+ else
22
+ # Display usage information:
23
+ commands.help
24
+ end
@@ -0,0 +1,10 @@
1
+ require "colored"
2
+
3
+ require "portfolio/version"
4
+ require "portfolio/store"
5
+ require "portfolio/formatter"
6
+ require "portfolio/stockprices"
7
+ require "portfolio/commands"
8
+
9
+ module Portfolio
10
+ end
@@ -0,0 +1,113 @@
1
+ module Portfolio
2
+ class Commands
3
+ # Show watched stocks and user portfolio
4
+ def show
5
+ # Get list of watched stocks:
6
+ watched_stocks = Store.instance.watched_stocks
7
+
8
+ # Get stock prices:
9
+ watched_stocks.map! do |stock|
10
+ {
11
+ symbol: stock,
12
+ price: StockPrices.instance.get_price_formatted(stock),
13
+ }
14
+ end
15
+
16
+ # Get portfolio:
17
+ portfolio = Formatter.instance.format_portfolio(Store.instance.portfolio)
18
+
19
+ # Output watched stock table:
20
+ Formatter.instance.output_table("Watched Stocks", [:symbol, :price], watched_stocks)
21
+
22
+ # Output portfolio:
23
+ Formatter.instance.output_table("Portfolio",
24
+ [:symbol, :current_price, :number_owned, :price_paid, :commission, :profit],
25
+ portfolio,
26
+ ['Symbol', 'Current Price', 'Number Owned', 'Price Paid', 'Commission', 'Profit'],
27
+ )
28
+ end
29
+
30
+ # Start watching a stock
31
+ def watch
32
+ if ARGV.length != 2
33
+ return help()
34
+ end
35
+
36
+ watched_stocks = Store.instance.watched_stocks
37
+ watched_stocks << ARGV[1].upcase
38
+
39
+ Store.instance.watched_stocks = watched_stocks
40
+ end
41
+
42
+ # Stop watching a stock
43
+ def ignore
44
+ if ARGV.length != 2
45
+ return help()
46
+ end
47
+
48
+ stock_to_remove = ARGV[1].upcase
49
+ watched_stocks = Store.instance.watched_stocks
50
+ watched_stocks.delete stock_to_remove
51
+
52
+ Store.instance.watched_stocks = watched_stocks
53
+ end
54
+
55
+ def invest
56
+ if ARGV.length != 5
57
+ return help()
58
+ end
59
+
60
+ # Get parameters:
61
+ stock = ARGV[1]
62
+ amount_bought = ARGV[2]
63
+ price_paid = ARGV[3]
64
+ commission_paid = ARGV[4]
65
+
66
+ # Store new investment:
67
+ portfolio = Store.instance.portfolio
68
+ portfolio << {
69
+ stock: stock,
70
+ amount_bought: amount_bought,
71
+ price_paid: price_paid,
72
+ commission_paid: commission_paid,
73
+ }
74
+ Store.instance.portfolio = portfolio
75
+
76
+ # Tell the user that the investment has been logged and wish the user
77
+ # good luck.
78
+ puts "Investment in #{stock} logged - good luck!"
79
+ end
80
+
81
+ # Remove stocks from portfolio
82
+ #
83
+ # FEATURE: Would be nice to be able to have multiple stocks from the same
84
+ # company, bought at different times, and be able to pick which one
85
+ # you're selling - rather than just automatically selling them all.
86
+ def sell
87
+ if ARGV.length != 2
88
+ return help()
89
+ end
90
+
91
+ stock_to_remove = ARGV[1].upcase
92
+ portfolio = Store.instance.portfolio
93
+ portfolio.reject! do |investment|
94
+ investment['stock'].upcase == stock_to_remove
95
+ end
96
+
97
+ Store.instance.portfolio = portfolio
98
+ end
99
+
100
+ # Show usage information
101
+ def help
102
+ puts "Portfolio, v#{Portfolio::VERSION}. Commands:\n".bold.underline
103
+ puts "Start watching a stock:".blue
104
+ puts " " + "--watch <stock>\n".underline
105
+ puts "Stop watching a stock:".blue
106
+ puts " " + "--ignore <stock>\n".underline
107
+ puts "Add investment to portfolio:".blue
108
+ puts " " + "--invest <stock> <amount bought> <price paid per stock> <total commission paid>\n".underline
109
+ puts "Remove investment from portfolio:".blue
110
+ puts " " + "--sell <stock>\n".underline
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,63 @@
1
+ require 'singleton'
2
+ require 'terminal-table'
3
+
4
+ module Portfolio
5
+ class Formatter
6
+ include Singleton
7
+
8
+ def output_table(title, columns, data, headings = nil)
9
+ # Format data correctly for table:
10
+ data.map! do |row|
11
+ new_row = []
12
+
13
+ columns.each do |col|
14
+ new_row << row[col]
15
+ end
16
+
17
+ new_row
18
+ end
19
+
20
+ # Create table with headings if some are passed in (or without if nil):
21
+ if headings.nil?
22
+ table = Terminal::Table.new(rows: data, title: title)
23
+ else
24
+ table = Terminal::Table.new(rows: data, title: title, headings: headings)
25
+ end
26
+
27
+ # Output table:
28
+ puts table
29
+ end
30
+
31
+ def format_portfolio(portfolio)
32
+ portfolio.map! do |investment|
33
+ # Work out price paid, value, current stock price and profit for
34
+ # this investment:
35
+ symbol = investment['stock']
36
+ current_price = StockPrices.instance.get_stock(symbol).ask
37
+ total_paid = (investment['amount_bought'].to_f * investment['price_paid'].to_f) + investment['commission_paid'].to_f
38
+ total_value = investment['amount_bought'].to_f * current_price
39
+ profit = (total_value - total_paid).round(2)
40
+
41
+ # If profit is negative, highlight as red:
42
+ if profit < 0.00
43
+ profit = profit.to_s.red
44
+ # If profit is positive, highlight as green:
45
+ elsif profit > 0.00
46
+ profit = profit.to_s.green
47
+ # Otherwise, don't highlight it - no profit or loss:
48
+ else
49
+ profit = profit.to_s
50
+ end
51
+
52
+ {
53
+ symbol: symbol,
54
+ current_price: current_price,
55
+ number_owned: investment['amount_bought'],
56
+ price_paid: investment['price_paid'],
57
+ commission: investment['commission_paid'],
58
+ profit: profit,
59
+ }
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,36 @@
1
+ require 'singleton'
2
+ require 'stock_quote'
3
+
4
+ module Portfolio
5
+ class StockPrices
6
+ include Singleton
7
+
8
+ def get_stock(symbol)
9
+ StockQuote::Stock.quote(symbol)
10
+ end
11
+
12
+ def get_price_formatted(symbol)
13
+ # Lookup stock price:
14
+ stock = get_stock(symbol)
15
+
16
+ # If the response code isn't 200, something went wrong:
17
+ if stock.response_code != 200
18
+ "ERROR".red
19
+ else
20
+ # Get the current stock price:
21
+ price = stock.ask.to_s
22
+
23
+ # If it's up, show it in green:
24
+ if stock.change > 0.0
25
+ price.green
26
+ # If it's down, show it in red:
27
+ elsif stock.change < 0.0
28
+ return price.red
29
+ # Otherwise, show it normally:
30
+ else
31
+ price
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,72 @@
1
+ require 'singleton'
2
+ require 'json'
3
+
4
+ module Portfolio
5
+ class Store
6
+ include Singleton
7
+
8
+ def watched_stocks
9
+ datastore = get_datastore
10
+ datastore['watched_stocks']
11
+ end
12
+
13
+ def watched_stocks=(watched_stocks)
14
+ data = get_datastore()
15
+ data['watched_stocks'] = watched_stocks
16
+ save_datastore(data)
17
+ end
18
+
19
+ def portfolio
20
+ datastore = get_datastore
21
+ datastore['portfolio']
22
+ end
23
+
24
+ def portfolio=(portfolio)
25
+ data = get_datastore()
26
+ data['portfolio'] = portfolio
27
+ save_datastore(data)
28
+ end
29
+
30
+ protected
31
+
32
+ def get_datastore
33
+ if @datastore
34
+ @datastore
35
+ else
36
+ begin
37
+ datastore_file_str = File.read(datastore_location)
38
+ @datastore = JSON.parse(datastore_file_str)
39
+ rescue
40
+ create_datastore
41
+ get_datastore
42
+ end
43
+ end
44
+ end
45
+
46
+ def save_datastore(data)
47
+ File.open(datastore_location, 'w') do |f|
48
+ f.write(data.to_json)
49
+ end
50
+ end
51
+
52
+ def datastore_location
53
+ File.expand_path('~/.portfolio/datastore.json')
54
+ end
55
+
56
+ def create_datastore
57
+ begin
58
+ Dir.mkdir File.expand_path('~/.portfolio')
59
+ rescue
60
+ end
61
+
62
+ File.open(datastore_location, 'w') do |f|
63
+ datastore_hash = {
64
+ watched_stocks: [],
65
+ portfolio: [],
66
+ }
67
+
68
+ f.write(datastore_hash.to_json)
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,3 @@
1
+ module Portfolio
2
+ VERSION = '1.0.0'
3
+ end
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'portfolio/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "portfolio_stocks"
8
+ spec.version = Portfolio::VERSION
9
+ spec.authors = ["Tim Davies"]
10
+ spec.email = ["mail@timdavi.es"]
11
+ spec.summary = %q{A simple command-line tool for managing your stock portfolio}
12
+ spec.description = <<-DESC
13
+ A simple command-line tool for managing your stock portfolio,
14
+ tracking your investments and watching stock prices.
15
+ DESC
16
+ spec.homepage = "https://github.com/timdavies/Portfolio"
17
+ spec.license = "MIT"
18
+
19
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f =~ /docs\// }
20
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
21
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_development_dependency "bundler"
25
+ spec.add_development_dependency "rake", "~> 10.3"
26
+ spec.add_development_dependency "rspec", "~> 2.14"
27
+
28
+ spec.add_dependency "colored", "~> 1.2"
29
+ spec.add_dependency "terminal-table"
30
+ spec.add_dependency "stock_quote"
31
+ end
metadata ADDED
@@ -0,0 +1,145 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: portfolio_stocks
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Tim Davies
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-06-10 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: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '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.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '10.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '2.14'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '2.14'
55
+ - !ruby/object:Gem::Dependency
56
+ name: colored
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.2'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '1.2'
69
+ - !ruby/object:Gem::Dependency
70
+ name: terminal-table
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: stock_quote
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: |2
98
+ A simple command-line tool for managing your stock portfolio,
99
+ tracking your investments and watching stock prices.
100
+ email:
101
+ - mail@timdavi.es
102
+ executables:
103
+ - portfolio
104
+ extensions: []
105
+ extra_rdoc_files: []
106
+ files:
107
+ - .gitignore
108
+ - Gemfile
109
+ - Gemfile.lock
110
+ - LICENSE.txt
111
+ - README.md
112
+ - Rakefile
113
+ - bin/portfolio
114
+ - lib/portfolio.rb
115
+ - lib/portfolio/commands.rb
116
+ - lib/portfolio/formatter.rb
117
+ - lib/portfolio/stockprices.rb
118
+ - lib/portfolio/store.rb
119
+ - lib/portfolio/version.rb
120
+ - portfolio.gemspec
121
+ homepage: https://github.com/timdavies/Portfolio
122
+ licenses:
123
+ - MIT
124
+ metadata: {}
125
+ post_install_message:
126
+ rdoc_options: []
127
+ require_paths:
128
+ - lib
129
+ required_ruby_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ required_rubygems_version: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ requirements: []
140
+ rubyforge_project:
141
+ rubygems_version: 2.1.10
142
+ signing_key:
143
+ specification_version: 4
144
+ summary: A simple command-line tool for managing your stock portfolio
145
+ test_files: []