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.
Files changed (135) hide show
  1. data/.gitignore +0 -0
  2. data/Gemfile +2 -0
  3. data/README.md +289 -269
  4. data/Rakefile +12 -0
  5. data/TODO.txt +113 -20
  6. data/examples/{dump_report.rb → dump_latest_10k.rb} +1 -1
  7. data/examples/list_disclosures.rb +50 -0
  8. data/examples/lists/nasdaq-mid-to-mega-tech-symbols.txt +0 -0
  9. data/examples/show_report.rb +112 -32
  10. data/examples/show_reports.rb +162 -33
  11. data/finmodeling.gemspec +4 -1
  12. data/lib/finmodeling/annual_report_filing.rb +97 -18
  13. data/lib/finmodeling/array_with_stats.rb +0 -0
  14. data/lib/finmodeling/assets_calculation.rb +12 -3
  15. data/lib/finmodeling/assets_item.rb +0 -0
  16. data/lib/finmodeling/assets_item_vectors.rb +0 -0
  17. data/lib/finmodeling/balance_sheet_analyses.rb +19 -4
  18. data/lib/finmodeling/balance_sheet_calculation.rb +52 -37
  19. data/lib/finmodeling/calculation_summary.rb +119 -14
  20. data/lib/finmodeling/can_cache_classifications.rb +0 -0
  21. data/lib/finmodeling/can_cache_summaries.rb +0 -0
  22. data/lib/finmodeling/can_choose_successive_periods.rb +15 -0
  23. data/lib/finmodeling/can_classify_rows.rb +0 -0
  24. data/lib/finmodeling/capm.rb +80 -0
  25. data/lib/finmodeling/cash_change_calculation.rb +3 -3
  26. data/lib/finmodeling/cash_change_item.rb +0 -0
  27. data/lib/finmodeling/cash_change_item_vectors.rb +0 -0
  28. data/lib/finmodeling/cash_change_summary_from_differences.rb +36 -0
  29. data/lib/finmodeling/cash_flow_statement_analyses.rb +36 -0
  30. data/lib/finmodeling/cash_flow_statement_calculation.rb +28 -52
  31. data/lib/finmodeling/classifiers.rb +2 -0
  32. data/lib/finmodeling/company.rb +0 -0
  33. data/lib/finmodeling/company_filing.rb +30 -7
  34. data/lib/finmodeling/company_filing_calculation.rb +16 -6
  35. data/lib/finmodeling/company_filings.rb +112 -46
  36. data/lib/finmodeling/comprehensive_income_calculation.rb +60 -0
  37. data/lib/finmodeling/comprehensive_income_statement_calculation.rb +74 -0
  38. data/lib/finmodeling/comprehensive_income_statement_item.rb +20 -0
  39. data/lib/finmodeling/comprehensive_income_statement_item_vectors.rb +235 -0
  40. data/lib/finmodeling/config.rb +0 -0
  41. data/lib/finmodeling/debt_cost_of_capital.rb +14 -0
  42. data/lib/finmodeling/equity_change_calculation.rb +43 -0
  43. data/lib/finmodeling/equity_change_item.rb +25 -0
  44. data/lib/finmodeling/equity_change_item_vectors.rb +156 -0
  45. data/lib/finmodeling/factory.rb +0 -0
  46. data/lib/finmodeling/fama_french_cost_of_equity.rb +119 -0
  47. data/lib/finmodeling/float_helpers.rb +14 -8
  48. data/lib/finmodeling/forecasted_reformulated_balance_sheet.rb +55 -0
  49. data/lib/finmodeling/forecasted_reformulated_income_statement.rb +110 -0
  50. data/lib/finmodeling/forecasts.rb +4 -4
  51. data/lib/finmodeling/has_string_classifer.rb +0 -0
  52. data/lib/finmodeling/income_statement_analyses.rb +23 -17
  53. data/lib/finmodeling/income_statement_calculation.rb +46 -43
  54. data/lib/finmodeling/income_statement_item.rb +1 -1
  55. data/lib/finmodeling/income_statement_item_vectors.rb +24 -13
  56. data/lib/finmodeling/invalid_filing_error.rb +4 -0
  57. data/lib/finmodeling/liabs_and_equity_calculation.rb +18 -8
  58. data/lib/finmodeling/liabs_and_equity_item.rb +1 -1
  59. data/lib/finmodeling/liabs_and_equity_item_vectors.rb +24 -24
  60. data/lib/finmodeling/linear_trend_forecasting_policy.rb +23 -0
  61. data/lib/finmodeling/net_income_calculation.rb +23 -10
  62. data/lib/finmodeling/net_income_summary_from_differences.rb +51 -0
  63. data/lib/finmodeling/paths.rb +0 -0
  64. data/lib/finmodeling/period_array.rb +8 -4
  65. data/lib/finmodeling/quarterly_report_filing.rb +9 -4
  66. data/lib/finmodeling/rate.rb +8 -0
  67. data/lib/finmodeling/ratio.rb +0 -0
  68. data/lib/finmodeling/reformulated_balance_sheet.rb +47 -88
  69. data/lib/finmodeling/reformulated_cash_flow_statement.rb +18 -41
  70. data/lib/finmodeling/reformulated_income_statement.rb +44 -206
  71. data/lib/finmodeling/reformulated_shareholder_equity_statement.rb +50 -0
  72. data/lib/finmodeling/reoi_valuation.rb +104 -0
  73. data/lib/finmodeling/shareholder_equity_statement_calculation.rb +34 -0
  74. data/lib/finmodeling/string_helpers.rb +18 -1
  75. data/lib/finmodeling/time_series_estimator.rb +25 -0
  76. data/lib/finmodeling/trailing_avg_forecasting_policy.rb +23 -0
  77. data/lib/finmodeling/version.rb +1 -1
  78. data/lib/finmodeling/weighted_avg_cost_of_capital.rb +35 -0
  79. data/lib/finmodeling/yahoo_finance_helpers.rb +20 -0
  80. data/lib/finmodeling.rb +33 -2
  81. data/spec/annual_report_filing_spec.rb +81 -45
  82. data/spec/assets_calculation_spec.rb +7 -4
  83. data/spec/assets_item_spec.rb +9 -14
  84. data/spec/balance_sheet_analyses_spec.rb +13 -13
  85. data/spec/balance_sheet_calculation_spec.rb +45 -51
  86. data/spec/calculation_summary_spec.rb +113 -21
  87. data/spec/can_classify_rows_spec.rb +0 -0
  88. data/spec/cash_change_calculation_spec.rb +1 -10
  89. data/spec/cash_change_item_spec.rb +10 -18
  90. data/spec/cash_flow_statement_calculation_spec.rb +10 -24
  91. data/spec/company_beta_spec.rb +53 -0
  92. data/spec/company_filing_calculation_spec.rb +39 -49
  93. data/spec/company_filing_spec.rb +0 -0
  94. data/spec/company_filings_spec.rb +75 -25
  95. data/spec/company_spec.rb +37 -47
  96. data/spec/comprehensive_income_statement_calculation_spec.rb +54 -0
  97. data/spec/comprehensive_income_statement_item_spec.rb +56 -0
  98. data/spec/debt_cost_of_capital_spec.rb +19 -0
  99. data/spec/equity_change_calculation_spec.rb +33 -0
  100. data/spec/equity_change_item_spec.rb +58 -0
  101. data/spec/factory_spec.rb +2 -2
  102. data/spec/forecasts_spec.rb +2 -2
  103. data/spec/income_statement_analyses_spec.rb +23 -21
  104. data/spec/income_statement_calculation_spec.rb +17 -49
  105. data/spec/income_statement_item_spec.rb +17 -29
  106. data/spec/liabs_and_equity_calculation_spec.rb +6 -3
  107. data/spec/liabs_and_equity_item_spec.rb +14 -22
  108. data/spec/linear_trend_forecasting_policy_spec.rb +37 -0
  109. data/spec/matchers/custom_matchers.rb +79 -0
  110. data/spec/mocks/calculation.rb +0 -0
  111. data/spec/mocks/income_statement_analyses.rb +0 -0
  112. data/spec/mocks/sec_query.rb +0 -0
  113. data/spec/net_income_calculation_spec.rb +16 -10
  114. data/spec/period_array.rb +0 -0
  115. data/spec/quarterly_report_filing_spec.rb +21 -38
  116. data/spec/rate_spec.rb +0 -0
  117. data/spec/ratio_spec.rb +0 -0
  118. data/spec/reformulated_balance_sheet_spec.rb +56 -33
  119. data/spec/reformulated_cash_flow_statement_spec.rb +18 -10
  120. data/spec/reformulated_income_statement_spec.rb +16 -15
  121. data/spec/reformulated_shareholder_equity_statement_spec.rb +43 -0
  122. data/spec/reoi_valuation_spec.rb +146 -0
  123. data/spec/shareholder_equity_statement_calculation_spec.rb +59 -0
  124. data/spec/spec_helper.rb +4 -1
  125. data/spec/string_helpers_spec.rb +15 -13
  126. data/spec/time_series_estimator_spec.rb +61 -0
  127. data/spec/trailing_avg_forecasting_policy_spec.rb +37 -0
  128. data/spec/weighted_avg_cost_of_capital_spec.rb +32 -0
  129. data/tools/create_equity_change_training_vectors.rb +49 -0
  130. data/tools/time_specs.sh +7 -0
  131. metadata +182 -36
  132. data/lib/finmodeling/constant_forecasting_policy.rb +0 -23
  133. data/lib/finmodeling/generic_forecasting_policy.rb +0 -19
  134. data/spec/constant_forecasting_policy_spec.rb +0 -37
  135. data/spec/generic_forecasting_policy_spec.rb +0 -33
