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,93 @@
|
|
|
1
|
+
@item = FinModeling::MultiColumnCalculationSummary.new
|
|
2
|
+
@item.title = ""
|
|
3
|
+
@item.num_value_columns = 9
|
|
4
|
+
@item.key_width = 18
|
|
5
|
+
@item.val_width = 12
|
|
6
|
+
@item.max_decimals = 4
|
|
7
|
+
@item.totals_row_enabled = false
|
|
8
|
+
args = { }
|
|
9
|
+
args[:key] = ""
|
|
10
|
+
args[:vals] = ["Unknown...", "2010-03-31", "2010-06-30", "2010-09-30", "2010-12-31", "2011-03-31", "2011-06-30", "2011-09-30", "2011-12-31"]
|
|
11
|
+
@item_header_row = FinModeling::MultiColumnCalculationSummaryHeaderRow.new(args)
|
|
12
|
+
@item.header_row = @item_header_row
|
|
13
|
+
args = { }
|
|
14
|
+
args[:key] = "Revenue (000's)"
|
|
15
|
+
args[:type] = ""
|
|
16
|
+
args[:vals] = [0, 1596960.0, 1601379.0, 1601203.0, 1525109.0, 1214357.0, 1229024.0, 1216665.0, 1324153.0]
|
|
17
|
+
@item_row0 = FinModeling::MultiColumnCalculationSummaryRow.new(args)
|
|
18
|
+
args = { }
|
|
19
|
+
args[:key] = "Core OI (000's)"
|
|
20
|
+
args[:type] = ""
|
|
21
|
+
args[:vals] = [0, 171660.0, 117788.0, 173458.0, 230022.0, 146258.0, 133437.0, 126070.0, 178193.0]
|
|
22
|
+
@item_row1 = FinModeling::MultiColumnCalculationSummaryRow.new(args)
|
|
23
|
+
args = { }
|
|
24
|
+
args[:key] = "OI (000's)"
|
|
25
|
+
args[:type] = ""
|
|
26
|
+
args[:vals] = [0, 168792.0, 111254.0, 169715.0, 205495.0, 139384.0, 133283.0, 127839.0, 167579.0]
|
|
27
|
+
@item_row2 = FinModeling::MultiColumnCalculationSummaryRow.new(args)
|
|
28
|
+
args = { }
|
|
29
|
+
args[:key] = "FI (000's)"
|
|
30
|
+
args[:type] = ""
|
|
31
|
+
args[:vals] = [0, 141399.0, 102067.0, 226416.0, 106525.0, 83608.0, 103689.0, 165452.0, 127993.0]
|
|
32
|
+
@item_row3 = FinModeling::MultiColumnCalculationSummaryRow.new(args)
|
|
33
|
+
args = { }
|
|
34
|
+
args[:key] = "NI (000's)"
|
|
35
|
+
args[:type] = ""
|
|
36
|
+
args[:vals] = [0, 310191.0, 213321.0, 396131.0, 312020.0, 222992.0, 236972.0, 293291.0, 295572.0]
|
|
37
|
+
@item_row4 = FinModeling::MultiColumnCalculationSummaryRow.new(args)
|
|
38
|
+
args = { }
|
|
39
|
+
args[:key] = "Gross Margin"
|
|
40
|
+
args[:type] = ""
|
|
41
|
+
args[:vals] = [0, 0.5576708245666767, 0.5736661964469373, 0.5748484108510913, 0.6343297429888618, 0.689167188890911, 0.6980083383237431, 0.7047042530195247, 0.7018796166304045]
|
|
42
|
+
@item_row5 = FinModeling::MultiColumnCalculationSummaryRow.new(args)
|
|
43
|
+
args = { }
|
|
44
|
+
args[:key] = "Sales PM"
|
|
45
|
+
args[:type] = ""
|
|
46
|
+
args[:vals] = [0, 0.10749148381925659, 0.07355385577055774, 0.10832951849328286, 0.15082361326305202, 0.12044085882487604, 0.10857147622829172, 0.10361968988998616, 0.13457104277224763]
|
|
47
|
+
@item_row6 = FinModeling::MultiColumnCalculationSummaryRow.new(args)
|
|
48
|
+
args = { }
|
|
49
|
+
args[:key] = "Operating PM"
|
|
50
|
+
args[:type] = ""
|
|
51
|
+
args[:vals] = [0, 0.10569569682396554, 0.06947374731403372, 0.10599208844849779, 0.13474099228317452, 0.1147804558297107, 0.10844613286640456, 0.10507337681284495, 0.12655546602243095]
|
|
52
|
+
@item_row7 = FinModeling::MultiColumnCalculationSummaryRow.new(args)
|
|
53
|
+
args = { }
|
|
54
|
+
args[:key] = "FI / Sales"
|
|
55
|
+
args[:type] = ""
|
|
56
|
+
args[:vals] = [0, 0.0885427311892596, 0.06373706661571059, 0.14140377578608085, 0.06984766334734108, 0.06884923461552081, 0.08436702619314188, 0.13598804929869768, 0.09666043123415496]
|
|
57
|
+
@item_row8 = FinModeling::MultiColumnCalculationSummaryRow.new(args)
|
|
58
|
+
args = { }
|
|
59
|
+
args[:key] = "NI / Sales"
|
|
60
|
+
args[:type] = ""
|
|
61
|
+
args[:vals] = [0, 0.19423842801322513, 0.1332108139297443, 0.24739586423457863, 0.2045886556305156, 0.1836296904452315, 0.19281315905954644, 0.24106142611154263, 0.22321589725658592]
|
|
62
|
+
@item_row9 = FinModeling::MultiColumnCalculationSummaryRow.new(args)
|
|
63
|
+
args = { }
|
|
64
|
+
args[:key] = "Sales / NOA"
|
|
65
|
+
args[:type] = ""
|
|
66
|
+
args[:vals] = [0, 0, 0.31589471931674, 0.3027171516963245, 0.2812087090658527, 0.22230803971150923, 0.21556306903197936, 0.2058721704664517, 0.22777909563225565]
|
|
67
|
+
@item_row10 = FinModeling::MultiColumnCalculationSummaryRow.new(args)
|
|
68
|
+
args = { }
|
|
69
|
+
args[:key] = "FI / NFA"
|
|
70
|
+
args[:type] = ""
|
|
71
|
+
args[:vals] = [0, 0, 0.01388443269851155, 0.03307811869055056, 0.01601019419285102, 0.011782960277534123, 0.014556844204896556, 0.0241571697495845, 0.019256398729732317]
|
|
72
|
+
@item_row11 = FinModeling::MultiColumnCalculationSummaryRow.new(args)
|
|
73
|
+
args = { }
|
|
74
|
+
args[:key] = "Revenue Growth"
|
|
75
|
+
args[:type] = ""
|
|
76
|
+
args[:vals] = [0, 0, 0.01114526118072745, -0.00043596613617347124, -0.17565789513442365, -0.6030968787992619, 0.04933275376563051, -0.03930454685490847, 0.39916742999669985]
|
|
77
|
+
@item_row12 = FinModeling::MultiColumnCalculationSummaryRow.new(args)
|
|
78
|
+
args = { }
|
|
79
|
+
args[:key] = "Core OI Growth"
|
|
80
|
+
args[:type] = ""
|
|
81
|
+
args[:vals] = [0, 0, -0.7792359004477285, 3.644010853978843, 2.0641568409990847, -0.8406048886193428, -0.30787317437309647, -0.20172411964684556, 2.9464409948934227]
|
|
82
|
+
@item_row13 = FinModeling::MultiColumnCalculationSummaryRow.new(args)
|
|
83
|
+
args = { }
|
|
84
|
+
args[:key] = "OI Growth"
|
|
85
|
+
args[:type] = ""
|
|
86
|
+
args[:vals] = [0, 0, -0.8121268899268889, 4.341206465523186, 1.136062554145436, -0.7928481118131626, -0.16434540325239466, -0.1524844979682214, 1.926767066125802]
|
|
87
|
+
@item_row14 = FinModeling::MultiColumnCalculationSummaryRow.new(args)
|
|
88
|
+
args = { }
|
|
89
|
+
args[:key] = "ReOI (000's)"
|
|
90
|
+
args[:type] = ""
|
|
91
|
+
args[:vals] = [0, 0, -10648.0, 41106.0, 73629.0, 9489.0, -3819.0, -15854.0, 26232.0]
|
|
92
|
+
@item_row15 = FinModeling::MultiColumnCalculationSummaryRow.new(args)
|
|
93
|
+
@item.rows = [@item_row0,@item_row1,@item_row2,@item_row3,@item_row4,@item_row5,@item_row6,@item_row7,@item_row8,@item_row9,@item_row10,@item_row11,@item_row12,@item_row13,@item_row14,@item_row15]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module FinModeling
|
|
2
|
+
module Mocks
|
|
3
|
+
class Filing_10K
|
|
4
|
+
attr_accessor :term, :date, :link
|
|
5
|
+
def initialize
|
|
6
|
+
@term="10-K"
|
|
7
|
+
@date="1994-01-26T00:00:00-05:00"
|
|
8
|
+
@link="http://www.sec.gov/Archives/edgar/data/320193/000119312512023398/0001193125-12-023398-index.htm"
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class Filing_10Q
|
|
13
|
+
attr_accessor :term, :date, :link
|
|
14
|
+
def initialize
|
|
15
|
+
@term="10-Q"
|
|
16
|
+
@date="1995-01-26T00:00:00-05:00"
|
|
17
|
+
@link="http://www.sec.gov/Archives/edgar/data/1288776/000119312511282235/0001193125-11-282235-index.htm"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class Entity
|
|
22
|
+
attr_accessor :name, :filings
|
|
23
|
+
def initialize
|
|
24
|
+
@name = "Apple Inc"
|
|
25
|
+
@filings = []
|
|
26
|
+
@filings.push Mocks::Filing_10K.new
|
|
27
|
+
@filings.push Mocks::Filing_10Q.new
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# income_statement_calculation_spec.rb
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe FinModeling::NetIncomeCalculation do
|
|
6
|
+
before(:all) do
|
|
7
|
+
google_2011_annual_rpt = "http://www.sec.gov/Archives/edgar/data/1288776/000119312512025336/0001193125-12-025336-index.htm"
|
|
8
|
+
filing = FinModeling::AnnualReportFiling.download google_2011_annual_rpt
|
|
9
|
+
@inc_stmt = filing.income_statement
|
|
10
|
+
@period = @inc_stmt.periods.last
|
|
11
|
+
@ni = @inc_stmt.net_income_calculation
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
describe "summary" do
|
|
15
|
+
it "only requires a period (knows how debts/credits work and whether to flip the total)" do
|
|
16
|
+
@ni.summary(:period=>@period).should be_an_instance_of FinModeling::CalculationSummary
|
|
17
|
+
end
|
|
18
|
+
it "tags each row with an Income Statement Type" do
|
|
19
|
+
FinModeling::IncomeStatementItem::TYPES.include?(@ni.summary(:period=>@period).rows.first.type).should be_true
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# period_array_spec.rb
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe FinModeling::PeriodArray do
|
|
6
|
+
before(:all) do
|
|
7
|
+
@t_now = Date.parse('2012-01-01')
|
|
8
|
+
@t_3mo_ago = Date.parse('2011-09-01')
|
|
9
|
+
@t_6mo_ago = Date.parse('2011-06-01')
|
|
10
|
+
@t_0mo_ago = Date.parse('2011-03-01')
|
|
11
|
+
@t_1yr_ago = Date.parse('2011-01-01')
|
|
12
|
+
|
|
13
|
+
@arr = FinModeling::PeriodArray.new
|
|
14
|
+
@arr.push Xbrlware::Context::Period.new({"start_date"=>@t_1yr_ago, "end_date"=>@t_now}) # 1 yr
|
|
15
|
+
@arr.push Xbrlware::Context::Period.new({"start_date"=>@t_1yr_ago, "end_date"=>@t_3mo_ago}) # 9 mo
|
|
16
|
+
@arr.push Xbrlware::Context::Period.new({"start_date"=>@t_1yr_ago, "end_date"=>@t_6mo_ago}) # 6 mo
|
|
17
|
+
@arr.push Xbrlware::Context::Period.new({"start_date"=>@t_3mo_ago, "end_date"=>@t_now}) # 3 mo
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
describe "yearly" do
|
|
21
|
+
subject { @arr.yearly }
|
|
22
|
+
it { should be_an_instance_of FinModeling::PeriodArray }
|
|
23
|
+
it "returns only annual periods" do
|
|
24
|
+
subject.first.to_pretty_s.should == @arr[0].to_pretty_s
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
describe "threequarterly" do
|
|
29
|
+
subject { @arr.threequarterly }
|
|
30
|
+
it { should be_an_instance_of FinModeling::PeriodArray }
|
|
31
|
+
it "returns only three-quarter periods" do
|
|
32
|
+
subject.first.to_pretty_s.should == @arr[1].to_pretty_s
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
describe "halfyearly" do
|
|
37
|
+
subject { @arr.halfyearly }
|
|
38
|
+
it { should be_an_instance_of FinModeling::PeriodArray }
|
|
39
|
+
it "returns only two-quarter periods" do
|
|
40
|
+
subject.first.to_pretty_s.should == @arr[2].to_pretty_s
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
describe "quarterly" do
|
|
45
|
+
subject { @arr.quarterly }
|
|
46
|
+
it { should be_an_instance_of FinModeling::PeriodArray }
|
|
47
|
+
it "returns only quarterly periods" do
|
|
48
|
+
subject.first.to_pretty_s.should == @arr[3].to_pretty_s
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# quarterly_report_filing_spec.rb
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe FinModeling::QuarterlyReportFiling do
|
|
6
|
+
before(:all) do
|
|
7
|
+
company = FinModeling::Company.new(FinModeling::Mocks::Entity.new)
|
|
8
|
+
filing_url = company.quarterly_reports.last.link
|
|
9
|
+
FinModeling::Config::disable_caching
|
|
10
|
+
@filing = FinModeling::QuarterlyReportFiling.download(filing_url)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
after(:all) do
|
|
14
|
+
FinModeling::Config::enable_caching
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
subject { @filing }
|
|
18
|
+
its(:balance_sheet) { should be_a FinModeling::BalanceSheetCalculation }
|
|
19
|
+
its(:income_statement) { should be_a FinModeling::IncomeStatementCalculation }
|
|
20
|
+
its(:cash_flow_statement) { should be_a FinModeling::CashFlowStatementCalculation }
|
|
21
|
+
|
|
22
|
+
its(:is_valid?) { should == (@filing.income_statement.is_valid? && @filing.balance_sheet.is_valid? && @filing.cash_flow_statement.is_valid?) }
|
|
23
|
+
|
|
24
|
+
describe "write_constructor" do
|
|
25
|
+
before(:all) do
|
|
26
|
+
file_name = "/tmp/finmodeling-quarterly-rpt.rb"
|
|
27
|
+
schema_version_item_name = "@schema_version"
|
|
28
|
+
item_name = "@quarterly_rpt"
|
|
29
|
+
file = File.open(file_name, "w")
|
|
30
|
+
@filing.write_constructor(file, item_name)
|
|
31
|
+
file.close
|
|
32
|
+
|
|
33
|
+
eval(File.read(file_name))
|
|
34
|
+
|
|
35
|
+
@schema_version = eval(schema_version_item_name)
|
|
36
|
+
@loaded_filing = eval(item_name)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "writes itself to a file, and saves a schema version of 1.1" do
|
|
40
|
+
@schema_version.should be == 1.1
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "writes itself to a file, and when reloaded, has the same periods" do
|
|
44
|
+
expected_periods = @filing.balance_sheet.periods.map{|x| x.to_pretty_s}.join(',')
|
|
45
|
+
@loaded_filing.balance_sheet.periods.map{|x| x.to_pretty_s}.join(',').should == expected_periods
|
|
46
|
+
end
|
|
47
|
+
it "writes itself to a file, and when reloaded, has the same net operating assets" do
|
|
48
|
+
period = @filing.balance_sheet.periods.last
|
|
49
|
+
expected_noa = @filing.balance_sheet.reformulated(period).net_operating_assets.total
|
|
50
|
+
@loaded_filing.balance_sheet.reformulated(period).net_operating_assets.total.should be_within(1.0).of(expected_noa)
|
|
51
|
+
end
|
|
52
|
+
it "writes itself to a file, and when reloaded, has the same net financing income" do
|
|
53
|
+
period = @filing.income_statement.periods.last
|
|
54
|
+
expected_nfi = @filing.income_statement.reformulated(period).net_financing_income.total
|
|
55
|
+
@loaded_filing.income_statement.reformulated(period).net_financing_income.total.should be_within(1.0).of(expected_nfi)
|
|
56
|
+
end
|
|
57
|
+
it "writes itself to a file, and when reloaded, has the same net change in cash" do
|
|
58
|
+
period = @filing.cash_flow_statement.periods.last
|
|
59
|
+
expected_cash_change = @filing.cash_flow_statement.cash_change_calculation.summary(:period=>period).total
|
|
60
|
+
@loaded_filing.cash_flow_statement.cash_change_calculation.summary(:period=>period).total.should be_within(1.0).of(expected_cash_change)
|
|
61
|
+
end
|
|
62
|
+
it "writes itself to a file, and when reloaded, has the same disclosures" do
|
|
63
|
+
period = @filing.disclosures.first.periods.last
|
|
64
|
+
expected_total = @filing.disclosures.first.summary(:period=>period).total
|
|
65
|
+
@loaded_filing.disclosures.first.summary(:period=>period).total.should == expected_total
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
end
|
|
69
|
+
end
|
data/spec/rate_spec.rb
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# rate_spec.rb
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe FinModeling::Rate do
|
|
6
|
+
describe ".annualize" do
|
|
7
|
+
let(:val) { 0.2 }
|
|
8
|
+
context "when annualizing from a quarter to a year" do
|
|
9
|
+
subject { FinModeling::Rate.new(val).annualize(from_days=365.0/4.0, to_days=365.0) }
|
|
10
|
+
it { should be_a_kind_of Float }
|
|
11
|
+
it { should be_within(0.0001).of( (val+1.0)**(4.00) - 1.0 ) }
|
|
12
|
+
end
|
|
13
|
+
context "when annualizing from a year to a quarter" do
|
|
14
|
+
subject { FinModeling::Rate.new(val).annualize(from_days=365.0, to_days=365.0/4.0) }
|
|
15
|
+
it { should be_a_kind_of Float }
|
|
16
|
+
it { should be_within(0.0001).of( (val+1.0)**(0.25) - 1.0 ) }
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
describe ".yearly_to_quarterly" do
|
|
21
|
+
let(:val) { 0.2 }
|
|
22
|
+
subject { FinModeling::Rate.new(val).yearly_to_quarterly }
|
|
23
|
+
it { should be_a_kind_of Float }
|
|
24
|
+
it { should be_within(0.0001).of( FinModeling::Rate.new(val).annualize(from_days=365.0, to_days=365.0/4.0) ) }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
describe ".quarterly_to_yearly" do
|
|
28
|
+
let(:val) { 0.2 }
|
|
29
|
+
subject { FinModeling::Rate.new(val).quarterly_to_yearly }
|
|
30
|
+
it { should be_a_kind_of Float }
|
|
31
|
+
it { should be_within(0.0001).of( FinModeling::Rate.new(val).annualize(from_days=365.0/4.0, to_days=365.0) ) }
|
|
32
|
+
end
|
|
33
|
+
end
|
data/spec/ratio_spec.rb
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# rate_spec.rb
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe FinModeling::Ratio do
|
|
6
|
+
describe ".annualize" do
|
|
7
|
+
let(:val) { 0.2 }
|
|
8
|
+
context "when annualizing from a quarter to a year" do
|
|
9
|
+
subject { FinModeling::Ratio.new(val).annualize(from_days=365.0/4.0, to_days=365.0) }
|
|
10
|
+
it { should be_a_kind_of Float }
|
|
11
|
+
it { should be_within(0.0001).of(val * 4.0) }
|
|
12
|
+
end
|
|
13
|
+
context "when annualizing from a year to a quarter" do
|
|
14
|
+
subject { FinModeling::Ratio.new(val).annualize(from_days=365.0, to_days=365.0/4.0) }
|
|
15
|
+
it { should be_a_kind_of Float }
|
|
16
|
+
it { should be_within(0.0001).of(val / 4.0) }
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
describe ".yearly_to_quarterly" do
|
|
21
|
+
let(:val) { 0.2 }
|
|
22
|
+
subject { FinModeling::Ratio.new(val).yearly_to_quarterly }
|
|
23
|
+
it { should be_a_kind_of Float }
|
|
24
|
+
it { should be_within(0.0001).of( FinModeling::Ratio.new(val).annualize(from_days=365.0, to_days=365.0/4.0) ) }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
describe ".quarterly_to_yearly" do
|
|
28
|
+
let(:val) { 0.2 }
|
|
29
|
+
subject { FinModeling::Ratio.new(val).quarterly_to_yearly }
|
|
30
|
+
it { should be_a_kind_of Float }
|
|
31
|
+
it { should be_within(0.0001).of( FinModeling::Ratio.new(val).annualize(from_days=365.0/4.0, to_days=365.0) ) }
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# reformulated_income_statement_spec.rb
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe FinModeling::ReformulatedBalanceSheet do
|
|
6
|
+
before(:all) do
|
|
7
|
+
google_2010_annual_rpt = "http://www.sec.gov/Archives/edgar/data/1288776/000119312510030774/0001193125-10-030774-index.htm"
|
|
8
|
+
filing = FinModeling::AnnualReportFiling.download google_2010_annual_rpt
|
|
9
|
+
@bal_sheet= filing.balance_sheet
|
|
10
|
+
|
|
11
|
+
@period = @bal_sheet.periods.last
|
|
12
|
+
@prev_reformed_bal_sheet = @bal_sheet.reformulated(@period)
|
|
13
|
+
|
|
14
|
+
google_2011_annual_rpt = "http://www.sec.gov/Archives/edgar/data/1288776/000119312512025336/0001193125-12-025336-index.htm"
|
|
15
|
+
filing = FinModeling::AnnualReportFiling.download google_2011_annual_rpt
|
|
16
|
+
@bal_sheet = filing.balance_sheet
|
|
17
|
+
@period = @bal_sheet.periods.last
|
|
18
|
+
@reformed_bal_sheet = @bal_sheet.reformulated(@period)
|
|
19
|
+
|
|
20
|
+
@years_between_sheets = 2.0
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
describe "new" do
|
|
24
|
+
it "takes an assets calculation and a liabs_and_equity calculation and a period and returns a CalculationSummary" do
|
|
25
|
+
rbs = FinModeling::ReformulatedBalanceSheet.new(@period, @bal_sheet.assets_calculation.summary(:period=>@period), @bal_sheet.liabs_and_equity_calculation.summary(:period=>@period))
|
|
26
|
+
rbs.should be_an_instance_of FinModeling::ReformulatedBalanceSheet
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
describe "operating_assets" do
|
|
31
|
+
subject { @reformed_bal_sheet.operating_assets }
|
|
32
|
+
it { should be_an_instance_of FinModeling::CalculationSummary }
|
|
33
|
+
its(:total) { should be_within(0.1).of(26943000000.0) }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
describe "financial_assets" do
|
|
37
|
+
subject { @reformed_bal_sheet.financial_assets }
|
|
38
|
+
it { should be_an_instance_of FinModeling::CalculationSummary }
|
|
39
|
+
its(:total) { should be_within(0.1).of(45631000000.0) }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
describe "operating_liabilities" do
|
|
43
|
+
subject { @reformed_bal_sheet.operating_liabilities }
|
|
44
|
+
it { should be_an_instance_of FinModeling::CalculationSummary }
|
|
45
|
+
its(:total) { should be_within(0.1).of(6041000000.0) }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
describe "financial_liabilities" do
|
|
49
|
+
subject { @reformed_bal_sheet.financial_liabilities }
|
|
50
|
+
it { should be_an_instance_of FinModeling::CalculationSummary }
|
|
51
|
+
its(:total) { should be_within(0.1).of(8388000000.0) }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
describe "net_operating_assets" do
|
|
55
|
+
subject { @reformed_bal_sheet.net_operating_assets }
|
|
56
|
+
it { should be_an_instance_of FinModeling::CalculationSummary }
|
|
57
|
+
its(:total) { should be_within(0.1).of(20902000000.0) }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
describe "net_financial_assets" do
|
|
61
|
+
subject { @reformed_bal_sheet.net_financial_assets }
|
|
62
|
+
it { should be_an_instance_of FinModeling::CalculationSummary }
|
|
63
|
+
its(:total) { should be_within(0.1).of(37243000000.0) }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
describe "common_shareholders_equity" do
|
|
67
|
+
subject { @reformed_bal_sheet.common_shareholders_equity }
|
|
68
|
+
it { should be_an_instance_of FinModeling::CalculationSummary }
|
|
69
|
+
its(:total) { should be_within(0.1).of(58145000000.0) }
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
describe "composition_ratio" do
|
|
73
|
+
subject { @reformed_bal_sheet.composition_ratio }
|
|
74
|
+
it { should be_within(0.1).of(@reformed_bal_sheet.net_operating_assets.total / @reformed_bal_sheet.net_financial_assets.total) }
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
describe "noa_growth" do
|
|
78
|
+
subject { @reformed_bal_sheet.noa_growth(@prev_reformed_bal_sheet) }
|
|
79
|
+
let(:noa0) { @prev_reformed_bal_sheet.net_operating_assets.total }
|
|
80
|
+
let(:noa1) { @reformed_bal_sheet.net_operating_assets.total }
|
|
81
|
+
let(:expected_growth) { FinModeling::Rate.new((noa1-noa0) / noa0).annualize(from=365.0*@years_between_sheets, to=365.0) }
|
|
82
|
+
it { should be_within(0.001).of(expected_growth) }
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
describe "cse_growth" do
|
|
86
|
+
subject { @reformed_bal_sheet.cse_growth(@prev_reformed_bal_sheet) }
|
|
87
|
+
let(:cse0) { @prev_reformed_bal_sheet.common_shareholders_equity.total }
|
|
88
|
+
let(:cse1) { @reformed_bal_sheet.common_shareholders_equity.total }
|
|
89
|
+
let(:expected_growth) { FinModeling::Rate.new((cse1-cse0) / cse0).annualize(from=365.0*@years_between_sheets, to=365.0) }
|
|
90
|
+
it { should be_within(0.001).of(expected_growth) }
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
describe "analysis" do
|
|
94
|
+
subject {@reformed_bal_sheet.analysis(@prev_reformed_bal_sheet) }
|
|
95
|
+
it { should be_an_instance_of FinModeling::CalculationSummary }
|
|
96
|
+
it "contains the expected rows" do
|
|
97
|
+
expected_keys = ["NOA ($MM)", "NFA ($MM)", "CSE ($MM)",
|
|
98
|
+
"Composition Ratio", "NOA Growth", "CSE Growth" ]
|
|
99
|
+
|
|
100
|
+
subject.rows.map{ |row| row.key }.should == expected_keys
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
describe "#forecast_next" do
|
|
105
|
+
before (:all) do
|
|
106
|
+
@company = FinModeling::Company.find("aapl")
|
|
107
|
+
@filings = FinModeling::CompanyFilings.new(@company.filings_since_date(Time.parse("2010-10-01")))
|
|
108
|
+
@policy = FinModeling::GenericForecastingPolicy.new
|
|
109
|
+
|
|
110
|
+
prev_bs_period = @filings.last.balance_sheet.periods.last
|
|
111
|
+
next_bs_period_value = prev_bs_period.value.next_month.next_month.next_month
|
|
112
|
+
@next_bs_period = Xbrlware::Context::Period.new(next_bs_period_value)
|
|
113
|
+
|
|
114
|
+
next_is_period_value = {"start_date" => prev_bs_period.value,
|
|
115
|
+
"end_date" => prev_bs_period.value.next_month.next_month.next_month }
|
|
116
|
+
@next_is_period = Xbrlware::Context::Period.new(next_is_period_value)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
let(:last_re_is) { @filings.last.income_statement.latest_quarterly_reformulated(nil) }
|
|
120
|
+
let(:last_re_bs) { @filings.last.balance_sheet.reformulated(@filings.last.balance_sheet.periods.last) }
|
|
121
|
+
let(:next_re_is) { FinModeling::ReformulatedIncomeStatement.forecast_next(@next_is_period, @policy, last_re_bs, last_re_is) }
|
|
122
|
+
|
|
123
|
+
subject { FinModeling::ReformulatedBalanceSheet.forecast_next(@next_bs_period, @policy, last_re_bs, next_re_is) }
|
|
124
|
+
|
|
125
|
+
it { should be_a_kind_of FinModeling::ReformulatedBalanceSheet }
|
|
126
|
+
it "should have the given period" do
|
|
127
|
+
subject.period.to_pretty_s == @next_bs_period.to_pretty_s
|
|
128
|
+
end
|
|
129
|
+
it "should set NOA to the same period's operating revenue over the policy's asset turnover" do
|
|
130
|
+
expected_val = next_re_is.operating_revenues.total / FinModeling::Ratio.new(@policy.sales_over_noa).yearly_to_quarterly
|
|
131
|
+
subject.net_operating_assets.total.should == expected_val
|
|
132
|
+
end
|
|
133
|
+
it "should set CSE to last year's CSE plus this year's net income" do
|
|
134
|
+
expected_val = last_re_bs.common_shareholders_equity.total + next_re_is.comprehensive_income.total
|
|
135
|
+
subject.common_shareholders_equity.total.should == expected_val
|
|
136
|
+
end
|
|
137
|
+
it "should set NFA to the gap between CSE and NOA" do
|
|
138
|
+
expected_val = subject.common_shareholders_equity.total - subject.net_operating_assets.total
|
|
139
|
+
subject.net_financial_assets.total.should == expected_val
|
|
140
|
+
end
|
|
141
|
+
it "should have an analysis (with the same rows)" do
|
|
142
|
+
subject.analysis(last_re_bs)
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# reformulated_income_statement_spec.rb
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe FinModeling::ReformulatedCashFlowStatement do
|
|
6
|
+
before(:all) do
|
|
7
|
+
google_2009_annual_rpt = "http://www.sec.gov/Archives/edgar/data/1288776/000119312510030774/0001193125-10-030774-index.htm"
|
|
8
|
+
filing_2009 = FinModeling::AnnualReportFiling.download google_2009_annual_rpt
|
|
9
|
+
cash_flow_stmt_2009 = filing_2009.cash_flow_statement
|
|
10
|
+
period_2009 = cash_flow_stmt_2009.periods.last
|
|
11
|
+
@reformed_cash_flow_stmt_2009 = cash_flow_stmt_2009.reformulated(period_2009)
|
|
12
|
+
|
|
13
|
+
google_2011_annual_rpt = "http://www.sec.gov/Archives/edgar/data/1288776/000119312512025336/0001193125-12-025336-index.htm"
|
|
14
|
+
filing = FinModeling::AnnualReportFiling.download google_2011_annual_rpt
|
|
15
|
+
|
|
16
|
+
@inc_stmt = filing.income_statement
|
|
17
|
+
@is_period = @inc_stmt.periods.last
|
|
18
|
+
@reformed_inc_stmt = @inc_stmt.reformulated(@is_period)
|
|
19
|
+
|
|
20
|
+
@cash_flow_stmt = filing.cash_flow_statement
|
|
21
|
+
@period = @cash_flow_stmt.periods.last
|
|
22
|
+
@reformed_cash_flow_stmt = @cash_flow_stmt.reformulated(@period)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
describe "new" do
|
|
26
|
+
it "takes a cash change calculation and a period and returns a CalculationSummary" do
|
|
27
|
+
rcfs = FinModeling::ReformulatedCashFlowStatement.new(@period, @cash_flow_stmt.cash_change_calculation.summary(:period=>@period))
|
|
28
|
+
rcfs.should be_an_instance_of FinModeling::ReformulatedCashFlowStatement
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
subject { @reformed_cash_flow_stmt }
|
|
33
|
+
|
|
34
|
+
describe "cash_from_operations" do
|
|
35
|
+
subject { @reformed_cash_flow_stmt.cash_from_operations }
|
|
36
|
+
it { should be_an_instance_of FinModeling::CalculationSummary }
|
|
37
|
+
it "totals up the values of rows with type :c" do
|
|
38
|
+
sum = @cash_flow_stmt.cash_change_calculation
|
|
39
|
+
.summary(:period=>@period)
|
|
40
|
+
.rows.select{ |row| row.type == :c }
|
|
41
|
+
.map{ |row| row.vals.first }
|
|
42
|
+
.inject(:+)
|
|
43
|
+
subject.total.should == sum
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
describe "cash_investments_in_operations" do
|
|
48
|
+
subject { @reformed_cash_flow_stmt.cash_investments_in_operations }
|
|
49
|
+
it { should be_an_instance_of FinModeling::CalculationSummary }
|
|
50
|
+
it "totals up the values of rows with type :i" do
|
|
51
|
+
sum = @cash_flow_stmt.cash_change_calculation
|
|
52
|
+
.summary(:period=>@period)
|
|
53
|
+
.rows.select{ |row| row.type == :i }
|
|
54
|
+
.map{ |row| row.vals.first }
|
|
55
|
+
.inject(:+)
|
|
56
|
+
subject.total.should == sum
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
describe "payments_to_debtholders" do
|
|
61
|
+
subject { @reformed_cash_flow_stmt.payments_to_debtholders }
|
|
62
|
+
it { should be_an_instance_of FinModeling::CalculationSummary }
|
|
63
|
+
it "totals up the values of rows with type :d, minus the total change in cash" do
|
|
64
|
+
sum = @cash_flow_stmt.cash_change_calculation
|
|
65
|
+
.summary(:period=>@period)
|
|
66
|
+
.rows.select{ |row| row.type == :d }
|
|
67
|
+
.map{ |row| row.vals.first }
|
|
68
|
+
.inject(:+)
|
|
69
|
+
sum = sum - @cash_flow_stmt.cash_change_calculation
|
|
70
|
+
.summary(:period=>@period)
|
|
71
|
+
.total
|
|
72
|
+
subject.total.should == sum
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
describe "payments_to_stockholders" do
|
|
77
|
+
subject { @reformed_cash_flow_stmt.payments_to_stockholders }
|
|
78
|
+
it { should be_an_instance_of FinModeling::CalculationSummary }
|
|
79
|
+
it "totals up the values of rows with type :f" do
|
|
80
|
+
sum = @cash_flow_stmt.cash_change_calculation
|
|
81
|
+
.summary(:period=>@period)
|
|
82
|
+
.rows.select{ |row| row.type == :f }
|
|
83
|
+
.map{ |row| row.vals.first }
|
|
84
|
+
.inject(:+)
|
|
85
|
+
subject.total.should == sum
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
describe "free_cash_flow" do
|
|
90
|
+
subject { @reformed_cash_flow_stmt.free_cash_flow }
|
|
91
|
+
it { should be_an_instance_of FinModeling::CalculationSummary }
|
|
92
|
+
it "totals up cash from operations and cash investments in operations" do
|
|
93
|
+
sum = @reformed_cash_flow_stmt.cash_from_operations.total
|
|
94
|
+
sum = sum + @reformed_cash_flow_stmt.cash_investments_in_operations.total
|
|
95
|
+
subject.total.should == sum
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
describe "ni_over_c" do
|
|
100
|
+
subject { @reformed_cash_flow_stmt.ni_over_c(@reformed_inc_stmt) }
|
|
101
|
+
it { should be_an_instance_of Float }
|
|
102
|
+
it { should be_within(0.1).of(@reformed_inc_stmt.comprehensive_income.total / @reformed_cash_flow_stmt.cash_from_operations.total) }
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
describe "financing_flows" do
|
|
106
|
+
subject { @reformed_cash_flow_stmt.financing_flows }
|
|
107
|
+
it { should be_an_instance_of FinModeling::CalculationSummary }
|
|
108
|
+
it "totals up payments to both debtholders and stockholders" do
|
|
109
|
+
sum = @reformed_cash_flow_stmt.payments_to_debtholders.total
|
|
110
|
+
sum = sum + @reformed_cash_flow_stmt.payments_to_stockholders.total
|
|
111
|
+
subject.total.should == sum
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
describe "analysis" do
|
|
116
|
+
subject { @reformed_cash_flow_stmt.analysis(@reformed_inc_stmt) }
|
|
117
|
+
|
|
118
|
+
it { should be_an_instance_of FinModeling::CalculationSummary }
|
|
119
|
+
it "contains the expected rows" do
|
|
120
|
+
expected_keys = [ "C ($MM)", "I ($MM)", "d ($MM)", "F ($MM)", "FCF ($MM)", "NI / C" ]
|
|
121
|
+
subject.rows.map{ |row| row.key }.should == expected_keys
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
describe "-" do
|
|
126
|
+
before(:all) do
|
|
127
|
+
google_2011_q3_rpt = "http://www.sec.gov/Archives/edgar/data/1288776/000119312511282235/0001193125-11-282235-index.htm"
|
|
128
|
+
@filing_2011_q3 = FinModeling::AnnualReportFiling.download google_2011_q3_rpt
|
|
129
|
+
|
|
130
|
+
@cash_flow_stmt_2011_q3 = @filing_2011_q3.cash_flow_statement
|
|
131
|
+
cfs_period_2011_q3 = @cash_flow_stmt_2011_q3.periods.threequarterly.last
|
|
132
|
+
@reformed_cash_flow_stmt_2011_q3 = @cash_flow_stmt_2011_q3.reformulated(cfs_period_2011_q3)
|
|
133
|
+
|
|
134
|
+
@diff = @reformed_cash_flow_stmt - @reformed_cash_flow_stmt_2011_q3
|
|
135
|
+
end
|
|
136
|
+
subject { @diff }
|
|
137
|
+
|
|
138
|
+
it { should be_an_instance_of FinModeling::ReformulatedCashFlowStatement }
|
|
139
|
+
its(:period) { should_not be_nil } # FIXME
|
|
140
|
+
|
|
141
|
+
it "returns the difference between the two re_cfs's for each calculation" do
|
|
142
|
+
methods = [ :cash_from_operations, :cash_investments_in_operations,
|
|
143
|
+
:payments_to_debtholders, :payments_to_stockholders,
|
|
144
|
+
:free_cash_flow, :financing_flows ]
|
|
145
|
+
methods.each do |method|
|
|
146
|
+
expected_val = @reformed_cash_flow_stmt.send(method).total - @reformed_cash_flow_stmt_2011_q3.send(method).total
|
|
147
|
+
@diff.send(method).total.should be_within(1.0).of(expected_val)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
it "returns values that are close to 1/4th of the annual value" do
|
|
152
|
+
methods = [ :cash_from_operations, :cash_investments_in_operations,
|
|
153
|
+
#:payments_to_debtholders, :payments_to_stockholders,
|
|
154
|
+
:free_cash_flow, :financing_flows ]
|
|
155
|
+
|
|
156
|
+
methods.each do |method|
|
|
157
|
+
orig = @reformed_cash_flow_stmt.send(method).total
|
|
158
|
+
max = (orig > 0) ? (0.35 * orig) : (0.15 * orig)
|
|
159
|
+
min = (orig > 0) ? (0.15 * orig) : (0.35 * orig)
|
|
160
|
+
actual = @diff.send(method).total
|
|
161
|
+
if (actual < min) || (actual > max)
|
|
162
|
+
err = "#{method} returns #{actual.to_nearest_thousand.to_s.with_thousands_separators}, "
|
|
163
|
+
err += "which is outside bounds: [#{min.to_nearest_thousand.to_s.with_thousands_separators}, "
|
|
164
|
+
err += "#{max.to_nearest_thousand.to_s.with_thousands_separators}]"
|
|
165
|
+
puts err
|
|
166
|
+
end
|
|
167
|
+
@diff.send(method).total.should be > min
|
|
168
|
+
@diff.send(method).total.should be < max
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
end
|
|
174
|
+
|