finmodeling 0.1 → 0.2
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 +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
|