data/spec/company_spec.rb CHANGED
@@ -3,71 +3,61 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe FinModeling::Company do
6
- before(:each) do
7
- end
8
-
9
6
  describe "initialize" do
10
- it "takes a SecQuery::Entity and creates a new company" do
11
- entity = SecQuery::Entity.find("aapl", {:relationships=>false, :transactions=>false, :filings=>true})
12
- FinModeling::Company.new(entity).should be_an_instance_of FinModeling::Company
13
- end
7
+ let(:entity) { SecQuery::Entity.find("aapl", {:relationships=>false, :transactions=>false, :filings=>true}) }
8
+ subject { FinModeling::Company.new(entity) }
9
+ it { should be_a FinModeling::Company }
14
10
  end
15
11
 
16
12
  describe "find" do
17
- it "looks up a company by its stock ticker" do
18
- FinModeling::Company.find("aapl").should be_an_instance_of FinModeling::Company
13
+ context "when given a valid stock ticker" do
14
+ subject { FinModeling::Company.find("aapl") }
15
+ it { should be_a FinModeling::Company }
19
16
  end
20
- it "returns nil if the stock symbol is invalid" do
21
- FinModeling::Company.find("bogus symbol").should be_nil
17
+ context "when given a bogus stock ticker" do
18
+ subject { FinModeling::Company.find("bogus symbol") }
19
+ it { should be_nil }
22
20
  end
