finmodeling 0.1 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +0 -0
- data/Gemfile +2 -0
- data/README.md +289 -269
- data/Rakefile +12 -0
- data/TODO.txt +113 -20
- data/examples/{dump_report.rb → dump_latest_10k.rb} +1 -1
- data/examples/list_disclosures.rb +50 -0
- data/examples/lists/nasdaq-mid-to-mega-tech-symbols.txt +0 -0
- data/examples/show_report.rb +112 -32
- data/examples/show_reports.rb +162 -33
- data/finmodeling.gemspec +4 -1
- data/lib/finmodeling/annual_report_filing.rb +97 -18
- data/lib/finmodeling/array_with_stats.rb +0 -0
- data/lib/finmodeling/assets_calculation.rb +12 -3
- data/lib/finmodeling/assets_item.rb +0 -0
- data/lib/finmodeling/assets_item_vectors.rb +0 -0
- data/lib/finmodeling/balance_sheet_analyses.rb +19 -4
- data/lib/finmodeling/balance_sheet_calculation.rb +52 -37
- data/lib/finmodeling/calculation_summary.rb +119 -14
- data/lib/finmodeling/can_cache_classifications.rb +0 -0
- data/lib/finmodeling/can_cache_summaries.rb +0 -0
- data/lib/finmodeling/can_choose_successive_periods.rb +15 -0
- data/lib/finmodeling/can_classify_rows.rb +0 -0
- data/lib/finmodeling/capm.rb +80 -0
- data/lib/finmodeling/cash_change_calculation.rb +3 -3
- data/lib/finmodeling/cash_change_item.rb +0 -0
- data/lib/finmodeling/cash_change_item_vectors.rb +0 -0
- data/lib/finmodeling/cash_change_summary_from_differences.rb +36 -0
- data/lib/finmodeling/cash_flow_statement_analyses.rb +36 -0
- data/lib/finmodeling/cash_flow_statement_calculation.rb +28 -52
- data/lib/finmodeling/classifiers.rb +2 -0
- data/lib/finmodeling/company.rb +0 -0
- data/lib/finmodeling/company_filing.rb +30 -7
- data/lib/finmodeling/company_filing_calculation.rb +16 -6
- data/lib/finmodeling/company_filings.rb +112 -46
- data/lib/finmodeling/comprehensive_income_calculation.rb +60 -0
- data/lib/finmodeling/comprehensive_income_statement_calculation.rb +74 -0
- data/lib/finmodeling/comprehensive_income_statement_item.rb +20 -0
- data/lib/finmodeling/comprehensive_income_statement_item_vectors.rb +235 -0
- data/lib/finmodeling/config.rb +0 -0
- data/lib/finmodeling/debt_cost_of_capital.rb +14 -0
- data/lib/finmodeling/equity_change_calculation.rb +43 -0
- data/lib/finmodeling/equity_change_item.rb +25 -0
- data/lib/finmodeling/equity_change_item_vectors.rb +156 -0
- data/lib/finmodeling/factory.rb +0 -0
- data/lib/finmodeling/fama_french_cost_of_equity.rb +119 -0
- data/lib/finmodeling/float_helpers.rb +14 -8
- data/lib/finmodeling/forecasted_reformulated_balance_sheet.rb +55 -0
- data/lib/finmodeling/forecasted_reformulated_income_statement.rb +110 -0
- data/lib/finmodeling/forecasts.rb +4 -4
- data/lib/finmodeling/has_string_classifer.rb +0 -0
- data/lib/finmodeling/income_statement_analyses.rb +23 -17
- data/lib/finmodeling/income_statement_calculation.rb +46 -43
- data/lib/finmodeling/income_statement_item.rb +1 -1
- data/lib/finmodeling/income_statement_item_vectors.rb +24 -13
- data/lib/finmodeling/invalid_filing_error.rb +4 -0
- data/lib/finmodeling/liabs_and_equity_calculation.rb +18 -8
- data/lib/finmodeling/liabs_and_equity_item.rb +1 -1
- data/lib/finmodeling/liabs_and_equity_item_vectors.rb +24 -24
- data/lib/finmodeling/linear_trend_forecasting_policy.rb +23 -0
- data/lib/finmodeling/net_income_calculation.rb +23 -10
- data/lib/finmodeling/net_income_summary_from_differences.rb +51 -0
- data/lib/finmodeling/paths.rb +0 -0
- data/lib/finmodeling/period_array.rb +8 -4
- data/lib/finmodeling/quarterly_report_filing.rb +9 -4
- data/lib/finmodeling/rate.rb +8 -0
- data/lib/finmodeling/ratio.rb +0 -0
- data/lib/finmodeling/reformulated_balance_sheet.rb +47 -88
- data/lib/finmodeling/reformulated_cash_flow_statement.rb +18 -41
- data/lib/finmodeling/reformulated_income_statement.rb +44 -206
- data/lib/finmodeling/reformulated_shareholder_equity_statement.rb +50 -0
- data/lib/finmodeling/reoi_valuation.rb +104 -0
- data/lib/finmodeling/shareholder_equity_statement_calculation.rb +34 -0
- data/lib/finmodeling/string_helpers.rb +18 -1
- data/lib/finmodeling/time_series_estimator.rb +25 -0
- data/lib/finmodeling/trailing_avg_forecasting_policy.rb +23 -0
- data/lib/finmodeling/version.rb +1 -1
- data/lib/finmodeling/weighted_avg_cost_of_capital.rb +35 -0
- data/lib/finmodeling/yahoo_finance_helpers.rb +20 -0
- data/lib/finmodeling.rb +33 -2
- data/spec/annual_report_filing_spec.rb +81 -45
- data/spec/assets_calculation_spec.rb +7 -4
- data/spec/assets_item_spec.rb +9 -14
- data/spec/balance_sheet_analyses_spec.rb +13 -13
- data/spec/balance_sheet_calculation_spec.rb +45 -51
- data/spec/calculation_summary_spec.rb +113 -21
- data/spec/can_classify_rows_spec.rb +0 -0
- data/spec/cash_change_calculation_spec.rb +1 -10
- data/spec/cash_change_item_spec.rb +10 -18
- data/spec/cash_flow_statement_calculation_spec.rb +10 -24
- data/spec/company_beta_spec.rb +53 -0
- data/spec/company_filing_calculation_spec.rb +39 -49
- data/spec/company_filing_spec.rb +0 -0
- data/spec/company_filings_spec.rb +75 -25
- data/spec/company_spec.rb +37 -47
- data/spec/comprehensive_income_statement_calculation_spec.rb +54 -0
- data/spec/comprehensive_income_statement_item_spec.rb +56 -0
- data/spec/debt_cost_of_capital_spec.rb +19 -0
- data/spec/equity_change_calculation_spec.rb +33 -0
- data/spec/equity_change_item_spec.rb +58 -0
- data/spec/factory_spec.rb +2 -2
- data/spec/forecasts_spec.rb +2 -2
- data/spec/income_statement_analyses_spec.rb +23 -21
- data/spec/income_statement_calculation_spec.rb +17 -49
- data/spec/income_statement_item_spec.rb +17 -29
- data/spec/liabs_and_equity_calculation_spec.rb +6 -3
- data/spec/liabs_and_equity_item_spec.rb +14 -22
- data/spec/linear_trend_forecasting_policy_spec.rb +37 -0
- data/spec/matchers/custom_matchers.rb +79 -0
- data/spec/mocks/calculation.rb +0 -0
- data/spec/mocks/income_statement_analyses.rb +0 -0
- data/spec/mocks/sec_query.rb +0 -0
- data/spec/net_income_calculation_spec.rb +16 -10
- data/spec/period_array.rb +0 -0
- data/spec/quarterly_report_filing_spec.rb +21 -38
- data/spec/rate_spec.rb +0 -0
- data/spec/ratio_spec.rb +0 -0
- data/spec/reformulated_balance_sheet_spec.rb +56 -33
- data/spec/reformulated_cash_flow_statement_spec.rb +18 -10
- data/spec/reformulated_income_statement_spec.rb +16 -15
- data/spec/reformulated_shareholder_equity_statement_spec.rb +43 -0
- data/spec/reoi_valuation_spec.rb +146 -0
- data/spec/shareholder_equity_statement_calculation_spec.rb +59 -0
- data/spec/spec_helper.rb +4 -1
- data/spec/string_helpers_spec.rb +15 -13
- data/spec/time_series_estimator_spec.rb +61 -0
- data/spec/trailing_avg_forecasting_policy_spec.rb +37 -0
- data/spec/weighted_avg_cost_of_capital_spec.rb +32 -0
- data/tools/create_equity_change_training_vectors.rb +49 -0
- data/tools/time_specs.sh +7 -0
- metadata +182 -36
- data/lib/finmodeling/constant_forecasting_policy.rb +0 -23
- data/lib/finmodeling/generic_forecasting_policy.rb +0 -19
- data/spec/constant_forecasting_policy_spec.rb +0 -37
- data/spec/generic_forecasting_policy_spec.rb +0 -33
@@ -1,17 +1,23 @@
|
|
1
1
|
class Fixnum
|
2
|
-
def to_nearest_million
|
3
|
-
return (self/1000000.0).round.to_f
|
2
|
+
def to_nearest_million(num_decimals=1)
|
3
|
+
return (self/1000000.0*(10.0**num_decimals)).round.to_f/(10.0**num_decimals)
|
4
4
|
end
|
5
|
-
def to_nearest_thousand
|
6
|
-
return (self/1000.0).round.to_f
|
5
|
+
def to_nearest_thousand(num_decimals=1)
|
6
|
+
return (self/1000.0*(10.0**num_decimals)).round.to_f/(10.0**num_decimals)
|
7
|
+
end
|
8
|
+
def to_nearest_dollar(num_decimals=1)
|
9
|
+
return ((self*(10.0**num_decimals)).round/(10.0**num_decimals)).to_f
|
7
10
|
end
|
8
11
|
end
|
9
12
|
|
10
13
|
class Float
|
11
|
-
def to_nearest_million
|
12
|
-
return (self/1000000.0).round.to_f
|
14
|
+
def to_nearest_million(num_decimals=1)
|
15
|
+
return (self/1000000.0*(10.0**num_decimals)).round.to_f/(10.0**num_decimals)
|
16
|
+
end
|
17
|
+
def to_nearest_thousand(num_decimals=1)
|
18
|
+
return (self/1000.0*(10.0**num_decimals)).round.to_f/(10.0**num_decimals)
|
13
19
|
end
|
14
|
-
def
|
15
|
-
return (self
|
20
|
+
def to_nearest_dollar(num_decimals=1)
|
21
|
+
return ((self*(10.0**num_decimals)).round/(10.0**num_decimals)).to_f
|
16
22
|
end
|
17
23
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module FinModeling
|
2
|
+
class ForecastedReformulatedBalanceSheet < ReformulatedBalanceSheet
|
3
|
+
def initialize(period, noa, nfa, cse)
|
4
|
+
@period = period
|
5
|
+
@noa = noa
|
6
|
+
@nfa = nfa
|
7
|
+
@cse = cse
|
8
|
+
|
9
|
+
@minority_interest = FinModeling::CalculationSummary.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def operating_assets
|
13
|
+
nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def financial_assets
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def operating_liabilities
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def financial_liabilities
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def net_operating_assets
|
29
|
+
cs = FinModeling::CalculationSummary.new
|
30
|
+
cs.title = "Net Operational Assets"
|
31
|
+
cs.rows = [ CalculationRow.new( :key => "NOA", :vals => [@noa] ) ]
|
32
|
+
return cs
|
33
|
+
end
|
34
|
+
|
35
|
+
def net_financial_assets
|
36
|
+
cs = FinModeling::CalculationSummary.new
|
37
|
+
cs.title = "Net Financial Assets"
|
38
|
+
cs.rows = [ CalculationRow.new( :key => "NFA", :vals => [@nfa] ) ]
|
39
|
+
return cs
|
40
|
+
end
|
41
|
+
|
42
|
+
def common_shareholders_equity
|
43
|
+
cs = FinModeling::CalculationSummary.new
|
44
|
+
cs.title = "Common Shareholders' Equity"
|
45
|
+
cs.rows = [ CalculationRow.new( :key => "CSE", :vals => [@cse] ) ]
|
46
|
+
return cs
|
47
|
+
end
|
48
|
+
|
49
|
+
def analysis(prev)
|
50
|
+
analysis = super(prev)
|
51
|
+
analysis.header_row.vals[0] += "E" # for estimated
|
52
|
+
return analysis
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module FinModeling
|
2
|
+
class ForecastedReformulatedIncomeStatement < ReformulatedIncomeStatement
|
3
|
+
def initialize(period, operating_revenues, income_from_sales_after_tax, net_financing_income, comprehensive_income)
|
4
|
+
@period = period
|
5
|
+
@orev = operating_revenues
|
6
|
+
@income_from_sales_after_tax = income_from_sales_after_tax
|
7
|
+
@net_financing_income = net_financing_income
|
8
|
+
@comprehensive_income = comprehensive_income
|
9
|
+
end
|
10
|
+
|
11
|
+
def -(ris2)
|
12
|
+
raise RuntimeError.new("not implmeneted")
|
13
|
+
end
|
14
|
+
|
15
|
+
def operating_revenues
|
16
|
+
cs = FinModeling::CalculationSummary.new
|
17
|
+
cs.title = "Operating Revenues"
|
18
|
+
cs.rows = [ CalculationRow.new(:key => "Operating Revenues (OR)", :vals => [@orev] ) ]
|
19
|
+
return cs
|
20
|
+
end
|
21
|
+
|
22
|
+
def cost_of_revenues
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def gross_revenue
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def operating_expenses
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def income_from_sales_before_tax
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def income_from_sales_after_tax
|
39
|
+
cs = FinModeling::CalculationSummary.new
|
40
|
+
cs.title = "Operating Income from sales, after tax (OISAT)"
|
41
|
+
cs.rows = [ CalculationRow.new(:key => "Operating income from sales (after tax)", :vals => [@income_from_sales_after_tax] ) ]
|
42
|
+
return cs
|
43
|
+
end
|
44
|
+
|
45
|
+
def operating_income_after_tax
|
46
|
+
income_from_sales_after_tax # this simplified version assumes no non-sales operating income
|
47
|
+
end
|
48
|
+
|
49
|
+
def net_financing_income
|
50
|
+
cs = FinModeling::CalculationSummary.new
|
51
|
+
cs.title = "Net financing income, after tax (NFI)"
|
52
|
+
cs.rows = [ CalculationRow.new(:key => "Net financing income", :vals => [@net_financing_income] ) ]
|
53
|
+
return cs
|
54
|
+
end
|
55
|
+
|
56
|
+
def comprehensive_income
|
57
|
+
cs = FinModeling::CalculationSummary.new
|
58
|
+
cs.title = "Comprehensive Income (CI)"
|
59
|
+
cs.rows = [ CalculationRow.new(:key => "Comprehensive income", :vals => [@comprehensive_income] ) ]
|
60
|
+
return cs
|
61
|
+
end
|
62
|
+
|
63
|
+
def analysis(re_bs, prev_re_is, prev_re_bs, expected_cost_of_capital)
|
64
|
+
analysis = CalculationSummary.new
|
65
|
+
analysis.title = ""
|
66
|
+
analysis.rows = []
|
67
|
+
|
68
|
+
if re_bs.nil?
|
69
|
+
analysis.header_row = CalculationHeader.new(:key => "", :vals => ["Unknown..."])
|
70
|
+
else
|
71
|
+
analysis.header_row = CalculationHeader.new(:key => "", :vals => [re_bs.period.to_pretty_s + "E"])
|
72
|
+
end
|
73
|
+
|
74
|
+
analysis.rows << CalculationRow.new(:key => "Revenue ($MM)", :vals => [operating_revenues.total.to_nearest_million])
|
75
|
+
if Config.income_detail_enabled?
|
76
|
+
analysis.rows << CalculationRow.new(:key => "COGS ($MM)", :vals => [nil])
|
77
|
+
analysis.rows << CalculationRow.new(:key => "GM ($MM)", :vals => [nil])
|
78
|
+
analysis.rows << CalculationRow.new(:key => "OE ($MM)", :vals => [nil])
|
79
|
+
analysis.rows << CalculationRow.new(:key => "OISBT ($MM)", :vals => [nil])
|
80
|
+
end
|
81
|
+
analysis.rows << CalculationRow.new(:key => "Core OI ($MM)", :vals => [income_from_sales_after_tax.total.to_nearest_million])
|
82
|
+
analysis.rows << CalculationRow.new(:key => "OI ($MM)", :vals => [nil])
|
83
|
+
analysis.rows << CalculationRow.new(:key => "FI ($MM)", :vals => [net_financing_income.total.to_nearest_million])
|
84
|
+
analysis.rows << CalculationRow.new(:key => "NI ($MM)", :vals => [comprehensive_income.total.to_nearest_million])
|
85
|
+
analysis.rows << CalculationRow.new(:key => "Gross Margin", :vals => [nil])
|
86
|
+
analysis.rows << CalculationRow.new(:key => "Sales PM", :vals => [sales_profit_margin])
|
87
|
+
analysis.rows << CalculationRow.new(:key => "Operating PM", :vals => [nil])
|
88
|
+
analysis.rows << CalculationRow.new(:key => "FI / Sales", :vals => [fi_over_sales])
|
89
|
+
analysis.rows << CalculationRow.new(:key => "NI / Sales", :vals => [ni_over_sales])
|
90
|
+
|
91
|
+
if !prev_re_bs.nil? && !prev_re_is.nil?
|
92
|
+
analysis.rows << CalculationRow.new(:key => "Sales / NOA", :vals => [sales_over_noa(prev_re_bs)])
|
93
|
+
analysis.rows << CalculationRow.new(:key => "FI / NFA", :vals => [fi_over_nfa( prev_re_bs)])
|
94
|
+
analysis.rows << CalculationRow.new(:key => "Revenue Growth",:vals => [revenue_growth(prev_re_is)])
|
95
|
+
analysis.rows << CalculationRow.new(:key => "Core OI Growth",:vals => [core_oi_growth(prev_re_is)])
|
96
|
+
analysis.rows << CalculationRow.new(:key => "OI Growth", :vals => [nil])
|
97
|
+
analysis.rows << CalculationRow.new(:key => "ReOI ($MM)", :vals => [re_oi(prev_re_bs, expected_cost_of_capital).to_nearest_million])
|
98
|
+
else
|
99
|
+
analysis.rows << CalculationRow.new(:key => "Sales / NOA", :vals => [nil])
|
100
|
+
analysis.rows << CalculationRow.new(:key => "FI / NFA", :vals => [nil])
|
101
|
+
analysis.rows << CalculationRow.new(:key => "Revenue Growth",:vals => [nil])
|
102
|
+
analysis.rows << CalculationRow.new(:key => "Core OI Growth",:vals => [nil])
|
103
|
+
analysis.rows << CalculationRow.new(:key => "OI Growth", :vals => [nil])
|
104
|
+
analysis.rows << CalculationRow.new(:key => "ReOI ($MM)", :vals => [nil])
|
105
|
+
end
|
106
|
+
|
107
|
+
return analysis
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -23,16 +23,16 @@ module FinModeling
|
|
23
23
|
return @balance_sheet_analyses
|
24
24
|
end
|
25
25
|
|
26
|
-
def income_statement_analyses(filings)
|
26
|
+
def income_statement_analyses(filings, expected_rate_of_return)
|
27
27
|
if !@income_statement_analyses
|
28
28
|
prev_filing = filings.last
|
29
29
|
prev_re_bs = prev_filing.balance_sheet.reformulated(prev_filing.balance_sheet.periods.last)
|
30
30
|
prev_prev_is = (filings.length > 2) ? filings[-2].income_statement : nil
|
31
|
-
prev_re_is = prev_filing.income_statement.latest_quarterly_reformulated(prev_prev_is)
|
31
|
+
prev_re_is = prev_filing.income_statement.latest_quarterly_reformulated(prev_cis=nil, prev_prev_is, prev_prev_cis=nil)
|
32
32
|
|
33
33
|
@reformulated_income_statements.zip(@reformulated_balance_sheets).each do |re_is, re_bs|
|
34
|
-
next_analysis = FinModeling::ReformulatedIncomeStatement.empty_analysis
|
35
|
-
next_analysis = re_is.analysis(re_bs, prev_re_is, prev_re_bs)
|
34
|
+
next_analysis = FinModeling::ReformulatedIncomeStatement.empty_analysis if !re_is
|
35
|
+
next_analysis = re_is.analysis(re_bs, prev_re_is, prev_re_bs, expected_rate_of_return) if re_is
|
36
36
|
|
37
37
|
@income_statement_analyses = @income_statement_analyses + next_analysis if @income_statement_analyses
|
38
38
|
@income_statement_analyses = next_analysis if !@income_statement_analyses
|
File without changes
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
module FinModeling
|
2
4
|
|
3
5
|
class IncomeStatementAnalyses < CalculationSummary
|
@@ -11,41 +13,45 @@ module FinModeling
|
|
11
13
|
@totals_row_enabled = false
|
12
14
|
end
|
13
15
|
|
14
|
-
def
|
15
|
-
if
|
16
|
-
lr =
|
17
|
-
puts "\t\
|
16
|
+
def print_regressions
|
17
|
+
if revenue_growth_row && revenue_growth_row.valid_vals.any?
|
18
|
+
lr = revenue_growth_row.valid_vals.linear_regression
|
19
|
+
puts "\t\trevenue growth: "+
|
18
20
|
"a:#{lr.a.to_s.cap_decimals(4)}, "+
|
19
21
|
"b:#{lr.b.to_s.cap_decimals(4)}, "+
|
20
|
-
"r
|
21
|
-
"
|
22
|
+
"r²:#{lr.r2.to_s.cap_decimals(4)}, "+
|
23
|
+
"σ²:#{revenue_growth_row.valid_vals.variance.to_s.cap_decimals(4)}, " +
|
24
|
+
( (lr.r2 > 0.6) ? "strong fit" : ( (lr.r2 < 0.2) ? "weak fit [**]" : "avg fit") )
|
22
25
|
end
|
23
26
|
|
24
27
|
if sales_over_noa_row && sales_over_noa_row.valid_vals.any?
|
25
28
|
lr = sales_over_noa_row.valid_vals.linear_regression
|
26
|
-
puts "\t\tsales / noa:
|
29
|
+
puts "\t\tsales / noa: "+
|
27
30
|
"a:#{lr.a.to_s.cap_decimals(4)}, "+
|
28
31
|
"b:#{lr.b.to_s.cap_decimals(4)}, "+
|
29
|
-
"r
|
30
|
-
"
|
32
|
+
"r²:#{lr.r2.to_s.cap_decimals(4)}, "+
|
33
|
+
"σ²:#{sales_over_noa_row.valid_vals.variance.to_s.cap_decimals(4)}, " +
|
34
|
+
( (lr.r2 > 0.6) ? "strong fit" : ( (lr.r2 < 0.2) ? "weak fit [**]" : "avg fit") )
|
31
35
|
end
|
32
36
|
|
33
|
-
if
|
34
|
-
lr =
|
35
|
-
puts "\t\
|
37
|
+
if operating_pm_row && operating_pm_row.valid_vals.any?
|
38
|
+
lr = operating_pm_row.valid_vals.linear_regression
|
39
|
+
puts "\t\toperating pm: "+
|
36
40
|
"a:#{lr.a.to_s.cap_decimals(4)}, "+
|
37
41
|
"b:#{lr.b.to_s.cap_decimals(4)}, "+
|
38
|
-
"r
|
39
|
-
"
|
42
|
+
"r²:#{lr.r2.to_s.cap_decimals(4)}, "+
|
43
|
+
"σ²:#{operating_pm_row.valid_vals.variance.to_s.cap_decimals(4)}, " +
|
44
|
+
( (lr.r2 > 0.6) ? "strong fit" : ( (lr.r2 < 0.2) ? "weak fit [**]" : "avg fit") )
|
40
45
|
end
|
41
46
|
|
42
47
|
if fi_over_nfa_row && fi_over_nfa_row.valid_vals.any?
|
43
48
|
lr = fi_over_nfa_row.valid_vals.linear_regression
|
44
|
-
puts "\t\tfi / nfa:
|
49
|
+
puts "\t\tfi / nfa: "+
|
45
50
|
"a:#{lr.a.to_s.cap_decimals(4)}, "+
|
46
51
|
"b:#{lr.b.to_s.cap_decimals(4)}, "+
|
47
|
-
"r
|
48
|
-
"
|
52
|
+
"r²:#{lr.r2.to_s.cap_decimals(4)}, "+
|
53
|
+
"σ²:#{fi_over_nfa_row.valid_vals.variance.to_s.cap_decimals(4)}, " +
|
54
|
+
( (lr.r2 > 0.6) ? "strong fit" : ( (lr.r2 < 0.2) ? "weak fit [**]" : "avg fit") )
|
49
55
|
end
|
50
56
|
end
|
51
57
|
|
@@ -1,66 +1,69 @@
|
|
1
1
|
module FinModeling
|
2
2
|
class IncomeStatementCalculation < CompanyFilingCalculation
|
3
|
+
include CanChooseSuccessivePeriods
|
3
4
|
|
5
|
+
NI_GOAL = "net income"
|
6
|
+
NI_LABELS = [ /^(|consolidated )net (|income loss|loss income|income|loss|)(| net of tax)(| attributable to parent)/,
|
7
|
+
/^profit loss$/, # I have a feeling this is from the misguided attempt to parse CI here. Get rid of it...
|
8
|
+
/^allocation.*of.*undistributed.*earnings/ ]
|
9
|
+
NI_ANTI_LABELS = [ ]
|
10
|
+
NI_IDS = [ /^(|Locator_|loc_)(|us-gaap_)NetIncomeLoss[_0-9a-z]+/,
|
11
|
+
/^(|Locator_|loc_)(|us-gaap_)NetIncomeLossAvailableToCommonStockholdersBasic[_0-9a-z]+/,
|
12
|
+
/^(|Locator_|loc_)(|us-gaap_)ProfitLoss[_0-9a-z]+/ ]
|
4
13
|
def net_income_calculation
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
id_regexes = [ /^(|Locator_|loc_)(|us-gaap_)NetIncomeLoss[_0-9a-z]+/,
|
11
|
-
/^(|Locator_|loc_)(|us-gaap_)NetIncomeLossAvailableToCommonStockholdersBasic[_0-9a-z]+/,
|
12
|
-
/^(|Locator_|loc_)(|us-gaap_)ProfitLoss[_0-9a-z]+/ ]
|
13
|
-
calc = find_and_verify_calculation_arc(friendly_goal, label_regexes, id_regexes)
|
14
|
-
@ni = NetIncomeCalculation.new(calc)
|
14
|
+
begin
|
15
|
+
@ni ||= NetIncomeCalculation.new(find_calculation_arc(NI_GOAL, NI_LABELS, NI_ANTI_LABELS, NI_IDS))
|
16
|
+
rescue FinModeling::InvalidFilingError => e
|
17
|
+
pre_msg = "calculation tree:\n" + self.calculation.sprint_tree
|
18
|
+
raise e, pre_msg+e.message, e.backtrace
|
15
19
|
end
|
16
|
-
return @ni
|
17
20
|
end
|
18
21
|
|
19
22
|
def is_valid?
|
20
|
-
|
21
|
-
|
22
|
-
net_income_calculation.
|
23
|
-
if
|
24
|
-
|
25
|
-
|
26
|
-
if !has_tax_item and leaf.name.downcase.matches_regexes?([/tax/])
|
27
|
-
has_tax_item = true
|
23
|
+
puts "income statement's net income calculation lacks tax item" if !net_income_calculation.has_tax_item?
|
24
|
+
puts "income statement's net income calculation lacks sales/revenue item" if !net_income_calculation.has_revenue_item?
|
25
|
+
if !net_income_calculation.has_tax_item? || !net_income_calculation.has_revenue_item?
|
26
|
+
if net_income_calculation
|
27
|
+
puts "summary:"
|
28
|
+
net_income_calculation.summary(:period => periods.last).print
|
28
29
|
end
|
30
|
+
puts "calculation tree:\n" + self.calculation.sprint_tree(indent_count=0, simplified=true)
|
29
31
|
end
|
30
|
-
|
31
|
-
puts "income statement's net income calculation lacks tax item" if !has_tax_item
|
32
|
-
puts "income statement's net income calculation lacks sales/revenue item" if !has_revenue_item
|
33
|
-
return (has_revenue_item and has_tax_item)
|
32
|
+
return (net_income_calculation.has_revenue_item? && net_income_calculation.has_tax_item?)
|
34
33
|
end
|
35
34
|
|
36
|
-
def reformulated(period)
|
35
|
+
def reformulated(period, comprehensive_income_calculation)
|
37
36
|
return ReformulatedIncomeStatement.new(period,
|
38
|
-
net_income_calculation.summary(:period=>period)
|
37
|
+
net_income_calculation.summary(:period=>period),
|
38
|
+
comprehensive_income_calculation ? comprehensive_income_calculation.summary(:period=>period) : nil)
|
39
39
|
end
|
40
40
|
|
41
|
-
def latest_quarterly_reformulated(
|
42
|
-
if
|
43
|
-
|
44
|
-
|
45
|
-
return reformulated(net_income_calculation.periods.quarterly.last)
|
41
|
+
def latest_quarterly_reformulated(cur_ci_calc, prev_is, prev_ci_calc)
|
42
|
+
if net_income_calculation.periods.quarterly.any?
|
43
|
+
period = net_income_calculation.periods.quarterly.last
|
44
|
+
lqr = reformulated(period, cur_ci_calc)
|
46
45
|
|
47
|
-
|
48
|
-
|
46
|
+
if (lqr.operating_revenues.total.abs > 1.0) && # FIXME: make an is_valid here?
|
47
|
+
(lqr.cost_of_revenues .total.abs > 1.0) # FIXME: make an is_valid here?
|
48
|
+
return lqr
|
49
|
+
end
|
50
|
+
end
|
49
51
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
52
|
+
return nil if !prev_is
|
53
|
+
|
54
|
+
cur_period, prev_period = choose_successive_periods(net_income_calculation, prev_is.net_income_calculation)
|
55
|
+
if cur_period && prev_period
|
56
|
+
new_re_is = reformulated(cur_period, cur_ci_calc) - prev_is.reformulated(prev_period, prev_ci_calc)
|
57
|
+
# the above subtraction doesn't know what period you want. So let's patch the result to have
|
58
|
+
# a quarterly period with the right end-points
|
59
|
+
new_re_is.period = Xbrlware::Context::Period.new({"start_date"=>prev_period.value["end_date"],
|
60
|
+
"end_date" =>cur_period.value["end_date"]})
|
61
|
+
return new_re_is
|
59
62
|
end
|
60
|
-
|
63
|
+
|
61
64
|
return nil
|
62
65
|
end
|
63
|
-
|
66
|
+
|
64
67
|
def write_constructor(file, item_name)
|
65
68
|
item_calc_name = item_name + "_calc"
|
66
69
|
@calculation.write_constructor(file, item_calc_name)
|
@@ -3,7 +3,7 @@ module FinModeling
|
|
3
3
|
include HasStringClassifier
|
4
4
|
|
5
5
|
BASE_FILENAME = File.join(FinModeling::BASE_PATH, "classifiers/isi_")
|
6
|
-
TYPES = [ :or, :cogs, :oe, :oibt, :fibt, :tax, :ooiat, :fiat ]
|
6
|
+
TYPES = [ :or, :cogs, :oe, :oibt, :fibt, :tax, :ooiat, :ooiat_nci, :fiat ]
|
7
7
|
|
8
8
|
has_string_classifier(TYPES, IncomeStatementItem)
|
9
9
|
|
@@ -19,6 +19,17 @@ module FinModeling
|
|
19
19
|
{:klass=>:cogs, :item_string=>"Fulfillment Expense"},
|
20
20
|
{:klass=>:cogs, :item_string=>"Fulfillment Expense"},
|
21
21
|
|
22
|
+
{:klass=>:or, :item_string=>"Net Sales"},
|
23
|
+
{:klass=>:cogs, :item_string=>"Cost of products sold"},
|
24
|
+
{:klass=>:oe, :item_string=>"Marketing, selling and administrative"},
|
25
|
+
{:klass=>:oe, :item_string=>"Advertising and product promotion"},
|
26
|
+
{:klass=>:oe, :item_string=>"Research and development"},
|
27
|
+
{:klass=>:oibt, :item_string=>"Provision for restructuring"},
|
28
|
+
{:klass=>:oibt, :item_string=>"Litigation expense, net"},
|
29
|
+
{:klass=>:oibt, :item_string=>"Equity in net income of affiliates"},
|
30
|
+
{:klass=>:oibt, :item_string=>"Other (income)/expense"},
|
31
|
+
{:klass=>:tax, :item_string=>"Provision for income taxes"},
|
32
|
+
|
22
33
|
{:klass=>:or, :item_string=>"Subscription Revenue"},
|
23
34
|
{:klass=>:or, :item_string=>"Sales Revenue Net"},
|
24
35
|
{:klass=>:cogs, :item_string=>"Cost Of Goods Sold Subscription"},
|
@@ -40,7 +51,7 @@ module FinModeling
|
|
40
51
|
{:klass=>:fibt, :item_string=>"Interest Expense"},
|
41
52
|
{:klass=>:fibt, :item_string=>"Other Nonoperating Income Expense"},
|
42
53
|
{:klass=>:tax, :item_string=>"Income Tax Expense Benefit"},
|
43
|
-
{:klass=>:
|
54
|
+
{:klass=>:ooiat_nci, :item_string=>"Net Income Loss Attributable To Noncontrolling Interest"},
|
44
55
|
|
45
56
|
{:klass=>:or, :item_string=>"Sales Revenue Goods Net"},
|
46
57
|
{:klass=>:or, :item_string=>"Sales Revenue Services Net"},
|
@@ -80,9 +91,9 @@ module FinModeling
|
|
80
91
|
{:klass=>:oibt, :item_string=>"Restructuring Charges"},
|
81
92
|
{:klass=>:fibt, :item_string=>"Interest Expense"},
|
82
93
|
{:klass=>:fibt, :item_string=>"Nonoperating Income Expense"},
|
83
|
-
{:klass=>:
|
94
|
+
{:klass=>:oibt, :item_string=>"Gain Loss On Sale Of Equity Investments"},
|
84
95
|
{:klass=>:tax, :item_string=>"Income Tax Expense Benefit"},
|
85
|
-
{:klass=>:
|
96
|
+
{:klass=>:ooiat, :item_string=>"Income Loss From Discontinued Operations Net Of Tax Attributable To Reporting Entity"},
|
86
97
|
|
87
98
|
{:klass=>:or, :item_string=>"Sales Revenue Net"},
|
88
99
|
{:klass=>:cogs, :item_string=>"Cost Of Goods And Services Sold"},
|
@@ -133,7 +144,7 @@ module FinModeling
|
|
133
144
|
{:klass=>:oibt, :item_string=>"Restructuring Charges"},
|
134
145
|
{:klass=>:fibt, :item_string=>"Nonoperating Income Expense"},
|
135
146
|
{:klass=>:tax, :item_string=>"Income Tax Expense Benefit"},
|
136
|
-
{:klass=>:
|
147
|
+
{:klass=>:ooiat, :item_string=>"Income Loss From Discontinued Operations Net Of Tax"},
|
137
148
|
|
138
149
|
{:klass=>:or, :item_string=>"Sales Revenue Goods Net"},
|
139
150
|
{:klass=>:or, :item_string=>"Sales Revenue Services Net"},
|
@@ -197,7 +208,7 @@ module FinModeling
|
|
197
208
|
{:klass=>:fibt, :item_string=>"Interest Expense"},
|
198
209
|
{:klass=>:fibt, :item_string=>"Other Nonoperating Income Expense"},
|
199
210
|
{:klass=>:tax, :item_string=>"Income Tax Expense Benefit"},
|
200
|
-
{:klass=>:
|
211
|
+
{:klass=>:ooiat, :item_string=>"Income Loss From Equity Method Investments"},
|
201
212
|
|
202
213
|
{:klass=>:or, :item_string=>"Licenses Revenue"},
|
203
214
|
{:klass=>:or, :item_string=>"Maintenance Revenue"},
|
@@ -244,7 +255,7 @@ module FinModeling
|
|
244
255
|
{:klass=>:fibt, :item_string=>"Gain Loss On Sale Of Business"},
|
245
256
|
{:klass=>:fibt, :item_string=>"Other Nonoperating Income Expense"},
|
246
257
|
{:klass=>:tax, :item_string=>"Income Tax Expense Benefit"},
|
247
|
-
{:klass=>:
|
258
|
+
{:klass=>:ooiat_nci, :item_string=>"Net Income Loss Attributable To Noncontrolling Interest"},
|
248
259
|
|
249
260
|
{:klass=>:or, :item_string=>"Licenses Revenue"},
|
250
261
|
{:klass=>:or, :item_string=>"Maintenance Revenue"},
|
@@ -297,10 +308,10 @@ module FinModeling
|
|
297
308
|
{:klass=>:oe, :item_string=>"Amortization Of Intangible Assets"},
|
298
309
|
{:klass=>:fibt, :item_string=>"Investment Income Interest"},
|
299
310
|
{:klass=>:fibt, :item_string=>"Interest Expense"},
|
300
|
-
{:klass=>:
|
311
|
+
{:klass=>:oibt, :item_string=>"Income Loss From Equity Method Investments"},
|
301
312
|
{:klass=>:fibt, :item_string=>"Other Nonoperating Expense"},
|
302
313
|
{:klass=>:tax, :item_string=>"Income Tax Expense Benefit"},
|
303
|
-
{:klass=>:
|
314
|
+
{:klass=>:ooiat_nci, :item_string=>"Net Income Loss Attributable To Noncontrolling Interest"},
|
304
315
|
|
305
316
|
{:klass=>:or, :item_string=>"Revenues"},
|
306
317
|
{:klass=>:cogs, :item_string=>"Cost Of Revenue"},
|
@@ -356,7 +367,7 @@ module FinModeling
|
|
356
367
|
{:klass=>:fibt, :item_string=>"Other Nonoperating Income Expense"},
|
357
368
|
{:klass=>:fibt, :item_string=>"Impairment Of Investments"},
|
358
369
|
{:klass=>:tax, :item_string=>"Income Tax Expense Benefit"},
|
359
|
-
{:klass=>:
|
370
|
+
{:klass=>:ooiat_nci, :item_string=>"Net Income Loss Attributable To Noncontrolling Interest"},
|
360
371
|
|
361
372
|
{:klass=>:or, :item_string=>"Sales Revenue Goods Net"},
|
362
373
|
{:klass=>:or, :item_string=>"Sales Revenue Services Net"},
|
@@ -504,7 +515,7 @@ module FinModeling
|
|
504
515
|
{:klass=>:oe, :item_string=>"Research And Development In Process"},
|
505
516
|
{:klass=>:oe, :item_string=>"Goodwill And Acquisition Related Intangible Assets Impairment"},
|
506
517
|
{:klass=>:fibt, :item_string=>"Gain Loss On Disposition Of Assets"},
|
507
|
-
{:klass=>:
|
518
|
+
{:klass=>:oibt, :item_string=>"Equity Method Investment Other Than Temporary Impairment"},
|
508
519
|
{:klass=>:fibt, :item_string=>"Interest Income And Other Expense Net"},
|
509
520
|
{:klass=>:tax, :item_string=>"Income Tax Expense Benefit"},
|
510
521
|
|
@@ -522,7 +533,7 @@ module FinModeling
|
|
522
533
|
{:klass=>:fibt, :item_string=>"Interest Expense"},
|
523
534
|
{:klass=>:fibt, :item_string=>"Other Nonoperating Income Expense"},
|
524
535
|
{:klass=>:tax, :item_string=>"Income Tax Expense Benefit"},
|
525
|
-
{:klass=>:
|
536
|
+
{:klass=>:ooiat, :item_string=>"Income Loss From Equity Method Investments Net Of Tax"},
|
526
537
|
|
527
538
|
{:klass=>:or, :item_string=>"Sales Revenue Net"},
|
528
539
|
{:klass=>:cogs, :item_string=>"Cost Of Revenue"},
|
@@ -533,7 +544,7 @@ module FinModeling
|
|
533
544
|
{:klass=>:fibt, :item_string=>"Interest Expense"},
|
534
545
|
{:klass=>:fibt, :item_string=>"Other Nonoperating Expense"},
|
535
546
|
{:klass=>:tax, :item_string=>"Income Tax Expense Benefit"},
|
536
|
-
{:klass=>:
|
547
|
+
{:klass=>:ooiat_nci, :item_string=>"Net Income Loss Attributable To Noncontrolling Interest"},
|
537
548
|
|
538
549
|
{:klass=>:or, :item_string=>"Sales Revenue Net"},
|
539
550
|
{:klass=>:cogs, :item_string=>"Cost Of Revenue"},
|
@@ -614,7 +625,7 @@ module FinModeling
|
|
614
625
|
{:klass=>:oe, :item_string=>"Research And Development Expense Software Excluding Acquired In Process Cost"},
|
615
626
|
{:klass=>:oe, :item_string=>"Selling And Marketing Expense"},
|
616
627
|
{:klass=>:oe, :item_string=>"General And Administrative Expense"},
|
617
|
-
{:klass=>:
|
628
|
+
{:klass=>:oibt, :item_string=>"Income Loss From Equity Method Investments"},
|
618
629
|
{:klass=>:oibt, :item_string=>"Amortization Of Intangible Assets"},
|
619
630
|
{:klass=>:oibt, :item_string=>"Other Restructuring Costs"},
|
620
631
|
{:klass=>:fibt, :item_string=>"Other Nonoperating Income Expense"},
|
@@ -6,20 +6,18 @@ module FinModeling
|
|
6
6
|
|
7
7
|
BASE_FILENAME = File.join(FinModeling::BASE_PATH, "summaries/liabs_and_equity_")
|
8
8
|
|
9
|
-
ALL_STATES = [ :ol, :fl, :cse ]
|
10
|
-
NEXT_STATES = { nil => [ :ol, :fl, :cse ],
|
11
|
-
:ol => [ :ol, :fl, :cse ],
|
12
|
-
:fl => [ :ol, :fl, :cse ],
|
13
|
-
:cse => [
|
9
|
+
ALL_STATES = [ :ol, :fl, :cse, :mi ]
|
10
|
+
NEXT_STATES = { nil => [ :ol, :fl, :cse, :mi ],
|
11
|
+
:ol => [ :ol, :fl, :cse, :mi ], # operating liabilities
|
12
|
+
:fl => [ :ol, :fl, :cse, :mi ], # financial liabilities
|
13
|
+
:cse => [ :ol, :fl, :cse, :mi ], # common shareholder equity
|
14
|
+
:mi => [ :fl, :cse, :mi ] } # minority interest
|
14
15
|
|
15
16
|
def summary(args)
|
16
17
|
summary_cache_key = args[:period].to_pretty_s
|
17
18
|
summary = lookup_cached_summary(summary_cache_key)
|
18
19
|
return summary if !summary.nil?
|
19
20
|
|
20
|
-
mapping = Xbrlware::ValueMapping.new
|
21
|
-
mapping.policy[:debit] = :flip
|
22
|
-
|
23
21
|
summary = super(:period => args[:period], :mapping => mapping) # FIXME: flip_total should == true!
|
24
22
|
if !lookup_cached_classifications(BASE_FILENAME, summary.rows)
|
25
23
|
lookahead = [4, summary.rows.length-1].min
|
@@ -32,5 +30,17 @@ module FinModeling
|
|
32
30
|
return summary
|
33
31
|
end
|
34
32
|
|
33
|
+
def mapping
|
34
|
+
m = Xbrlware::ValueMapping.new
|
35
|
+
m.policy[:debit] = :flip
|
36
|
+
m
|
37
|
+
end
|
38
|
+
|
39
|
+
def has_equity_item
|
40
|
+
@has_equity_item ||= leaf_items.any? do |leaf|
|
41
|
+
leaf.name.downcase.matches_any_regex?([/equity/, /stock/])
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
35
45
|
end
|
36
46
|
end
|