Workdesk to stack research work on Security and/or Organization items
[smr.git] / gui / app / models / security_fund.rb
blob57000ea077ca9fdb719f0bb2a73c9a33a5ddc5c0
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 SecurityFund < ActiveRecord::Base
22     include Smr::HelperMethods
23     include Smr::SecurityTypemodelMandatoryMethods
25     after_initialize :build_cf_stream
27     self.inheritance_column = :none
28         has_one :Security, :foreign_key=>:id_security_fund, :inverse_of=>:SecurityFund
30     ##
31     # types of funds supported (may alter behaviour of some methods)
32     # NOTE: do NOT change the relations! just ADD!
33     enum type: { :equity=>0, :bond=>1, :balanced=>2, :etf=>3, :fund_of_funds=>4,
34                  :hedge=>5, :real_estate=>6, :commodity=>7 }
36     # data validations
37     #validates :id_security, numericality: { greater_than: 0 }
39     ##
40     # Returns human readable String describing :type. It may be given as
41     # symbol, as string or as numerical index.
42     def SecurityFund.describe_type(lookup)
43         descriptions = {
44             :equity        => 'Equity Fund',
45             :bond          => 'Fixed Income Fund',
46             :balanced      => 'Equity/Fixed Income Fund',
47             :etf           => 'Indexing Fund',
48             :fund_of_funds => 'Fund of Funds',
49             :hedge         => 'Hedge Fund',
50             :real_estate   => 'Real Estate Fund',
51             :commodity     => 'Commodities Fund',
52         }
54         if lookup.is_a?(Numeric) then
55             lookup = self.types.key(lookup) || ''
56         end
57         descriptions[lookup.to_sym]
58     end
60     ##
61     # Describes type of the instance object.
62     def describe_type
63         SecurityFund.describe_type(type)
64     end
66     ##
67     # Returns humanreadable String describing the model itself.
68     def SecurityFund.describe
69         'Investment Fund'
70     end
71     def describe
72         SecurityFund.describe
73     end
74     ##
75     # Human readable String composed of essential properties known by a
76     # SecurityFund on its own. Useful as part to compose the name of a
77     # Security.
78     def to_s
79         [
80             self.Security.brief,
81             unless tranche.blank? then '(%s)' % tranche end,
82             SecurityFund.describe_type(type),
83             unless currency.blank? and currency != Smr::DEFAULT_CURRENCY then currency end,
84         ].join(' ')
85     end
87     ##
88     # Wrapper to convert integer date_* column to Time and vice versa.
89     # If :integer is given, the date_* field is updated first. The Time
90     # value is always returned.
91     def time_distribution=(string)
92         write_attribute(:date_distribution, string.to_time)
93     end
94     def time_distribution
95         Time.at read_attribute(:date_distribution)
96     end
98     def time_inception=(string)
99         write_attribute(:date_inception, string.to_time)
100     end
101     def time_inception
102         Time.at read_attribute(:date_inception)
103     end
105     def time_fiscalyearend=(string)
106         write_attribute(:date_fiscalyearend, string.to_time)
107     end
108     def time_fiscalyearend
109         Time.at read_attribute(:date_fiscalyearend)
110     end
112     ##
113     # mandatory method: result based on :distribution declared, :distribution_interval
114     # and :quote.
115     def current_yield(quote=false)
116         quote = self.Security.last_quote unless quote
117         raise ':quote must be of Quote' if quote and not quote.is_a?(Quote)
118         if quote.quote.zero? then return false end
119         BigDecimal( (
120             distribution * 12 / distribution_interval
121         ).to_s ).as_percentage_of(quote.quote)
122     end
124     ##
125     # mandatory method: funds never expires but we expire it
126     # artificially if the last available quotation is older than 3
127     # years. New Quote data will automatically unexpire it.
128     def is_expired?(date=Time.now)
129         quote = self.Security.last_quote(date)
130         quote.time < date - 3.years
131     end
133     ##
134     # mandatory method: constructs :distribution payments forecast
135     #
136     # Its based on :time_distribution and :distribution_interval.
137     # Since a fund never expires, the steam of cashflow is limited to
138     # 10 years from :start_date.
139     #
140     # The :type option allows to filter what is returned. This model supports
141     # :none and :dividend_booking. Also see PositionRevision#types to have all
142     # types of cashflow known to SMR.
143     #
144     # :start_date is Time.now unless given.
145     def cashflow(options={:start_date=>false, :end_date=>false, :amount=>false, :type=>:none, :item_link=>false})
146         start_date = options[:start_date] || Time.now
147         end_date = options[:end_date] || (start_date + 10.years).end_of_year
148         shares = options[:amount] || 1
150         @cf_stream.get(
151             shares,
152             :start=>start_date, :end=>end_date,
153             :filter=>(options[:type] || :none),
154             :item_link=>options[:item_link]
155         )
156     end
158     ##
159     # mandatory method: inspects :distribution_interval for happening multiple times a year
160     def has_subannual_payments?
161         distribution_interval < 12
162     end
164  protected
166     ##
167     # create Smr::CashflowStream from SecurityFund attributes
168     def build_cf_stream
169         @cf_stream = Smr::CashflowStream.new(
170             distribution, time_distribution,
171             :payment_interval=>distribution_interval,
172             :item_description=>self.Security.to_s,
173             :id_for_caching=>('%s%i' % [self.class, (id || 0)]).to_sym
174         )
175     end