23
21
  end
24
22
 
23
+ let(:company) { FinModeling::Company.find("aapl") }
24
+
25
25
  describe "name" do
26
- it "returns the name of the company" do
27
- c = FinModeling::Company.find("aapl")
28
- c.name.should == "APPLE INC"
29
- end
26
+ subject { company.name }
27
+ it { should == "APPLE INC" }
30
28
  end
31
29
 
32
30
  describe "annual_reports" do
33
- before(:all) do
34
- @company = FinModeling::Company.find "aapl"
35
- end
36
- it "returns a CompanyFilings object " do
37
- @company.annual_reports.should be_an_instance_of FinModeling::CompanyFilings
38
- end
39
- it "returns an array of 10-K filings" do
40
- @company.annual_reports.last.term.should == "10-K"
41
- end
31
+ subject { company.annual_reports }
32
+ it { should be_a FinModeling::CompanyFilings }
33
+ specify { subject.all?{ |report| report.term == "10-K" }.should be_true }
42
34
  end
43
35
 
44
36
  describe "quarterly_reports" do
45
- before(:all) do
46
- @company = FinModeling::Company.find "aapl"
47
- end
48
- it "returns a CompanyFilings object " do
49
- @company.quarterly_reports.should be_an_instance_of FinModeling::CompanyFilings
50
- end
51
- it "returns an array of 10-Q filings" do
52
- @company.quarterly_reports.last.term.should == "10-Q"
53
- end
37
+ subject { company.quarterly_reports }
38
+ it { should be_a FinModeling::CompanyFilings }
39
+ specify { subject.all?{ |report| report.term == "10-Q" }.should be_true }
54
40
  end
55
41
 
56
42
  describe "filings_since_date" do
57
- before(:all) do
58
- @company = FinModeling::Company.find "aapl"
59
- end
60
- it "returns a CompanyFilings object " do
61
- @company.filings_since_date(Time.parse("2010-01-01")).should be_an_instance_of FinModeling::CompanyFilings
62
- end
63
- it "returns an array of 10-Q and/or 10-K filings filed after the given date" do
64
- @company.filings_since_date(Time.parse("1994-01-01")).length.should == 11
65
- end
66
- it "returns an array of 10-Q and/or 10-K filings filed after the given date" do
67
- @company.filings_since_date(Time.parse("2010-01-01")).length.should == 9
68
- end
69
- it "returns an array of 10-Q and/or 10-K filings filed after the given date" do
70
- @company.filings_since_date(Time.parse("2011-01-01")).length.should == 5
43
+ let(:start_date) { Time.parse("1994-01-01") }
44
+ subject { company.filings_since_date(start_date) }
45
+ it { should be_a FinModeling::CompanyFilings }
46
+
47
+ context "when start date is 1994" do
48
+ let(:start_date) { Time.parse("1994-01-01") }
49
+ subject { company.filings_since_date(start_date) }
50
+ it { should have_at_least(11).items }
51
+ end
52
+ context "when start date is 2010" do
53
+ let(:start_date) { Time.parse("2010-01-01") }
54
+ subject { company.filings_since_date(start_date) }
55
+ it { should have_at_least( 9).items }
56
+ end
57
+ context "when start date is 2011" do
58
+ let(:start_date) { Time.parse("2011-01-01") }
59
+ subject { company.filings_since_date(start_date) }
60
+ it { should have_at_least( 5).items }
71
61
  end
72
62
  end
73
63
  end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ describe FinModeling::ComprehensiveIncomeStatementCalculation do
