finmodeling 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.
- data/.gitignore +3 -0
- data/Gemfile +10 -0
- data/README.md +292 -0
- data/Rakefile +6 -0
- data/TODO.txt +36 -0
- data/examples/dump_report.rb +33 -0
- data/examples/lists/nasdaq-mid-to-mega-tech-symbols.txt +226 -0
- data/examples/show_report.rb +218 -0
- data/examples/show_reports.rb +77 -0
- data/finmodeling.gemspec +31 -0
- data/lib/finmodeling/annual_report_filing.rb +104 -0
- data/lib/finmodeling/array_with_stats.rb +22 -0
- data/lib/finmodeling/assets_calculation.rb +36 -0
- data/lib/finmodeling/assets_item.rb +14 -0
- data/lib/finmodeling/assets_item_vectors.rb +638 -0
- data/lib/finmodeling/balance_sheet_analyses.rb +33 -0
- data/lib/finmodeling/balance_sheet_calculation.rb +68 -0
- data/lib/finmodeling/calculation_summary.rb +148 -0
- data/lib/finmodeling/can_cache_classifications.rb +36 -0
- data/lib/finmodeling/can_cache_summaries.rb +16 -0
- data/lib/finmodeling/can_classify_rows.rb +54 -0
- data/lib/finmodeling/cash_change_calculation.rb +67 -0
- data/lib/finmodeling/cash_change_item.rb +14 -0
- data/lib/finmodeling/cash_change_item_vectors.rb +241 -0
- data/lib/finmodeling/cash_flow_statement_calculation.rb +85 -0
- data/lib/finmodeling/classifiers.rb +11 -0
- data/lib/finmodeling/company.rb +102 -0
- data/lib/finmodeling/company_filing.rb +64 -0
- data/lib/finmodeling/company_filing_calculation.rb +75 -0
- data/lib/finmodeling/company_filings.rb +100 -0
- data/lib/finmodeling/config.rb +37 -0
- data/lib/finmodeling/constant_forecasting_policy.rb +23 -0
- data/lib/finmodeling/factory.rb +27 -0
- data/lib/finmodeling/float_helpers.rb +17 -0
- data/lib/finmodeling/forecasts.rb +48 -0
- data/lib/finmodeling/generic_forecasting_policy.rb +19 -0
- data/lib/finmodeling/has_string_classifer.rb +96 -0
- data/lib/finmodeling/income_statement_analyses.rb +74 -0
- data/lib/finmodeling/income_statement_calculation.rb +71 -0
- data/lib/finmodeling/income_statement_item.rb +14 -0
- data/lib/finmodeling/income_statement_item_vectors.rb +654 -0
- data/lib/finmodeling/liabs_and_equity_calculation.rb +36 -0
- data/lib/finmodeling/liabs_and_equity_item.rb +14 -0
- data/lib/finmodeling/liabs_and_equity_item_vectors.rb +1936 -0
- data/lib/finmodeling/net_income_calculation.rb +41 -0
- data/lib/finmodeling/paths.rb +5 -0
- data/lib/finmodeling/period_array.rb +24 -0
- data/lib/finmodeling/quarterly_report_filing.rb +23 -0
- data/lib/finmodeling/rate.rb +20 -0
- data/lib/finmodeling/ratio.rb +20 -0
- data/lib/finmodeling/reformulated_balance_sheet.rb +176 -0
- data/lib/finmodeling/reformulated_cash_flow_statement.rb +140 -0
- data/lib/finmodeling/reformulated_income_statement.rb +436 -0
- data/lib/finmodeling/string_helpers.rb +26 -0
- data/lib/finmodeling/version.rb +3 -0
- data/lib/finmodeling.rb +70 -0
- data/spec/annual_report_filing_spec.rb +68 -0
- data/spec/assets_calculation_spec.rb +21 -0
- data/spec/assets_item_spec.rb +66 -0
- data/spec/balance_sheet_analyses_spec.rb +43 -0
- data/spec/balance_sheet_calculation_spec.rb +91 -0
- data/spec/calculation_summary_spec.rb +63 -0
- data/spec/can_classify_rows_spec.rb +86 -0
- data/spec/cash_change_calculation_spec.rb +56 -0
- data/spec/cash_change_item_spec.rb +66 -0
- data/spec/cash_flow_statement_calculation_spec.rb +108 -0
- data/spec/company_filing_calculation_spec.rb +74 -0
- data/spec/company_filing_spec.rb +30 -0
- data/spec/company_filings_spec.rb +55 -0
- data/spec/company_spec.rb +73 -0
- data/spec/constant_forecasting_policy_spec.rb +37 -0
- data/spec/factory_spec.rb +18 -0
- data/spec/forecasts_spec.rb +21 -0
- data/spec/generic_forecasting_policy_spec.rb +33 -0
- data/spec/income_statement_analyses_spec.rb +63 -0
- data/spec/income_statement_calculation_spec.rb +88 -0
- data/spec/income_statement_item_spec.rb +86 -0
- data/spec/liabs_and_equity_calculation_spec.rb +20 -0
- data/spec/liabs_and_equity_item_spec.rb +66 -0
- data/spec/mocks/calculation.rb +10 -0
- data/spec/mocks/income_statement_analyses.rb +93 -0
- data/spec/mocks/sec_query.rb +31 -0
- data/spec/net_income_calculation_spec.rb +23 -0
- data/spec/period_array.rb +52 -0
- data/spec/quarterly_report_filing_spec.rb +69 -0
- data/spec/rate_spec.rb +33 -0
- data/spec/ratio_spec.rb +33 -0
- data/spec/reformulated_balance_sheet_spec.rb +146 -0
- data/spec/reformulated_cash_flow_statement_spec.rb +174 -0
- data/spec/reformulated_income_statement_spec.rb +293 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/string_helpers_spec.rb +23 -0
- data/tools/create_balance_sheet_training_vectors.rb +65 -0
- data/tools/create_cash_change_training_vectors.rb +48 -0
- data/tools/create_credit_debit_training_vectors.rb +51 -0
- data/tools/create_income_statement_training_vectors.rb +48 -0
- metadata +289 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module FinModeling
|
|
2
|
+
class NetIncomeCalculation < CompanyFilingCalculation
|
|
3
|
+
include CanCacheClassifications
|
|
4
|
+
include CanCacheSummaries
|
|
5
|
+
include CanClassifyRows
|
|
6
|
+
|
|
7
|
+
BASE_FILENAME = File.join(FinModeling::BASE_PATH, "summaries/net_income_")
|
|
8
|
+
|
|
9
|
+
ALL_STATES = [ :or, :cogs, :oe, :oibt, :fibt, :tax, :ooiat, :fiat ]
|
|
10
|
+
NEXT_STATES = { nil => [ :or, :cogs, :oe, :oibt, :fibt, :tax, :ooiat, :fiat ],
|
|
11
|
+
:or => [ :or, :cogs, :oe, :oibt, :fibt, :tax, :ooiat, :fiat ],
|
|
12
|
+
:cogs => [ :cogs, :oe, :oibt, :fibt, :tax, :ooiat, :fiat ],
|
|
13
|
+
:oe => [ :oe, :oibt, :fibt, :tax, :ooiat, :fiat ],
|
|
14
|
+
:oibt => [ :oibt, :fibt, :tax, :ooiat, :fiat ], # obit/fibt can cycle back/forth
|
|
15
|
+
:fibt => [ :obit, :fibt, :tax, :ooiat, :fiat ], # obit/fibt can cycle back/forth
|
|
16
|
+
:tax => [ :ooiat, :fiat ], # tax can't go to itself. only 1 such item.
|
|
17
|
+
:ooiat => [ :ooiat, :fiat ], # ooiat/fiat can cycle back/forth
|
|
18
|
+
:fiat => [ :ooiat, :fiat ] }# ooiat/fiat can cycle back/forth
|
|
19
|
+
|
|
20
|
+
def summary(args)
|
|
21
|
+
summary_cache_key = args[:period].to_pretty_s
|
|
22
|
+
thesummary = lookup_cached_summary(summary_cache_key)
|
|
23
|
+
return thesummary if !thesummary.nil?
|
|
24
|
+
|
|
25
|
+
mapping = Xbrlware::ValueMapping.new
|
|
26
|
+
mapping.policy[:debit] = :flip
|
|
27
|
+
|
|
28
|
+
thesummary = super(:period => args[:period], :mapping => mapping) # FIXME: flip_total should == true!
|
|
29
|
+
if !lookup_cached_classifications(BASE_FILENAME, thesummary.rows)
|
|
30
|
+
lookahead = [4, thesummary.rows.length-1].min
|
|
31
|
+
classify_rows(ALL_STATES, NEXT_STATES, thesummary.rows, FinModeling::IncomeStatementItem, lookahead)
|
|
32
|
+
save_cached_classifications(BASE_FILENAME, thesummary.rows)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
save_cached_summary(summary_cache_key, thesummary)
|
|
36
|
+
|
|
37
|
+
return thesummary
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module FinModeling
|
|
2
|
+
class PeriodArray < Array
|
|
3
|
+
def quarterly
|
|
4
|
+
PeriodArray.new(self.select{ |x| (Xbrlware::DateUtil.days_between(x.value["end_date"], x.value["start_date"]) >= 2*28) &&
|
|
5
|
+
(Xbrlware::DateUtil.days_between(x.value["end_date"], x.value["start_date"]) <= 4*31) })
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def halfyearly
|
|
9
|
+
PeriodArray.new(self.select{ |x| (Xbrlware::DateUtil.days_between(x.value["end_date"], x.value["start_date"]) >= 5*30) &&
|
|
10
|
+
(Xbrlware::DateUtil.days_between(x.value["end_date"], x.value["start_date"]) <= 7*31) })
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def threequarterly
|
|
14
|
+
PeriodArray.new(self.select{ |x| (Xbrlware::DateUtil.days_between(x.value["end_date"], x.value["start_date"]) >= 8*30) &&
|
|
15
|
+
(Xbrlware::DateUtil.days_between(x.value["end_date"], x.value["start_date"]) <= 10*31) })
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def yearly
|
|
19
|
+
PeriodArray.new(self.select{ |x| (Xbrlware::DateUtil.days_between(x.value["end_date"], x.value["start_date"]) >= 11*30) &&
|
|
20
|
+
(Xbrlware::DateUtil.days_between(x.value["end_date"], x.value["start_date"]) <= 13*31) })
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module FinModeling
|
|
2
|
+
class QuarterlyReportFiling < AnnualReportFiling
|
|
3
|
+
|
|
4
|
+
def write_constructor(file, item_name)
|
|
5
|
+
balance_sheet.write_constructor( file, bs_name = item_name + "_bs")
|
|
6
|
+
income_statement.write_constructor( file, is_name = item_name + "_is")
|
|
7
|
+
cash_flow_statement.write_constructor(file, cfs_name = item_name + "_cfs")
|
|
8
|
+
|
|
9
|
+
names_of_discs = []
|
|
10
|
+
disclosures.each_with_index do |disclosure, idx|
|
|
11
|
+
name_of_disc = item_name + "_disc#{idx}"
|
|
12
|
+
disclosure.write_constructor(file, name_of_disc)
|
|
13
|
+
names_of_discs << name_of_disc
|
|
14
|
+
end
|
|
15
|
+
names_of_discs_str = "[" + names_of_discs.join(',') + "]"
|
|
16
|
+
|
|
17
|
+
file.puts "#{SCHEMA_VERSION_ITEM} = #{CURRENT_SCHEMA_VERSION}"
|
|
18
|
+
|
|
19
|
+
file.puts "#{item_name} = FinModeling::CachedQuarterlyFiling.new(#{bs_name}, #{is_name}, #{cfs_name}, #{names_of_discs_str})"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module FinModeling
|
|
2
|
+
class Rate
|
|
3
|
+
def initialize(value)
|
|
4
|
+
@value = value
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def annualize(from_days=365, to_days=365)
|
|
8
|
+
((1.0 + @value)**(to_days.to_f/from_days.to_f)) - 1.0
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def yearly_to_quarterly
|
|
12
|
+
annualize(from_days=365.0, to_days=365.0/4.0)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def quarterly_to_yearly
|
|
16
|
+
annualize(from_days=365.0/4.0, to_days=365.0)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module FinModeling
|
|
2
|
+
class Ratio
|
|
3
|
+
def initialize(value)
|
|
4
|
+
@value = value
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def annualize(from_days=365, to_days=365)
|
|
8
|
+
@value*(to_days.to_f/from_days.to_f)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def yearly_to_quarterly
|
|
12
|
+
annualize(from_days=365.0, to_days=365.0/4.0)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def quarterly_to_yearly
|
|
16
|
+
annualize(from_days=365.0/4.0, to_days=365.0)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
module FinModeling
|
|
2
|
+
class ReformulatedBalanceSheet
|
|
3
|
+
attr_accessor :period
|
|
4
|
+
|
|
5
|
+
def initialize(period, assets_summary, liabs_and_equity_summary)
|
|
6
|
+
@period = period
|
|
7
|
+
@oa = assets_summary.filter_by_type(:oa)
|
|
8
|
+
@fa = assets_summary.filter_by_type(:fa)
|
|
9
|
+
@ol = liabs_and_equity_summary.filter_by_type(:ol)
|
|
10
|
+
@fl = liabs_and_equity_summary.filter_by_type(:fl)
|
|
11
|
+
@cse = liabs_and_equity_summary.filter_by_type(:cse)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def operating_assets
|
|
15
|
+
@oa
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def financial_assets
|
|
19
|
+
@fa
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def operating_liabilities
|
|
23
|
+
@ol
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def financial_liabilities
|
|
27
|
+
@fl
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def net_operating_assets
|
|
31
|
+
cs = FinModeling::CalculationSummary.new
|
|
32
|
+
cs.title = "Net Operational Assets"
|
|
33
|
+
cs.rows = [ CalculationRow.new( :key => "OA", :vals => [ @oa.total ] ),
|
|
34
|
+
CalculationRow.new( :key => "OL", :vals => [ -@ol.total ] ) ]
|
|
35
|
+
return cs
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def net_financial_assets
|
|
39
|
+
cs = FinModeling::CalculationSummary.new
|
|
40
|
+
cs.title = "Net Financial Assets"
|
|
41
|
+
cs.rows = [ CalculationRow.new( :key => "FA", :vals => [ @fa.total ] ),
|
|
42
|
+
CalculationRow.new( :key => "FL", :vals => [ -@fl.total ] ) ]
|
|
43
|
+
return cs
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def common_shareholders_equity
|
|
47
|
+
cs = FinModeling::CalculationSummary.new
|
|
48
|
+
cs.title = "Common Shareholders' Equity"
|
|
49
|
+
cs.rows = [ CalculationRow.new( :key => "NOA", :vals => [ net_operating_assets.total ] ),
|
|
50
|
+
CalculationRow.new( :key => "NFA", :vals => [ net_financial_assets.total ] ) ]
|
|
51
|
+
return cs
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def composition_ratio
|
|
55
|
+
net_operating_assets.total / net_financial_assets.total
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def noa_growth(prev)
|
|
59
|
+
rate = (net_operating_assets.total - prev.net_operating_assets.total) / prev.net_operating_assets.total
|
|
60
|
+
return annualize_rate(prev, rate)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def cse_growth(prev)
|
|
64
|
+
rate = (common_shareholders_equity.total - prev.common_shareholders_equity.total) / prev.common_shareholders_equity.total
|
|
65
|
+
return annualize_rate(prev, rate)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def analysis(prev)
|
|
69
|
+
analysis = CalculationSummary.new
|
|
70
|
+
|
|
71
|
+
analysis.title = ""
|
|
72
|
+
analysis.header_row = CalculationHeader.new(:key => "", :vals => [@period.to_pretty_s])
|
|
73
|
+
|
|
74
|
+
analysis.rows = []
|
|
75
|
+
if Config.balance_detail_enabled?
|
|
76
|
+
analysis.rows << CalculationRow.new(:key => "A ($MM)",:vals => [@oa.total.to_nearest_million +
|
|
77
|
+
@fa.total.to_nearest_million])
|
|
78
|
+
analysis.rows << CalculationRow.new(:key => "L ($MM)",:vals => [@ol.total.to_nearest_million +
|
|
79
|
+
@fl.total.to_nearest_million])
|
|
80
|
+
end
|
|
81
|
+
analysis.rows << CalculationRow.new(:key => "NOA ($MM)", :vals => [net_operating_assets.total.to_nearest_million])
|
|
82
|
+
if Config.balance_detail_enabled?
|
|
83
|
+
analysis.rows << CalculationRow.new(:key => "OA ($MM)",:vals => [operating_assets.total.to_nearest_million])
|
|
84
|
+
analysis.rows << CalculationRow.new(:key => "OL ($MM)",:vals => [operating_liabilities.total.to_nearest_million])
|
|
85
|
+
end
|
|
86
|
+
analysis.rows << CalculationRow.new(:key => "NFA ($MM)", :vals => [net_financial_assets.total.to_nearest_million])
|
|
87
|
+
if Config.balance_detail_enabled?
|
|
88
|
+
analysis.rows << CalculationRow.new(:key => "FA ($MM)",:vals => [financial_assets.total.to_nearest_million])
|
|
89
|
+
analysis.rows << CalculationRow.new(:key => "FL ($MM)",:vals => [financial_liabilities.total.to_nearest_million])
|
|
90
|
+
end
|
|
91
|
+
analysis.rows << CalculationRow.new(:key => "CSE ($MM)", :vals => [common_shareholders_equity.total.to_nearest_million])
|
|
92
|
+
analysis.rows << CalculationRow.new(:key => "Composition Ratio", :vals => [composition_ratio] )
|
|
93
|
+
if prev.nil?
|
|
94
|
+
analysis.rows << CalculationRow.new(:key => "NOA Growth", :vals => [nil] )
|
|
95
|
+
analysis.rows << CalculationRow.new(:key => "CSE Growth", :vals => [nil] )
|
|
96
|
+
else
|
|
97
|
+
analysis.rows << CalculationRow.new(:key => "NOA Growth", :vals => [noa_growth(prev)] )
|
|
98
|
+
analysis.rows << CalculationRow.new(:key => "CSE Growth", :vals => [cse_growth(prev)] )
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
return analysis
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def self.forecast_next(period, policy, last_re_bs, next_re_is)
|
|
105
|
+
noa = next_re_is.operating_revenues.total / Ratio.new(policy.sales_over_noa).yearly_to_quarterly
|
|
106
|
+
cse = last_re_bs.common_shareholders_equity.total + next_re_is.comprehensive_income.total
|
|
107
|
+
nfa = cse - noa
|
|
108
|
+
|
|
109
|
+
ForecastedReformulatedBalanceSheet.new(period, noa, nfa, cse)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
private
|
|
113
|
+
|
|
114
|
+
def annualize_rate(prev, rate)
|
|
115
|
+
from_days = Xbrlware::DateUtil.days_between(prev.period.value, @period.value)
|
|
116
|
+
return Rate.new(rate).annualize(from_days, to_days=365)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def deannualize_rate(prev, rate)
|
|
120
|
+
to_days = Xbrlware::DateUtil.days_between(prev.period.value, @period.value)
|
|
121
|
+
return Rate.new(rate).annualize(from_days=365, to_days)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
class ForecastedReformulatedBalanceSheet < ReformulatedBalanceSheet
|
|
126
|
+
def initialize(period, noa, nfa, cse)
|
|
127
|
+
@period = period
|
|
128
|
+
@noa = noa
|
|
129
|
+
@nfa = nfa
|
|
130
|
+
@cse = cse
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def operating_assets
|
|
134
|
+
nil
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def financial_assets
|
|
138
|
+
nil
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def operating_liabilities
|
|
142
|
+
nil
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def financial_liabilities
|
|
146
|
+
nil
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def net_operating_assets
|
|
150
|
+
cs = FinModeling::CalculationSummary.new
|
|
151
|
+
cs.title = "Net Operational Assets"
|
|
152
|
+
cs.rows = [ CalculationRow.new( :key => "NOA", :vals => [@noa] ) ]
|
|
153
|
+
return cs
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def net_financial_assets
|
|
157
|
+
cs = FinModeling::CalculationSummary.new
|
|
158
|
+
cs.title = "Net Financial Assets"
|
|
159
|
+
cs.rows = [ CalculationRow.new( :key => "NFA", :vals => [@nfa] ) ]
|
|
160
|
+
return cs
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def common_shareholders_equity
|
|
164
|
+
cs = FinModeling::CalculationSummary.new
|
|
165
|
+
cs.title = "Common Shareholders' Equity"
|
|
166
|
+
cs.rows = [ CalculationRow.new( :key => "CSE", :vals => [@cse] ) ]
|
|
167
|
+
return cs
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def analysis(prev)
|
|
171
|
+
analysis = super(prev)
|
|
172
|
+
analysis.header_row.vals[0] += "E" # for estimated
|
|
173
|
+
return analysis
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
module FinModeling
|
|
2
|
+
class ReformulatedCashFlowStatement
|
|
3
|
+
attr_accessor :period
|
|
4
|
+
|
|
5
|
+
class FakeCashChangeSummary
|
|
6
|
+
def initialize(re_cfs1, re_cfs2)
|
|
7
|
+
@re_cfs1 = re_cfs1
|
|
8
|
+
@re_cfs2 = re_cfs2
|
|
9
|
+
end
|
|
10
|
+
def filter_by_type(key)
|
|
11
|
+
case key
|
|
12
|
+
when :c
|
|
13
|
+
@cs = FinModeling::CalculationSummary.new
|
|
14
|
+
@cs.title = "Cash from Operations"
|
|
15
|
+
@cs.rows = [ CalculationRow.new(:key => "First Row", :vals => [ @re_cfs1.cash_from_operations.total] ),
|
|
16
|
+
CalculationRow.new(:key => "Second Row", :vals => [-@re_cfs2.cash_from_operations.total] ) ]
|
|
17
|
+
return @cs
|
|
18
|
+
when :i
|
|
19
|
+
@cs = FinModeling::CalculationSummary.new
|
|
20
|
+
@cs.title = "Cash Investments in Operations"
|
|
21
|
+
@cs.rows = [ CalculationRow.new(:key => "First Row", :vals => [ @re_cfs1.cash_investments_in_operations.total] ),
|
|
22
|
+
CalculationRow.new(:key => "Second Row", :vals => [-@re_cfs2.cash_investments_in_operations.total] ) ]
|
|
23
|
+
return @cs
|
|
24
|
+
when :d
|
|
25
|
+
@cs = FinModeling::CalculationSummary.new
|
|
26
|
+
@cs.title = "Payments to Debtholders"
|
|
27
|
+
@cs.rows = [ CalculationRow.new(:key => "First Row", :vals => [ @re_cfs1.payments_to_debtholders.total] ),
|
|
28
|
+
CalculationRow.new(:key => "Second Row", :vals => [-@re_cfs2.payments_to_debtholders.total] ) ]
|
|
29
|
+
return @cs
|
|
30
|
+
when :f
|
|
31
|
+
@cs = FinModeling::CalculationSummary.new
|
|
32
|
+
@cs.title = "Payments to Stockholders"
|
|
33
|
+
@cs.rows = [ CalculationRow.new(:key => "First Row", :vals => [ @re_cfs1.payments_to_stockholders.total] ),
|
|
34
|
+
CalculationRow.new(:key => "Second Row", :vals => [-@re_cfs2.payments_to_stockholders.total] ) ]
|
|
35
|
+
return @cs
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def initialize(period, cash_change_summary)
|
|
41
|
+
@period = period
|
|
42
|
+
|
|
43
|
+
@c = cash_change_summary.filter_by_type(:c) # just make this a member....
|
|
44
|
+
@i = cash_change_summary.filter_by_type(:i)
|
|
45
|
+
@d = cash_change_summary.filter_by_type(:d)
|
|
46
|
+
@f = cash_change_summary.filter_by_type(:f)
|
|
47
|
+
|
|
48
|
+
@c.title = "Cash from operations"
|
|
49
|
+
@i.title = "Cash investments in operations"
|
|
50
|
+
@d.title = "Payments to debtholders"
|
|
51
|
+
@f.title = "Payments to stockholders"
|
|
52
|
+
|
|
53
|
+
if cash_change_summary.class != FakeCashChangeSummary
|
|
54
|
+
@d.rows << CalculationRow.new(:key => "Investment in Cash and Equivalents",
|
|
55
|
+
:type => :d,
|
|
56
|
+
:vals => [-cash_change_summary.total])
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def -(re_cfs2)
|
|
61
|
+
summary = FakeCashChangeSummary.new(self, re_cfs2)
|
|
62
|
+
return ReformulatedCashFlowStatement.new(@period, summary)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def cash_from_operations
|
|
66
|
+
@c
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def cash_investments_in_operations
|
|
70
|
+
@i
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def payments_to_debtholders
|
|
74
|
+
@d
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def payments_to_stockholders
|
|
78
|
+
@f
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def free_cash_flow
|
|
82
|
+
cs = FinModeling::CalculationSummary.new
|
|
83
|
+
cs.title = "Free Cash Flow"
|
|
84
|
+
cs.rows = [ CalculationRow.new(:key => "Cash from Operations (C)", :vals => [@c.total] ),
|
|
85
|
+
CalculationRow.new(:key => "Cash Investment in Operations (I)", :vals => [@i.total] ) ]
|
|
86
|
+
return cs
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def financing_flows
|
|
90
|
+
cs = FinModeling::CalculationSummary.new
|
|
91
|
+
cs.title = "Financing Flows"
|
|
92
|
+
cs.rows = [ CalculationRow.new(:key => "Payments to debtholders (d)", :vals => [@d.total] ),
|
|
93
|
+
CalculationRow.new(:key => "Payments to stockholders (F)", :vals => [@f.total] ) ]
|
|
94
|
+
return cs
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def ni_over_c(inc_stmt)
|
|
98
|
+
inc_stmt.comprehensive_income.total.to_f / cash_from_operations.total
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def self.empty_analysis
|
|
102
|
+
analysis = CalculationSummary.new
|
|
103
|
+
|
|
104
|
+
analysis.title = ""
|
|
105
|
+
analysis.header_row = CalculationHeader.new(:key => "", :vals => ["Unknown..."])
|
|
106
|
+
|
|
107
|
+
analysis.rows = []
|
|
108
|
+
analysis.rows << CalculationRow.new(:key => "C ($MM)", :vals => [nil])
|
|
109
|
+
analysis.rows << CalculationRow.new(:key => "I ($MM)", :vals => [nil])
|
|
110
|
+
analysis.rows << CalculationRow.new(:key => "d ($MM)", :vals => [nil])
|
|
111
|
+
analysis.rows << CalculationRow.new(:key => "F ($MM)", :vals => [nil])
|
|
112
|
+
analysis.rows << CalculationRow.new(:key => "FCF ($MM)", :vals => [nil])
|
|
113
|
+
analysis.rows << CalculationRow.new(:key => "NI / C", :vals => [nil])
|
|
114
|
+
|
|
115
|
+
return analysis
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def analysis(inc_stmt)
|
|
119
|
+
analysis = CalculationSummary.new
|
|
120
|
+
|
|
121
|
+
analysis.title = ""
|
|
122
|
+
analysis.header_row = CalculationHeader.new(:key => "", :vals => [@period.value["end_date"].to_s])
|
|
123
|
+
|
|
124
|
+
analysis.rows = []
|
|
125
|
+
analysis.rows << CalculationRow.new(:key => "C ($MM)", :vals => [cash_from_operations.total.to_nearest_million])
|
|
126
|
+
analysis.rows << CalculationRow.new(:key => "I ($MM)", :vals => [cash_investments_in_operations.total.to_nearest_million])
|
|
127
|
+
analysis.rows << CalculationRow.new(:key => "d ($MM)", :vals => [payments_to_debtholders.total.to_nearest_million])
|
|
128
|
+
analysis.rows << CalculationRow.new(:key => "F ($MM)", :vals => [payments_to_stockholders.total.to_nearest_million])
|
|
129
|
+
analysis.rows << CalculationRow.new(:key => "FCF ($MM)", :vals => [free_cash_flow.total.to_nearest_million])
|
|
130
|
+
if inc_stmt
|
|
131
|
+
analysis.rows << CalculationRow.new(:key => "NI / C", :vals => [ni_over_c(inc_stmt)])
|
|
132
|
+
else
|
|
133
|
+
analysis.rows << CalculationRow.new(:key => "NI / C", :vals => [nil])
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
return analysis
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
end
|
|
140
|
+
end
|