portfolio_stocks 1.0.0

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