4
+ before(:all) do
5
+ xray_2012_q2_rpt = "http://www.sec.gov/Archives/edgar/data/1288776/000119312511282235/0001193125-11-282235-index.htm"
6
+ filing_q2 = FinModeling::AnnualReportFiling.download xray_2012_q2_rpt
7
+ @prev_ci_stmt = filing_q2.comprehensive_income_statement
8
+
9
+ xray_2012_q3_rpt = "http://www.sec.gov/Archives/edgar/data/818479/000081847912000023/0000818479-12-000023-index.htm"
10
+ filing = FinModeling::AnnualReportFiling.download xray_2012_q3_rpt
11
+ @ci_stmt = filing.comprehensive_income_statement
12
+ @period = @ci_stmt.periods.last
13
+ end
14
+
15
+ describe ".comprehensive_income_calculation" do
16
+ subject { @ci_stmt.comprehensive_income_calculation }
17
+ it { should be_a FinModeling::ComprehensiveIncomeCalculation }
18
+ its(:label) { should match /comprehensive.*income/i }
19
+ end
20
+
21
+ describe ".is_valid?" do
22
+ subject { @ci_stmt.is_valid? }
23
+ it { should == (@ci_stmt.comprehensive_income_calculation.has_net_income_item? || @ci_stmt.comprehensive_income_calculation.has_revenue_item?) }
24
+ end
25
+
26
+ describe ".reformulated" do
27
+ subject { @ci_stmt.reformulated(@period, ci_calc=nil) }
28
+ it { should be_a FinModeling::ReformulatedIncomeStatement }
29
+ end
30
+
31
+ describe ".latest_quarterly_reformulated" do
32
+ subject{ @ci_stmt.latest_quarterly_reformulated(@ci_stmt, @prev_ci_stmt, @prev_ci_calc) }
33
+ it { should be_a FinModeling::ReformulatedIncomeStatement }
34
+ end
35
+
36
+ describe ".write_constructor" do
37
+ before(:all) do
38
+ file_name = "/tmp/finmodeling-ci-stmt.rb"
39
+ item_name = "@ci_stmt"
40
+ file = File.open(file_name, "w")
41
+ @ci_stmt.write_constructor(file, item_name)
42
+ file.close
43
+
44
+ eval(File.read(file_name))
45
+ @loaded_cis = eval(item_name)
46
+ end
47
+
48
+ subject { @loaded_cis }
49
+ it { should have_the_same_periods_as(@ci_stmt) }
50
+ #it { should have_the_same_reformulated_last_total(:net_financing_income).as(@ci_stmt) }
51
+ end
52
+
53
+ end
54
+
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+
3
+ describe FinModeling::ComprehensiveIncomeStatementItem do
4
+
5
+ describe "new" do
6
+ subject { FinModeling::ComprehensiveIncomeStatementItem.new("Comprehensive Income Net Of Tax Attributable To Noncontrolling Interest") }
7
+ it { should be_a FinModeling::ComprehensiveIncomeStatementItem }
8
+ end
9
+
10
+ describe "train" do
11
+ let(:item) { FinModeling::ComprehensiveIncomeStatementItem.new("Comprehensive Income Net Of Tax Attributable To Noncontrolling Interest") }
12
+ it "trains the classifier that this ComprehensiveIncomeStatementItem is of the given type" do
13
+ item.train(:ooci_nci)
14
+ end
15
+ end
16
+
17
+ describe "classification_estimates" do
18
+ let(:item) { FinModeling::ComprehensiveIncomeStatementItem.new("Comprehensive Income Net Of Tax Attributable To Noncontrolling Interest") }
19
+ subject { item.classification_estimates }
20
+ its(:keys) { should == FinModeling::ComprehensiveIncomeStatementItem::TYPES }
21
+ end
22
+
23
+ describe "classify" do
24
+ let(:cisi) { FinModeling::ComprehensiveIncomeStatementItem.new("Comprehensive Income Net Of Tax Attributable To Noncontrolling Interest") }
25
+ subject { cisi.classify }
26
+ it "returns the ComprehensiveIncomeStatementItem type with the highest probability estimate" do
27
+ estimates = cisi.classification_estimates
28
+ estimates[subject].should be_within(0.1).of(estimates.values.max)
29
+ end
30
+ end
31
+
32
+ describe "load_vectors_and_train" do
33
+ # the before(:all) clause calls load_vectors_and_train already
34
+ # we can just focus, here, on its effects
35
+
36
+ it "classifies >95% correctly" do # FIXME: add more vectors to tighten this up
37
+ num_items = 0
38
+ errors = []
39
+ FinModeling::ComprehensiveIncomeStatementItem::TRAINING_VECTORS.each do |vector|
40
+ num_items += 1
41
+ cisi = FinModeling::ComprehensiveIncomeStatementItem.new(vector[:item_string])
42
+ if cisi.classify != vector[:klass]
43
+ errors.push({ :cisi=>cisi.to_s, :expected=>vector[:klass], :got=>cisi.classify })
44
+ end
45
+ end
46
+
47
+ pct_errors = errors.length.to_f / num_items
48
+ if pct_errors > 0.05
49
+ puts "errors: " + errors.inspect
50
+ end
51
+ pct_errors.should be < 0.05
52
+
53
+ end
54
+ end
55
+
56
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe FinModeling::DebtCostOfCapital do
4
+ describe "#calculate" do
5
+ context "if given the after tax cost" do
6
+ let(:r) { FinModeling::Rate.new(0.05) }
7
+ subject { FinModeling::DebtCostOfCapital.calculate(:after_tax_cost => r) }
8
+ it { should be_a FinModeling::Rate }
9
+ its(:value) { should be_within(0.1).of(r.value) }
10
+ end
11
+ context "if given the after tax cost and marginal tax rate" do
12
+ let(:r) { FinModeling::Rate.new(0.05) }
13
+ let(:t) { FinModeling::Rate.new(0.35) }
14
+ subject { FinModeling::DebtCostOfCapital.calculate(:before_tax_cost => r, :marginal_tax_rate => t) }
15
+ it { should be_a FinModeling::Rate }
16
+ its(:value) { should be_within(0.1).of(r.value * (1.0 - t.value)) }
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,33 @@
1
+ # equity_change_calculation_spec.rb
2
+
3
+ require 'spec_helper'
4
+
5
+ describe FinModeling::EquityChangeCalculation do
6
+ before(:all) do
7
+ deere_2011_annual_rpt = "http://www.sec.gov/Archives/edgar/data/315189/000110465910063219/0001104659-10-063219-index.htm"
8
+ @filing = FinModeling::AnnualReportFiling.download(deere_2011_annual_rpt)
9
+ @ses_period = @filing.shareholder_equity_statement.periods.last
10
+
11
+ @equity_changes = @filing.shareholder_equity_statement.equity_change_calculation
12
+
13
+ bs_period_initial = @filing.balance_sheet.periods[-2]
14
+ bs_period_final = @filing.balance_sheet.periods[-1]
15
+
16
+ @equity_plus_minority_int_initial = @filing.balance_sheet.reformulated(bs_period_initial).common_shareholders_equity.total +
17
+ @filing.balance_sheet.reformulated(bs_period_initial).minority_interest .total
18
+ @equity_plus_minority_int_final = @filing.balance_sheet.reformulated(bs_period_final) .common_shareholders_equity.total +
19
+ @filing.balance_sheet.reformulated(bs_period_final) .minority_interest .total
20
+ end
21
+
22
+ describe ".summary" do
23
+ subject{ @equity_changes.summary(:period => @ses_period) }
24
+
25
+ it { should be_an_instance_of FinModeling::CalculationSummary }
26
+
27
+ describe ".total" do
28
+ subject{ @equity_changes.summary(:period => @ses_period).total }
29
+ it { should be_within(1.0).of(@equity_plus_minority_int_final - @equity_plus_minority_int_initial) }
30
+ end
31
+ end
32
+ end
33
+
@@ -0,0 +1,58 @@
1
+ # equity_change_item_spec.rb
2
+
3
+ require 'spec_helper'
4
+
5
+ describe FinModeling::EquityChangeItem do
6
+
7
+ describe "new" do
8
+ subject { FinModeling::EquityChangeItem.new("Depreciation and amortization of property and equipment") }
9
+ it { should be_a FinModeling::EquityChangeItem }
10
+ end
11
+
12
+ describe "train" do
13
+ let(:item) { FinModeling::EquityChangeItem.new("Depreciation and amortization of property and equipment") }
14
+ it "trains the classifier that this EquityChangeItem is of the given type" do
15
+ item.train(:oci)
16
+ end
17
+ end
18
+
19
+ describe "classification_estimates" do
20
+ let(:item) { FinModeling::EquityChangeItem.new("Depreciation and amortization of property and equipment") }
21
+ subject { item.classification_estimates }
22
+ its(:keys) { should == FinModeling::EquityChangeItem::TYPES }
23
+ end
24
+
25
+ describe "classify" do
26
+ let(:eci) { FinModeling::EquityChangeItem.new("Depreciation and amortization of property and equipment") }
27
+ subject { eci.classify }
28
+ it "returns the EquityChangeItem type with the highest probability estimate" do
29
+ estimates = eci.classification_estimates
30
+ estimates[subject].should be_within(0.1).of(estimates.values.max)
31
+ end
32
+ end
33
+
34
+ describe "load_vectors_and_train" do
35
+ # the before(:all) clause calls load_vectors_and_train already
36
+ # we can just focus, here, on its effects
37
+
38
+ it "classifies >95% correctly" do # FIXME: add more vectors to tighten this up
39
+ num_items = 0
40
+ errors = []
41
+ FinModeling::EquityChangeItem::TRAINING_VECTORS.each do |vector|
42
+ num_items += 1
43
+ eci = FinModeling::EquityChangeItem.new(vector[:item_string])
44
+ if eci.classify != vector[:klass]
45
+ errors.push({ :eci=>eci.to_s, :expected=>vector[:klass], :got=>eci.classify })
46
+ end
47
+ end
48
+
49
+ pct_errors = errors.length.to_f / num_items
50
+ if pct_errors > 0.05
51
+ puts "errors: " + errors.inspect
52
+ end
53
+ pct_errors.should be < 0.05
54
+
55
+ end
56
+ end
57
+
58
+ end
data/spec/factory_spec.rb CHANGED
@@ -6,13 +6,13 @@ describe FinModeling::Factory do
6
6
  describe "BalanceSheetCalculation" do
