manage bookmarks, bugfixes
[smr.git] / gui / app / models / security_stock.rb
bloba0db1b15daa262dd97f7b6f0fd27365fcb05442e
2 # This file is part of SMR.
4 # SMR is free software: you can redistribute it and/or modify it under the
5 # terms of the GNU General Public License as published by the Free Software
6 # Foundation, either version 3 of the License, or (at your option) any later
7 # version.
9 # SMR is distributed in the hope that it will be useful, but WITHOUT ANY
10 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License along with
14 # SMR.  If not, see <http://www.gnu.org/licenses/>.
16 require 'percentage'
17 require 'finance'
18 require 'cashflowitem'
19 require 'cashflowstream'
21 class SecurityStock < ActiveRecord::Base
22     include Smr::Extensions::HelperMethods
23     include Smr::Extensions::SecurityTypemodelMandatoryMethods
24     include Smr::Extensions::DateTimeWrapper
26     after_initialize :build_cf_stream
28     self.inheritance_column = :none
29         has_one :Security, :foreign_key=>:id_security_stock, :inverse_of=>:SecurityStock
31     ##
32     # types of stock supported (may alter behaviour of some methods)
33     # see http://api.rubyonrails.org/v4.1.0/classes/ActiveRecord/Enum.html
34     # NOTE: do NOT change the relations! just ADD!
35     enum type: { :common=>0, :preferred=>1, :bearer=>2, :registered=>3, :vinculum=>4, :ADR=>5, }
37     # data validations
38     #validates :id_security, numericality: { greater_than: 0 }
40     ##
41     # Returns human readable String describing :type. It may be given as
42     # symbol, as string or as numerical index.
43     def SecurityStock.describe_type(lookup)
44         descriptions = {
45             :common     => 'Common Stock',
46             :preferred  => 'Preferred Stock',
47             :bearer     => 'Bearer Share',
48             :registered => 'Registered Share',
49             :vinculum   => 'Registered Share with restricted transferability',
50             :ADR        => 'American Depositary Receipt',
51         }
53         if lookup.is_a?(Numeric) then
54             lookup = self.types.key(lookup) || ''
55         end
56         descriptions[lookup.to_sym]
57     end
59     ##
60     # Describes type of the instance object.
61     def describe_type
62         SecurityStock.describe_type(type)
63     end
65     ##
66     # Returns humanreadable String describing the model itself.
67     def SecurityStock.describe
68         'Equity Ownership'
69     end
70     def describe
71         SecurityStock.describe
72     end
74     ##
75     # Returns a collection of all types in their described form.
76     #
77     # Useful for Select boxes in forms, also see SecurityStock#describe_type. The
78     # :as_number option toggles whether symbols or integers are retured as
79     # index.
80     def self.types_for_form(options={ :as_number=>false })
81         SecurityStock.types.map { |t|
82             [
83                 SecurityStock.describe_type(t.first),
84                 options[:as_number] ? SecurityStock.types[t.first] : t.first
85             ]
86         }
87     end
89     ##
90     # Human readable String composed of essential properties known by a
91     # SecurityStock on its own. Useful as part to compose the name of a
92     # Security.
93     def to_s
94         SecurityStock.describe_type(type)
95     end
97     ##
98     # mandatory method: result based on :dividend declared, :dividend_interval
99     # and :quote.
100     def current_yield(quote=false)
101         quote = self.Security.last_quote unless quote
102         raise ':quote must be of Quote' if quote and not quote.is_a?(Quote)
103         if quote.last.zero? then return false end
104         BigDecimal( (
105             dividend * 12 / dividend_interval
106         ).to_s ).as_percentage_of(quote.last)
107     end
109     ##
110     # mandatory method: equity never expires but we expire it
111     # artificially if the last available quotation is older than 3
112     # years. New Quote data will automatically unexpire it.
113     def is_expired?(date=Time.now)
114         quote = self.Security.last_quote(date)
115         quote.time_last < date - 3.years
116     end
118     ##
119     # mandatory method: constructs :dividend payments
120     #
121     # Its based on :dividend_exdate and :dividend_interval. Since equity never
122     # expires, the steam of cashflow is limited to 10 years from :start_date.
123     #
124     # The :type option allows to filter what is returned. This model supports
125     # :none and :dividend_booking. Also see PositionRevision#types to have all
126     # types of cashflow known to SMR.
127     #
128     # :start_date is Time.now unless given.
129     def cashflow(options={:start_date=>false, :end_date=>false, :amount=>false, :type=>:none, :item_link=>false})
130         start_date = options[:start_date] || Time.now
131         end_date = options[:end_date] || (start_date + 10.years).end_of_year
132         shares = options[:amount] || 1
134         @cf_stream.get(
135             shares,
136             :start=>start_date, :end=>end_date,
137             :filter=>(options[:type] || :none),
138             :item_link=>options[:item_link]
139         )
140     end
142     ##
143     # mandatory method: inspects :dividend_interval for happening multiple times a year
144     def has_subannual_payments?
145         dividend_interval < 12
146     end
148  protected
150     ##
151     # create Smr::CashflowStream from SecurityStock attributes
152     def build_cf_stream
153         @cf_stream = Smr::CashflowStream.new(
154             dividend, time_dividend_exdate,
155             :payment_interval=>dividend_interval,
156             :item_description=>self.Security.to_s,
157             :id_for_caching=>('%s%i' % [self.class, (id || 0)]).to_sym
158         )
159     end