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,45 +1,37 @@
|
|
1
|
-
#
|
1
|
+
# liabs_and_equity_item_spec.rb
|
2
2
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
5
|
describe FinModeling::LiabsAndEquityItem do
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
describe ".new" do
|
8
|
+
subject { FinModeling::LiabsAndEquityItem.new("Accounts Payable Current") }
|
9
|
+
it { should be_a FinModeling::LiabsAndEquityItem }
|
9
10
|
end
|
10
11
|
|
11
|
-
describe "
|
12
|
-
it "takes a string and returns a new LiabsAndEquityItem" do
|
13
|
-
laei = FinModeling::LiabsAndEquityItem.new("Accounts Payable Current")
|
14
|
-
laei.should be_an_instance_of FinModeling::LiabsAndEquityItem
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
describe "train" do
|
12
|
+
describe ".train" do
|
19
13
|
it "trains the classifier that this LiabsAndEquityItem is of the given type" do
|
20
14
|
FinModeling::LiabsAndEquityItem.new("Accounts Payable Current").train(:ol)
|
21
15
|
end
|
22
16
|
end
|
23
17
|
|
24
|
-
describe "classification_estimates" do
|
25
|
-
|
26
|
-
laei = FinModeling::LiabsAndEquityItem.new("Accounts Payable Current")
|
18
|
+
describe ".classification_estimates" do
|
19
|
+
subject { FinModeling::LiabsAndEquityItem.new("Accounts Payable Current").classification_estimates }
|
27
20
|
|
28
|
-
|
29
|
-
|
30
|
-
end
|
31
|
-
end
|
21
|
+
it { should be_a Hash }
|
22
|
+
specify { subject.keys.sort == FinModeling::LiabsAndEquityItem::TYPES.sort }
|
32
23
|
end
|
33
24
|
|
34
|
-
describe "classify" do
|
25
|
+
describe ".classify" do
|
26
|
+
let(:laei) { FinModeling::LiabsAndEquityItem.new("Accounts Payable Current") }
|
27
|
+
subject { laei.classify }
|
35
28
|
it "returns the LiabsAndEquityItem type with the highest probability estimate" do
|
36
|
-
laei = FinModeling::LiabsAndEquityItem.new("Accounts Payable Current")
|
37
29
|
estimates = laei.classification_estimates
|
38
|
-
estimates[
|
30
|
+
estimates[subject].should be_within(0.1).of(estimates.values.max)
|
39
31
|
end
|
40
32
|
end
|
41
33
|
|
42
|
-
describe "load_vectors_and_train" do
|
34
|
+
describe ".load_vectors_and_train" do
|
43
35
|
# the before(:all) clause calls load_vectors_and_train already
|
44
36
|
# we can just focus, here, on its effects
|
45
37
|
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FinModeling::LinearTrendForecastingPolicy do
|
4
|
+
before (:all) do
|
5
|
+
@vals = { :revenue_estimator => FinModeling::TimeSeriesEstimator.new(0.04, 0.0),
|
6
|
+
:sales_pm_estimator => FinModeling::TimeSeriesEstimator.new(0.20, 0.0),
|
7
|
+
:fi_over_nfa_estimator => FinModeling::TimeSeriesEstimator.new(0.01, 0.0),
|
8
|
+
:sales_over_noa_estimator => FinModeling::TimeSeriesEstimator.new(2.00, 0.0) }
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:policy) { FinModeling::LinearTrendForecastingPolicy.new(@vals) }
|
12
|
+
let(:date) { Date.today }
|
13
|
+
|
14
|
+
describe ".revenue_on" do
|
15
|
+
subject { policy.revenue_on(date) }
|
16
|
+
it { should be_a Float }
|
17
|
+
it { should be_within(0.01).of(@vals[:revenue_estimator].a) }
|
18
|
+
end
|
19
|
+
|
20
|
+
describe ".sales_pm_on" do
|
21
|
+
subject { policy.sales_pm_on(date) }
|
22
|
+
it { should be_a Float }
|
23
|
+
it { should be_within(0.01).of(@vals[:sales_pm_estimator].a) }
|
24
|
+
end
|
25
|
+
|
26
|
+
describe ".fi_over_nfa_on" do
|
27
|
+
subject { policy.fi_over_nfa_on(date) }
|
28
|
+
it { should be_a Float }
|
29
|
+
it { should be_within(0.01).of(@vals[:fi_over_nfa_estimator].a) }
|
30
|
+
end
|
31
|
+
|
32
|
+
describe ".sales_over_noa_on" do
|
33
|
+
subject { policy.sales_over_noa_on(date) }
|
34
|
+
it { should be_a Float }
|
35
|
+
it { should be_within(0.01).of(@vals[:sales_over_noa_estimator].a) }
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
RSpec::Matchers.define :be_in do |expected|
|
2
|
+
match do |actual|
|
3
|
+
expected.include?(actual)
|
4
|
+
end
|
5
|
+
|
6
|
+
description { "be one of #{expected}" }
|
7
|
+
failure_message_for_should { |actual| "expected one of #{expected} but got '#{actual}'" }
|
8
|
+
failure_message_for_should_not { |actual| "expected other one of #{expected} but got '#{actual}'" }
|
9
|
+
end
|
10
|
+
|
11
|
+
RSpec::Matchers.define :have_the_same_periods_as do |expected|
|
12
|
+
match do |actual|
|
13
|
+
str1 = actual .periods.map{|x| x.to_pretty_s}.join(',')
|
14
|
+
str2 = expected.periods.map{|x| x.to_pretty_s}.join(',')
|
15
|
+
str1 == str2
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
RSpec::Matchers.define :have_a_plausible_total do
|
20
|
+
match do |actual|
|
21
|
+
actual.total.abs >= 1.0
|
22
|
+
end
|
23
|
+
|
24
|
+
description { "have a plausible total" }
|
25
|
+
failure_message_for_should { |actual| "expected that #{actual} would have a total with an absolute value > 1.0" }
|
26
|
+
failure_message_for_should_not { |actual| "expected that #{actual} would not have a total with an absolute value > 1.0" }
|
27
|
+
end
|
28
|
+
|
29
|
+
RSpec::Matchers.define :have_the_same_last_total_as do |expected|
|
30
|
+
match do |actual|
|
31
|
+
period = actual.periods.last
|
32
|
+
val1 = actual .summary(:period=>period).total
|
33
|
+
val2 = expected.summary(:period=>period).total
|
34
|
+
(val1 - val2).abs <= 0.1
|
35
|
+
end
|
36
|
+
|
37
|
+
description { "have the same last reformulated total as #{expected}" }
|
38
|
+
failure_message_for_should { |actual| "expected that #{actual} would have the same last total as #{expected}" }
|
39
|
+
failure_message_for_should_not { |actual| "expected that #{actual} would not have the same last total as #{expected}" }
|
40
|
+
end
|
41
|
+
|
42
|
+
RSpec::Matchers.define :have_the_same_last_total do |calc|
|
43
|
+
match do |actual|
|
44
|
+
a = actual .send(calc)
|
45
|
+
e = expected.send(calc)
|
46
|
+
|
47
|
+
period = a.periods.last
|
48
|
+
val1 = a.summary(:period=>period).total
|
49
|
+
val2 = e.summary(:period=>period).total
|
50
|
+
(val1 - val2).abs <= 0.1
|
51
|
+
end
|
52
|
+
chain :as do |expected|
|
53
|
+
@expected = expected
|
54
|
+
end
|
55
|
+
|
56
|
+
description { "have #{calc} with the same last total as #{@expected}" }
|
57
|
+
failure_message_for_should { |actual| "expected that #{actual}'s #{calc} would have the same last total as #{@expected}" }
|
58
|
+
failure_message_for_should_not { |actual| "expected that #{actual}'s #{calc} would not have the same last total as #{@expected}" }
|
59
|
+
end
|
60
|
+
|
61
|
+
RSpec::Matchers.define :have_the_same_reformulated_last_total do |calc|
|
62
|
+
match do |actual|
|
63
|
+
period = actual.periods.last
|
64
|
+
|
65
|
+
params = (actual.is_a? FinModeling::IncomeStatementCalculation) ? [period, ci_calc=nil] : [period]
|
66
|
+
|
67
|
+
val1 = actual .reformulated(*params).send(calc).total
|
68
|
+
val2 = expected.reformulated(*params).send(calc).total
|
69
|
+
(val1 - val2).abs <= 0.1
|
70
|
+
end
|
71
|
+
chain :as do |expected|
|
72
|
+
@expected = expected
|
73
|
+
end
|
74
|
+
|
75
|
+
description { "have the same last reformulated total as #{@expected}" }
|
76
|
+
failure_message_for_should { |actual| "expected that #{actual} would have the same last reformulated total as #{@expected}" }
|
77
|
+
failure_message_for_should_not { |actual| "expected that #{actual} would not have the same last reformulated total as #{@expected}" }
|
78
|
+
end
|
79
|
+
|
data/spec/mocks/calculation.rb
CHANGED
File without changes
|
File without changes
|
data/spec/mocks/sec_query.rb
CHANGED
File without changes
|
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# net_income_calculation_spec.rb
|
2
2
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
@@ -6,18 +6,24 @@ describe FinModeling::NetIncomeCalculation do
|
|
6
6
|
before(:all) do
|
7
7
|
google_2011_annual_rpt = "http://www.sec.gov/Archives/edgar/data/1288776/000119312512025336/0001193125-12-025336-index.htm"
|
8
8
|
filing = FinModeling::AnnualReportFiling.download google_2011_annual_rpt
|
9
|
-
@
|
10
|
-
@
|
11
|
-
@ni = @inc_stmt.net_income_calculation
|
9
|
+
@period = filing.income_statement.periods.last
|
10
|
+
@ni = filing.income_statement.net_income_calculation
|
12
11
|
end
|
13
12
|
|
14
|
-
describe "summary" do
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
FinModeling::IncomeStatementItem::TYPES.include?(@ni.summary(:period=>@period).rows.first.type).should be_true
|
13
|
+
describe ".summary" do
|
14
|
+
subject { @ni.summary(:period=>@period) }
|
15
|
+
it { should be_a FinModeling::CalculationSummary }
|
16
|
+
it "should tag each row with an Income Statement Type" do
|
17
|
+
subject.rows.first.type.should be_in(FinModeling::IncomeStatementItem::TYPES) # FIXME: seems weak.
|
20
18
|
end
|
21
19
|
end
|
20
|
+
|
21
|
+
describe ".has_revenue_item?" do
|
22
|
+
pending "Find a test case..."
|
23
|
+
end
|
24
|
+
|
25
|
+
describe ".has_tax_item?" do
|
26
|
+
pending "Find a test case..."
|
27
|
+
end
|
22
28
|
end
|
23
29
|
|
data/spec/period_array.rb
CHANGED
File without changes
|
@@ -6,22 +6,24 @@ describe FinModeling::QuarterlyReportFiling do
|
|
6
6
|
before(:all) do
|
7
7
|
company = FinModeling::Company.new(FinModeling::Mocks::Entity.new)
|
8
8
|
filing_url = company.quarterly_reports.last.link
|
9
|
-
FinModeling::Config::disable_caching
|
9
|
+
FinModeling::Config::disable_caching
|
10
10
|
@filing = FinModeling::QuarterlyReportFiling.download(filing_url)
|
11
|
-
|
12
|
-
|
13
|
-
after(:all) do
|
14
|
-
FinModeling::Config::enable_caching
|
11
|
+
FinModeling::Config::enable_caching
|
15
12
|
end
|
16
13
|
|
17
14
|
subject { @filing }
|
18
|
-
its(:balance_sheet)
|
19
|
-
its(:income_statement)
|
20
|
-
its(:cash_flow_statement)
|
21
|
-
|
22
|
-
|
15
|
+
its(:balance_sheet) { should be_a FinModeling::BalanceSheetCalculation }
|
16
|
+
its(:income_statement) { should be_a FinModeling::IncomeStatementCalculation }
|
17
|
+
its(:cash_flow_statement) { should be_a FinModeling::CashFlowStatementCalculation }
|
18
|
+
|
19
|
+
context "when the report doesn't have a statement of shareholders' equity" do
|
20
|
+
its(:has_a_shareholder_equity_statement?) { should be_false }
|
21
|
+
#its(:is_valid?) { should == [@filing.income_statement,
|
22
|
+
# @filing.balance_sheet,
|
23
|
+
# @filing.cash_flow_statement].all?{|x| x.is_valid?} } # FIXME: this is failing, but I'm not sure how I want it to work.
|
24
|
+
end
|
23
25
|
|
24
|
-
|
26
|
+
context "after write_constructor()ing it to a file and then eval()ing the results" do
|
25
27
|
before(:all) do
|
26
28
|
file_name = "/tmp/finmodeling-quarterly-rpt.rb"
|
27
29
|
schema_version_item_name = "@schema_version"
|
@@ -36,34 +38,15 @@ describe FinModeling::QuarterlyReportFiling do
|
|
36
38
|
@loaded_filing = eval(item_name)
|
37
39
|
end
|
38
40
|
|
39
|
-
it "writes itself to a file, and saves a schema version of 1.
|
40
|
-
@schema_version.should be == 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
|
41
|
+
it "writes itself to a file, and saves a schema version of 1.3" do
|
42
|
+
@schema_version.should be == 1.3
|
66
43
|
end
|
67
44
|
|
45
|
+
subject { @loaded_filing }
|
46
|
+
its(:balance_sheet) { should have_the_same_periods_as(@filing.balance_sheet) }
|
47
|
+
its(:balance_sheet) { should have_the_same_reformulated_last_total(:net_operating_assets).as(@filing.balance_sheet) }
|
48
|
+
its(:income_statement) { should have_the_same_reformulated_last_total(:net_financing_income).as(@filing.income_statement) }
|
49
|
+
its(:cash_flow_statement) { should have_the_same_last_total(:cash_change_calculation).as(@filing.cash_flow_statement) }
|
50
|
+
its(:disclosures) { should have_the_same_last_total(:first).as(@filing.disclosures) }
|
68
51
|
end
|
69
52
|
end
|
data/spec/rate_spec.rb
CHANGED
File without changes
|
data/spec/ratio_spec.rb
CHANGED
File without changes
|
@@ -20,61 +20,81 @@ describe FinModeling::ReformulatedBalanceSheet do
|
|
20
20
|
@years_between_sheets = 2.0
|
21
21
|
end
|
22
22
|
|
23
|
-
describe "new" do
|
23
|
+
describe ".new" do
|
24
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,
|
26
|
-
|
25
|
+
rbs = FinModeling::ReformulatedBalanceSheet.new(@period,
|
26
|
+
@bal_sheet.assets_calculation .summary(:period=>@period),
|
27
|
+
@bal_sheet.liabs_and_equity_calculation.summary(:period=>@period))
|
28
|
+
rbs.should be_a FinModeling::ReformulatedBalanceSheet
|
27
29
|
end
|
28
30
|
end
|
29
31
|
|
30
|
-
describe "operating_assets" do
|
32
|
+
describe ".operating_assets" do
|
31
33
|
subject { @reformed_bal_sheet.operating_assets }
|
32
|
-
it { should
|
33
|
-
|
34
|
+
it { should be_a FinModeling::CalculationSummary }
|
35
|
+
let(:total_assets) { @bal_sheet.assets_calculation.summary(:period=>@period).total }
|
36
|
+
its(:total) { should be_within(0.1).of(total_assets - @reformed_bal_sheet.financial_assets.total) }
|
34
37
|
end
|
35
38
|
|
36
|
-
describe "financial_assets" do
|
39
|
+
describe ".financial_assets" do
|
37
40
|
subject { @reformed_bal_sheet.financial_assets }
|
38
|
-
it { should
|
39
|
-
|
41
|
+
it { should be_a FinModeling::CalculationSummary }
|
42
|
+
let(:total_assets) { @bal_sheet.assets_calculation.summary(:period=>@period).total }
|
43
|
+
its(:total) { should be_within(0.1).of(total_assets - @reformed_bal_sheet.operating_assets.total) }
|
40
44
|
end
|
41
45
|
|
42
|
-
describe "operating_liabilities" do
|
46
|
+
describe ".operating_liabilities" do
|
43
47
|
subject { @reformed_bal_sheet.operating_liabilities }
|
44
|
-
it { should
|
45
|
-
|
48
|
+
it { should be_a FinModeling::CalculationSummary }
|
49
|
+
let(:total_equity) { @reformed_bal_sheet.common_shareholders_equity.total }
|
50
|
+
let(:total_l_and_e) { @bal_sheet.liabs_and_equity_calculation.summary(:period=>@period).total }
|
51
|
+
let(:total_liabs) { total_l_and_e - total_equity }
|
52
|
+
its(:total) { should be_within(0.1).of(total_liabs - @reformed_bal_sheet.financial_liabilities.total) }
|
46
53
|
end
|
47
54
|
|
48
|
-
describe "financial_liabilities" do
|
55
|
+
describe ".financial_liabilities" do
|
49
56
|
subject { @reformed_bal_sheet.financial_liabilities }
|
50
|
-
it { should
|
51
|
-
|
57
|
+
it { should be_a FinModeling::CalculationSummary }
|
58
|
+
let(:total_equity) { @reformed_bal_sheet.common_shareholders_equity.total }
|
59
|
+
let(:total_l_and_e) { @bal_sheet.liabs_and_equity_calculation.summary(:period=>@period).total }
|
60
|
+
let(:total_liabs) { total_l_and_e - total_equity }
|
61
|
+
its(:total) { should be_within(0.1).of(total_liabs - @reformed_bal_sheet.operating_liabilities.total) }
|
52
62
|
end
|
53
63
|
|
54
|
-
describe "net_operating_assets" do
|
64
|
+
describe ".net_operating_assets" do
|
55
65
|
subject { @reformed_bal_sheet.net_operating_assets }
|
56
|
-
it { should
|
57
|
-
its(:total) { should be_within(0.1).of(
|
66
|
+
it { should be_a FinModeling::CalculationSummary }
|
67
|
+
its(:total) { should be_within(0.1).of(@reformed_bal_sheet.operating_assets.total - @reformed_bal_sheet.operating_liabilities.total) }
|
58
68
|
end
|
59
69
|
|
60
|
-
describe "net_financial_assets" do
|
70
|
+
describe ".net_financial_assets" do
|
61
71
|
subject { @reformed_bal_sheet.net_financial_assets }
|
62
|
-
it { should
|
63
|
-
its(:total) { should be_within(0.1).of(
|
72
|
+
it { should be_a FinModeling::CalculationSummary }
|
73
|
+
its(:total) { should be_within(0.1).of(@reformed_bal_sheet.financial_assets.total - @reformed_bal_sheet.financial_liabilities.total) }
|
64
74
|
end
|
65
75
|
|
66
|
-
describe "
|
76
|
+
describe ".minority_interest" do
|
77
|
+
subject { @reformed_bal_sheet.minority_interest }
|
78
|
+
it { should be_a FinModeling::CalculationSummary }
|
79
|
+
its(:total) { should be_within(0.1).of(@reformed_bal_sheet.net_operating_assets.total +
|
80
|
+
@reformed_bal_sheet.net_financial_assets.total -
|
81
|
+
@reformed_bal_sheet.common_shareholders_equity.total) }
|
82
|
+
end
|
83
|
+
|
84
|
+
describe ".common_shareholders_equity" do
|
67
85
|
subject { @reformed_bal_sheet.common_shareholders_equity }
|
68
|
-
it { should
|
69
|
-
its(:total) { should be_within(0.1).of(
|
86
|
+
it { should be_a FinModeling::CalculationSummary }
|
87
|
+
its(:total) { should be_within(0.1).of(@reformed_bal_sheet.net_operating_assets.total +
|
88
|
+
@reformed_bal_sheet.net_financial_assets.total -
|
89
|
+
@reformed_bal_sheet.minority_interest.total) }
|
70
90
|
end
|
71
91
|
|
72
|
-
describe "composition_ratio" do
|
92
|
+
describe ".composition_ratio" do
|
73
93
|
subject { @reformed_bal_sheet.composition_ratio }
|
74
94
|
it { should be_within(0.1).of(@reformed_bal_sheet.net_operating_assets.total / @reformed_bal_sheet.net_financial_assets.total) }
|
75
95
|
end
|
76
96
|
|
77
|
-
describe "noa_growth" do
|
97
|
+
describe ".noa_growth" do
|
78
98
|
subject { @reformed_bal_sheet.noa_growth(@prev_reformed_bal_sheet) }
|
79
99
|
let(:noa0) { @prev_reformed_bal_sheet.net_operating_assets.total }
|
80
100
|
let(:noa1) { @reformed_bal_sheet.net_operating_assets.total }
|
@@ -82,7 +102,7 @@ describe FinModeling::ReformulatedBalanceSheet do
|
|
82
102
|
it { should be_within(0.001).of(expected_growth) }
|
83
103
|
end
|
84
104
|
|
85
|
-
describe "cse_growth" do
|
105
|
+
describe ".cse_growth" do
|
86
106
|
subject { @reformed_bal_sheet.cse_growth(@prev_reformed_bal_sheet) }
|
87
107
|
let(:cse0) { @prev_reformed_bal_sheet.common_shareholders_equity.total }
|
88
108
|
let(:cse1) { @reformed_bal_sheet.common_shareholders_equity.total }
|
@@ -90,11 +110,11 @@ describe FinModeling::ReformulatedBalanceSheet do
|
|
90
110
|
it { should be_within(0.001).of(expected_growth) }
|
91
111
|
end
|
92
112
|
|
93
|
-
describe "analysis" do
|
113
|
+
describe ".analysis" do
|
94
114
|
subject {@reformed_bal_sheet.analysis(@prev_reformed_bal_sheet) }
|
95
|
-
it { should
|
115
|
+
it { should be_a FinModeling::CalculationSummary }
|
96
116
|
it "contains the expected rows" do
|
97
|
-
expected_keys = ["NOA ($MM)", "NFA ($MM)", "CSE ($MM)",
|
117
|
+
expected_keys = ["NOA ($MM)", "NFA ($MM)", "Minority Interest ($MM)", "CSE ($MM)",
|
98
118
|
"Composition Ratio", "NOA Growth", "CSE Growth" ]
|
99
119
|
|
100
120
|
subject.rows.map{ |row| row.key }.should == expected_keys
|
@@ -105,7 +125,7 @@ describe FinModeling::ReformulatedBalanceSheet do
|
|
105
125
|
before (:all) do
|
106
126
|
@company = FinModeling::Company.find("aapl")
|
107
127
|
@filings = FinModeling::CompanyFilings.new(@company.filings_since_date(Time.parse("2010-10-01")))
|
108
|
-
@policy = FinModeling::GenericForecastingPolicy.new
|
128
|
+
@policy = FinModeling::GenericForecastingPolicy.new(:operating_revenues=>@filings.last.income_statement.latest_quarterly_reformulated(nil, nil, nil).operating_revenues.total)
|
109
129
|
|
110
130
|
prev_bs_period = @filings.last.balance_sheet.periods.last
|
111
131
|
next_bs_period_value = prev_bs_period.value.next_month.next_month.next_month
|
@@ -116,7 +136,7 @@ describe FinModeling::ReformulatedBalanceSheet do
|
|
116
136
|
@next_is_period = Xbrlware::Context::Period.new(next_is_period_value)
|
117
137
|
end
|
118
138
|
|
119
|
-
let(:last_re_is) { @filings.last.income_statement.latest_quarterly_reformulated(nil) }
|
139
|
+
let(:last_re_is) { @filings.last.income_statement.latest_quarterly_reformulated(ci_calc=nil, prev_is=nil, prev_ci_calc=nil) }
|
120
140
|
let(:last_re_bs) { @filings.last.balance_sheet.reformulated(@filings.last.balance_sheet.periods.last) }
|
121
141
|
let(:next_re_is) { FinModeling::ReformulatedIncomeStatement.forecast_next(@next_is_period, @policy, last_re_bs, last_re_is) }
|
122
142
|
|
@@ -126,8 +146,11 @@ describe FinModeling::ReformulatedBalanceSheet do
|
|
126
146
|
it "should have the given period" do
|
127
147
|
subject.period.to_pretty_s == @next_bs_period.to_pretty_s
|
128
148
|
end
|
149
|
+
it "should set minority interest to ...?" do
|
150
|
+
pending "not sure how to treat this"
|
151
|
+
end
|
129
152
|
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.
|
153
|
+
expected_val = next_re_is.operating_revenues.total / FinModeling::Ratio.new(@policy.sales_over_noa_on(@next_bs_period.value)).yearly_to_quarterly
|
131
154
|
subject.net_operating_assets.total.should == expected_val
|
132
155
|
end
|
133
156
|
it "should set CSE to last year's CSE plus this year's net income" do
|
@@ -15,14 +15,14 @@ describe FinModeling::ReformulatedCashFlowStatement do
|
|
15
15
|
|
16
16
|
@inc_stmt = filing.income_statement
|
17
17
|
@is_period = @inc_stmt.periods.last
|
18
|
-
@reformed_inc_stmt = @inc_stmt.reformulated(@is_period)
|
18
|
+
@reformed_inc_stmt = @inc_stmt.reformulated(@is_period, ci_calc=nil)
|
19
19
|
|
20
20
|
@cash_flow_stmt = filing.cash_flow_statement
|
21
21
|
@period = @cash_flow_stmt.periods.last
|
22
22
|
@reformed_cash_flow_stmt = @cash_flow_stmt.reformulated(@period)
|
23
23
|
end
|
24
24
|
|
25
|
-
describe "new" do
|
25
|
+
describe ".new" do
|
26
26
|
it "takes a cash change calculation and a period and returns a CalculationSummary" do
|
27
27
|
rcfs = FinModeling::ReformulatedCashFlowStatement.new(@period, @cash_flow_stmt.cash_change_calculation.summary(:period=>@period))
|
28
28
|
rcfs.should be_an_instance_of FinModeling::ReformulatedCashFlowStatement
|
@@ -31,7 +31,7 @@ describe FinModeling::ReformulatedCashFlowStatement do
|
|
31
31
|
|
32
32
|
subject { @reformed_cash_flow_stmt }
|
33
33
|
|
34
|
-
describe "cash_from_operations" do
|
34
|
+
describe ".cash_from_operations" do
|
35
35
|
subject { @reformed_cash_flow_stmt.cash_from_operations }
|
36
36
|
it { should be_an_instance_of FinModeling::CalculationSummary }
|
37
37
|
it "totals up the values of rows with type :c" do
|
@@ -44,7 +44,7 @@ describe FinModeling::ReformulatedCashFlowStatement do
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
-
describe "cash_investments_in_operations" do
|
47
|
+
describe ".cash_investments_in_operations" do
|
48
48
|
subject { @reformed_cash_flow_stmt.cash_investments_in_operations }
|
49
49
|
it { should be_an_instance_of FinModeling::CalculationSummary }
|
50
50
|
it "totals up the values of rows with type :i" do
|
@@ -57,7 +57,7 @@ describe FinModeling::ReformulatedCashFlowStatement do
|
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
60
|
-
describe "payments_to_debtholders" do
|
60
|
+
describe ".payments_to_debtholders" do
|
61
61
|
subject { @reformed_cash_flow_stmt.payments_to_debtholders }
|
62
62
|
it { should be_an_instance_of FinModeling::CalculationSummary }
|
63
63
|
it "totals up the values of rows with type :d, minus the total change in cash" do
|
@@ -73,7 +73,7 @@ describe FinModeling::ReformulatedCashFlowStatement do
|
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
|
-
describe "payments_to_stockholders" do
|
76
|
+
describe ".payments_to_stockholders" do
|
77
77
|
subject { @reformed_cash_flow_stmt.payments_to_stockholders }
|
78
78
|
it { should be_an_instance_of FinModeling::CalculationSummary }
|
79
79
|
it "totals up the values of rows with type :f" do
|
@@ -86,7 +86,7 @@ describe FinModeling::ReformulatedCashFlowStatement do
|
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
89
|
-
describe "free_cash_flow" do
|
89
|
+
describe ".free_cash_flow" do
|
90
90
|
subject { @reformed_cash_flow_stmt.free_cash_flow }
|
91
91
|
it { should be_an_instance_of FinModeling::CalculationSummary }
|
92
92
|
it "totals up cash from operations and cash investments in operations" do
|
@@ -96,13 +96,13 @@ describe FinModeling::ReformulatedCashFlowStatement do
|
|
96
96
|
end
|
97
97
|
end
|
98
98
|
|
99
|
-
describe "ni_over_c" do
|
99
|
+
describe ".ni_over_c" do
|
100
100
|
subject { @reformed_cash_flow_stmt.ni_over_c(@reformed_inc_stmt) }
|
101
101
|
it { should be_an_instance_of Float }
|
102
102
|
it { should be_within(0.1).of(@reformed_inc_stmt.comprehensive_income.total / @reformed_cash_flow_stmt.cash_from_operations.total) }
|
103
103
|
end
|
104
104
|
|
105
|
-
describe "financing_flows" do
|
105
|
+
describe ".financing_flows" do
|
106
106
|
subject { @reformed_cash_flow_stmt.financing_flows }
|
107
107
|
it { should be_an_instance_of FinModeling::CalculationSummary }
|
108
108
|
it "totals up payments to both debtholders and stockholders" do
|
@@ -112,7 +112,7 @@ describe FinModeling::ReformulatedCashFlowStatement do
|
|
112
112
|
end
|
113
113
|
end
|
114
114
|
|
115
|
-
describe "analysis" do
|
115
|
+
describe ".analysis" do
|
116
116
|
subject { @reformed_cash_flow_stmt.analysis(@reformed_inc_stmt) }
|
117
117
|
|
118
118
|
it { should be_an_instance_of FinModeling::CalculationSummary }
|
@@ -170,5 +170,13 @@ describe FinModeling::ReformulatedCashFlowStatement do
|
|
170
170
|
end
|
171
171
|
end
|
172
172
|
|
173
|
+
describe ".flows_are_balanced?" do
|
174
|
+
pending "Find examples..."
|
175
|
+
end
|
176
|
+
|
177
|
+
describe ".flows_are_plausible?" do
|
178
|
+
pending "Find examples..."
|
179
|
+
end
|
180
|
+
|
173
181
|
end
|
174
182
|
|