7
7
  subject { FinModeling::Factory.BalanceSheetCalculation }
8
8
 
9
- it { should be_an_instance_of FinModeling::BalanceSheetCalculation }
9
+ it { should be_a FinModeling::BalanceSheetCalculation }
10
10
  end
11
11
 
12
12
  describe "incomeStatementCalculation" do
13
13
  subject { FinModeling::Factory.IncomeStatementCalculation }
14
14
 
15
- it { should be_an_instance_of FinModeling::IncomeStatementCalculation }
15
+ it { should be_a FinModeling::IncomeStatementCalculation }
16
16
  end
17
17
  end
18
18
 
@@ -6,7 +6,7 @@ describe FinModeling::Forecasts do
6
6
  before (:all) do
7
7
  @company = FinModeling::Company.find("aapl")
8
8
  @filings = FinModeling::CompanyFilings.new(@company.filings_since_date(Time.parse("2010-10-01")))
9
- @forecasts = @filings.forecasts(@filings.choose_forecasting_policy, num_quarters=3)
9
+ @forecasts = @filings.forecasts(@filings.choose_forecasting_policy(e_ror=0.10), num_quarters=3)
10
10
  end
11
11
 
12
12
  describe "balance_sheet_analyses" do
@@ -15,7 +15,7 @@ describe FinModeling::Forecasts do
15
15
  end
16
16
 
17
17
  describe "income_statement_analyses" do
18
- subject { @forecasts.income_statement_analyses(@filings) }
18
+ subject { @forecasts.income_statement_analyses(@filings, e_ror=0.10) }
19
19
  it { should be_a FinModeling::CalculationSummary }
20
20
  end
21
21
  end
@@ -8,55 +8,57 @@ describe FinModeling::IncomeStatementAnalyses do
8
8
  @summary.title = "Title 123"
9
9
  @summary.rows = [ ]
10
10
  @summary.rows << FinModeling::CalculationRow.new(:key => "Revenue Growth", :type => :oa, :vals => [ 4])
11
- @summary.rows << FinModeling::CalculationRow.new(:key => "Sales / NOA", :type => :oa, :vals => [ 4])
12
- @summary.rows << FinModeling::CalculationRow.new(:key => "Operating PM", :type => :oa, :vals => [ 4])
13
- @summary.rows << FinModeling::CalculationRow.new(:key => "FI / NFA", :type => :oa, :vals => [ 4])
14
- @summary.rows << FinModeling::CalculationRow.new(:key => "Row", :type => :fa, :vals => [109])
15
- @summary.rows << FinModeling::CalculationRow.new(:key => "Row", :type => :oa, :vals => [ 93])
16
- @summary.rows << FinModeling::CalculationRow.new(:key => "Row", :type => :fa, :vals => [ 1])
11
+ @summary.rows << FinModeling::CalculationRow.new(:key => "Sales / NOA", :type => :oa, :vals => [ 4])
12
+ @summary.rows << FinModeling::CalculationRow.new(:key => "Operating PM", :type => :oa, :vals => [ 4])
13
+ @summary.rows << FinModeling::CalculationRow.new(:key => "FI / NFA", :type => :oa, :vals => [ 4])
14
+ @summary.rows << FinModeling::CalculationRow.new(:key => "Row", :type => :fa, :vals => [109])
15
+ @summary.rows << FinModeling::CalculationRow.new(:key => "Row", :type => :oa, :vals => [ 93])
16
+ @summary.rows << FinModeling::CalculationRow.new(:key => "Row", :type => :fa, :vals => [ 1])
17
17
  end
18
18
 
19
19
  describe ".new" do
20
20
  subject { FinModeling::IncomeStatementAnalyses.new(@summary) }
21
21
 
22
22
  it { should be_a_kind_of FinModeling::CalculationSummary }
23
- its(:title) { should == @summary.title }
24
- its(:rows) { should == @summary.rows }
25
- its(:header_row) { should == @summary.header_row }
26
- its(:rows) { should == @summary.rows }
27
- its(:num_value_columns) { should == @summary.num_value_columns }
28
- its(:key_width) { should == @summary.key_width }
29
- its(:val_width) { should == @summary.val_width }
30
- its(:max_decimals) { should == @summary.max_decimals }
23
+ its(:title) { should == @summary.title }
24
+ its(:rows) { should == @summary.rows }
25
+ its(:header_row) { should == @summary.header_row }
26
+ its(:rows) { should == @summary.rows }
27
+ its(:num_value_columns) { should == @summary.num_value_columns }
28
+ its(:key_width) { should == @summary.key_width }
29
+ its(:val_width) { should == @summary.val_width }
30
+ its(:max_decimals) { should == @summary.max_decimals }
31
31
  its(:totals_row_enabled) { should be_false }
32
32
  end
33
33
 
34
- describe ".print_extras" do
35
- subject { FinModeling::IncomeStatementAnalyses.new(@summary) }
34
+ let(:isa) { FinModeling::IncomeStatementAnalyses.new(@summary) }
35
+
36
+ describe ".print_regressions" do
37
+ subject { isa }
36
38
 
37
- it { should respond_to(:print_extras) }
39
+ it { should respond_to(:print_regressions) }
38
40
  end
39
41
 
40
42
  describe ".revenue_growth_row" do
41
- subject { FinModeling::IncomeStatementAnalyses.new(@summary).revenue_growth_row }
43
+ subject { isa.revenue_growth_row }
42
44
  it { should be_a FinModeling::CalculationRow }
43
45
  its(:key) { should == "Revenue Growth" }
44
46
  end
45
47
 
46
48
  describe ".operating_pm_row" do
47
- subject { FinModeling::IncomeStatementAnalyses.new(@summary).operating_pm_row }
49
+ subject { isa.operating_pm_row }
48
50
  it { should be_a FinModeling::CalculationRow }
49
51
  its(:key) { should == "Operating PM" }
50
52
  end
51
53
 
52
54
  describe ".sales_over_noa_row" do
53
- subject { FinModeling::IncomeStatementAnalyses.new(@summary).sales_over_noa_row }
55
+ subject { isa.sales_over_noa_row }
54
56
  it { should be_a FinModeling::CalculationRow }
55
57
  its(:key) { should == "Sales / NOA" }
56
58
  end
57
59
 
58
60
  describe ".fi_over_nfa_row" do
59
- subject { FinModeling::IncomeStatementAnalyses.new(@summary).fi_over_nfa_row }
61
+ subject { isa.fi_over_nfa_row }
60
62
  it { should be_a FinModeling::CalculationRow }
61
63
  its(:key) { should == "FI / NFA" }
62
64
  end
@@ -14,53 +14,28 @@ describe FinModeling::IncomeStatementCalculation do
14
14
  @period = @inc_stmt.periods.last
15
15
  end
16
16
 
17
- describe "net_income_calculation" do
18
- it "returns a NetIncomeCalculation" do
19
- @inc_stmt.net_income_calculation.should be_an_instance_of FinModeling::NetIncomeCalculation
20
- end
21
- it "returns the root node of the net income calculation" do
22
- @inc_stmt.net_income_calculation.label.downcase.should match /net.*income/
23
- end
17
+ describe ".net_income_calculation" do
18
+ subject { @inc_stmt.net_income_calculation }
19
+ it { should be_a FinModeling::NetIncomeCalculation }
20
+ its(:label) { should match /net.*income/i }
24
21
  end
25
22
 
26
- describe "is_valid?" do
27
- context "when no node contains the term 'tax'" do
28
- #before(:all) do
29
- # @inc_stmt_no_taxes = FinModeling::Factory.IncomeStatementCalculation(:sheet => 'google 10-k 2011-12-31 income statement',
30
- # :delete_tax_item => true)
31
- #end
32
- it "returns false if none of the net income leaf nodes contains the term 'tax'" do
33
- #@inc_stmt_no_taxes.is_valid?.should be_false
34
- pending "no good way of setting up this test..."
35
- end
36
- end
37
- context "when no node contains the terms 'sales' or 'revenue'" do
38
- #before(:all) do
39
- # @inc_stmt_no_sales = FinModeling::Factory.IncomeStatementCalculation(:sheet => 'google 10-k 2011-12-31 income statement',
40
- # :delete_sales_item => true)
41
- #end
42
- it "returns false if none of the net income leaf nodes contains the term 'tax'" do
43
- #@inc_stmt_no_sales.is_valid?.should be_false
44
- pending "no good way of setting up this test..."
45
- end
46
- end
47
- context "otherwise" do
48
- subject { @inc_stmt.is_valid? }
49
- it { should be_true }
50
- end
23
+ describe ".is_valid?" do
24
+ subject { @inc_stmt.is_valid? }
25
+ it { should == (@inc_stmt.net_income_calculation.has_tax_item? && @inc_stmt.net_income_calculation.has_revenue_item?) }
51
26
  end
52
27
 
53
- describe "reformulated" do
54
- subject { @inc_stmt.reformulated(@period) }
55
- it { should be_an_instance_of FinModeling::ReformulatedIncomeStatement }
28
+ describe ".reformulated" do
29
+ subject { @inc_stmt.reformulated(@period, ci_calc=nil) }
30
+ it { should be_a FinModeling::ReformulatedIncomeStatement }
56
31
  end
57
32
 
58
- describe "latest_quarterly_reformulated" do
59
- subject{ @inc_stmt.latest_quarterly_reformulated(@prev_inc_stmt) }
60
- it { should be_an_instance_of FinModeling::ReformulatedIncomeStatement }
33
+ describe ".latest_quarterly_reformulated" do
34
+ subject{ @inc_stmt.latest_quarterly_reformulated(ci_calc=nil, @prev_inc_stmt, prev_ci_calc=nil) }
35
+ it { should be_a FinModeling::ReformulatedIncomeStatement }
61
36
  end
62
37
 
63
- describe "write_constructor" do
38
+ describe ".write_constructor" do
64
39
  before(:all) do
65
40
  file_name = "/tmp/finmodeling-inc-stmt.rb"
66
41
  item_name = "@inc_stmt"
@@ -69,19 +44,12 @@ describe FinModeling::IncomeStatementCalculation do
69
44
  file.close
70
45
 
71
46
  eval(File.read(file_name))
72
-
73
47
  @loaded_is = eval(item_name)
74
48
  end
75
49
 
76
- it "writes itself to a file, and when reloaded, has the same periods" do
77
- expected_periods = @inc_stmt.periods.map{|x| x.to_pretty_s}.join(',')
78
- @loaded_is.periods.map{|x| x.to_pretty_s}.join(',').should == expected_periods
79
- end
80
- it "writes itself to a file, and when reloaded, has the same net financing income" do
81
- period = @inc_stmt.periods.yearly.last
82
- expected_nfi = @inc_stmt.reformulated(period).net_financing_income.total
83
- @loaded_is.reformulated(period).net_financing_income.total.should be_within(1.0).of(expected_nfi)
84
- end
50
+ subject { @loaded_is }
51
+ it { should have_the_same_periods_as(@inc_stmt) }
52
+ it { should have_the_same_reformulated_last_total(:net_financing_income).as(@inc_stmt) }
85
53
  end
86
54
 
87
55
  end
@@ -3,54 +3,42 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe FinModeling::IncomeStatementItem do
6
-
7
- before(:all) do
8
- #FinModeling::IncomeStatementItem.load_vectors_and_train(FinModeling::IncomeStatementItem::TRAINING_VECTORS)
9
- end
6
+ let(:isi) { FinModeling::IncomeStatementItem.new("Cost of Goods Sold") }
10
7
 
11
8
  describe "new" do
12
- it "takes a string and returns a new IncomeStatementItem" do
13
- isi = FinModeling::IncomeStatementItem.new("Cost of Goods Sold")
14
- isi.should be_an_instance_of FinModeling::IncomeStatementItem
15
- end
9
+ subject { isi }
10
+ it { should be_a FinModeling::IncomeStatementItem }
16
11
  end
17
12
 
18
13
  describe "train" do
19
14
  it "trains the classifier that this ISI is of the given type" do
20
- FinModeling::IncomeStatementItem.new("provision for income tax").train(:tax)
15
+ isi.train(:tax)
21
16
  end
22
17
  end
23
18
 
24
19
  describe "classification_estimates" do
25
- it "returns a hash with the confidence in each ISI type" do
26
- isi = FinModeling::IncomeStatementItem.new("Cost of Services")
27
-
28
- FinModeling::IncomeStatementItem::TYPES.each do |klass|
29
- isi.classification_estimates.keys.include?(klass).should be_true
30
- end
31
- end
20
+ subject { isi.classification_estimates }
21
+ it { should be_a Hash }
22
+ its(:keys) { should == FinModeling::IncomeStatementItem::TYPES }
32
23
  end
33
24
 
34
25
  describe "classify" do
26
+ subject { isi.classify }
35
27
  it "returns the ISI type with the highest probability estimate" do
36
- isi = FinModeling::IncomeStatementItem.new("provision for income tax")
37
28
  estimates = isi.classification_estimates
38
- estimates[isi.classify].should be_within(0.1).of(estimates.values.max)
29
+ estimates[subject].should be_within(0.1).of(estimates.values.max)
39
30
  end
40
31
  end
41
32
 
42
33
  describe "load_vectors_and_train" do
43
- # the before(:all) clause calls load_vectors_and_train already
44
- # we can just focus, here, on its effects
45
-
46
- it "loads vectors from a given file, trains on each example, and correctly classifies tax" do
47
- isi = FinModeling::IncomeStatementItem.new("provision for income tax")
48
- isi.classify.should == :tax
34
+ context "tax" do
35
+ subject { FinModeling::IncomeStatementItem.new("provision for income tax").classify }
36
+ it { should == :tax }
49
37
  end
50
38
 
51
- it "loads vectors from a given file, trains on each example, and correctly classifies revenue" do
52
- isi = FinModeling::IncomeStatementItem.new("software licensing revenues net")
53
- isi.classify.should == :or
39
+ context "or" do
40
+ subject { FinModeling::IncomeStatementItem.new("software licensing revenues net").classify }
41
+ it { should == :or }
54
42
  end
55
43
 
56
44
  it "classifies >95% correctly" do
@@ -74,12 +62,12 @@ describe FinModeling::IncomeStatementItem do
74
62
  end
75
63
 
76
64
  describe "tokenize" do
65
+ subject { FinModeling::IncomeStatementItem.new("Cost of Goods Sold").tokenize }
77
66
  it "returns an array of downcased 1-word, 2-word, and 3-word tokens" do
78
- isi = FinModeling::IncomeStatementItem.new("Cost of Goods Sold")
79
67
  expected_tokens = ["^", "cost", "of", "goods", "sold", "$"]
80
68
  expected_tokens += ["^ cost", "cost of", "of goods", "goods sold", "sold $"]
81
69
  expected_tokens += ["^ cost of", "cost of goods", "of goods sold", "goods sold $"]
82
- isi.tokenize.sort.should == expected_tokens.sort
70
+ subject.sort.should == expected_tokens.sort
83
71
  end
84
72
  end
85
73
 
@@ -12,9 +12,12 @@ describe FinModeling::LiabsAndEquityCalculation do
12
12
  end
13
13
 
14
14
  describe "summary" do
15
- it "only requires a period (knows how debts/credits work and whether to flip the total)" do
16
- @lse.summary(:period=>@period).should be_an_instance_of FinModeling::CalculationSummary
17
- end
15
+ subject { @lse.summary(:period=>@period) }
16
+ it { should be_a FinModeling::CalculationSummary }
17
+ end
18
+
19
+ describe ".has_equity_item" do
20
+ pending "Find a test case..."
18
21
  end
19
22
  end
20
23