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.
Files changed (97) hide show
  1. data/.gitignore +3 -0
  2. data/Gemfile +10 -0
  3. data/README.md +292 -0
  4. data/Rakefile +6 -0
  5. data/TODO.txt +36 -0
  6. data/examples/dump_report.rb +33 -0
  7. data/examples/lists/nasdaq-mid-to-mega-tech-symbols.txt +226 -0
  8. data/examples/show_report.rb +218 -0
  9. data/examples/show_reports.rb +77 -0
  10. data/finmodeling.gemspec +31 -0
  11. data/lib/finmodeling/annual_report_filing.rb +104 -0
  12. data/lib/finmodeling/array_with_stats.rb +22 -0
  13. data/lib/finmodeling/assets_calculation.rb +36 -0
  14. data/lib/finmodeling/assets_item.rb +14 -0
  15. data/lib/finmodeling/assets_item_vectors.rb +638 -0
  16. data/lib/finmodeling/balance_sheet_analyses.rb +33 -0
  17. data/lib/finmodeling/balance_sheet_calculation.rb +68 -0
  18. data/lib/finmodeling/calculation_summary.rb +148 -0
  19. data/lib/finmodeling/can_cache_classifications.rb +36 -0
  20. data/lib/finmodeling/can_cache_summaries.rb +16 -0
  21. data/lib/finmodeling/can_classify_rows.rb +54 -0
  22. data/lib/finmodeling/cash_change_calculation.rb +67 -0
  23. data/lib/finmodeling/cash_change_item.rb +14 -0
  24. data/lib/finmodeling/cash_change_item_vectors.rb +241 -0
  25. data/lib/finmodeling/cash_flow_statement_calculation.rb +85 -0
  26. data/lib/finmodeling/classifiers.rb +11 -0
  27. data/lib/finmodeling/company.rb +102 -0
  28. data/lib/finmodeling/company_filing.rb +64 -0
  29. data/lib/finmodeling/company_filing_calculation.rb +75 -0
  30. data/lib/finmodeling/company_filings.rb +100 -0
  31. data/lib/finmodeling/config.rb +37 -0
  32. data/lib/finmodeling/constant_forecasting_policy.rb +23 -0
  33. data/lib/finmodeling/factory.rb +27 -0
  34. data/lib/finmodeling/float_helpers.rb +17 -0
  35. data/lib/finmodeling/forecasts.rb +48 -0
  36. data/lib/finmodeling/generic_forecasting_policy.rb +19 -0
  37. data/lib/finmodeling/has_string_classifer.rb +96 -0
  38. data/lib/finmodeling/income_statement_analyses.rb +74 -0
  39. data/lib/finmodeling/income_statement_calculation.rb +71 -0
  40. data/lib/finmodeling/income_statement_item.rb +14 -0
  41. data/lib/finmodeling/income_statement_item_vectors.rb +654 -0
  42. data/lib/finmodeling/liabs_and_equity_calculation.rb +36 -0
  43. data/lib/finmodeling/liabs_and_equity_item.rb +14 -0
  44. data/lib/finmodeling/liabs_and_equity_item_vectors.rb +1936 -0
  45. data/lib/finmodeling/net_income_calculation.rb +41 -0
  46. data/lib/finmodeling/paths.rb +5 -0
  47. data/lib/finmodeling/period_array.rb +24 -0
  48. data/lib/finmodeling/quarterly_report_filing.rb +23 -0
  49. data/lib/finmodeling/rate.rb +20 -0
  50. data/lib/finmodeling/ratio.rb +20 -0
  51. data/lib/finmodeling/reformulated_balance_sheet.rb +176 -0
  52. data/lib/finmodeling/reformulated_cash_flow_statement.rb +140 -0
  53. data/lib/finmodeling/reformulated_income_statement.rb +436 -0
  54. data/lib/finmodeling/string_helpers.rb +26 -0
  55. data/lib/finmodeling/version.rb +3 -0
  56. data/lib/finmodeling.rb +70 -0
  57. data/spec/annual_report_filing_spec.rb +68 -0
  58. data/spec/assets_calculation_spec.rb +21 -0
  59. data/spec/assets_item_spec.rb +66 -0
  60. data/spec/balance_sheet_analyses_spec.rb +43 -0
  61. data/spec/balance_sheet_calculation_spec.rb +91 -0
  62. data/spec/calculation_summary_spec.rb +63 -0
  63. data/spec/can_classify_rows_spec.rb +86 -0
  64. data/spec/cash_change_calculation_spec.rb +56 -0
  65. data/spec/cash_change_item_spec.rb +66 -0
  66. data/spec/cash_flow_statement_calculation_spec.rb +108 -0
  67. data/spec/company_filing_calculation_spec.rb +74 -0
  68. data/spec/company_filing_spec.rb +30 -0
  69. data/spec/company_filings_spec.rb +55 -0
  70. data/spec/company_spec.rb +73 -0
  71. data/spec/constant_forecasting_policy_spec.rb +37 -0
  72. data/spec/factory_spec.rb +18 -0
  73. data/spec/forecasts_spec.rb +21 -0
  74. data/spec/generic_forecasting_policy_spec.rb +33 -0
  75. data/spec/income_statement_analyses_spec.rb +63 -0
  76. data/spec/income_statement_calculation_spec.rb +88 -0
  77. data/spec/income_statement_item_spec.rb +86 -0
  78. data/spec/liabs_and_equity_calculation_spec.rb +20 -0
  79. data/spec/liabs_and_equity_item_spec.rb +66 -0
  80. data/spec/mocks/calculation.rb +10 -0
  81. data/spec/mocks/income_statement_analyses.rb +93 -0
  82. data/spec/mocks/sec_query.rb +31 -0
  83. data/spec/net_income_calculation_spec.rb +23 -0
  84. data/spec/period_array.rb +52 -0
  85. data/spec/quarterly_report_filing_spec.rb +69 -0
  86. data/spec/rate_spec.rb +33 -0
  87. data/spec/ratio_spec.rb +33 -0
  88. data/spec/reformulated_balance_sheet_spec.rb +146 -0
  89. data/spec/reformulated_cash_flow_statement_spec.rb +174 -0
  90. data/spec/reformulated_income_statement_spec.rb +293 -0
  91. data/spec/spec_helper.rb +5 -0
  92. data/spec/string_helpers_spec.rb +23 -0
  93. data/tools/create_balance_sheet_training_vectors.rb +65 -0
  94. data/tools/create_cash_change_training_vectors.rb +48 -0
  95. data/tools/create_credit_debit_training_vectors.rb +51 -0
  96. data/tools/create_income_statement_training_vectors.rb +48 -0
  97. metadata +289 -0
@@ -0,0 +1,30 @@
1
+ # company_filing_spec.rb
2
+
3
+ require 'spec_helper'
4
+
5
+ describe FinModeling::CompanyFiling do
6
+ let(:company) { FinModeling::Company.new(FinModeling::Mocks::Entity.new) }
7
+ let(:filing_url) { company.annual_reports.last.link }
8
+
9
+ subject { FinModeling::CompanyFiling.download filing_url }
10
+
11
+ describe "#download" do
12
+ it { should be_a FinModeling::CompanyFiling }
13
+ end
14
+
15
+ describe ".print_presentations" do
16
+ it { should respond_to(:print_presentations) }
17
+ end
18
+
19
+ describe ".print_calculations" do
20
+ it { should respond_to(:print_calculations) }
21
+ end
22
+
23
+ describe ".disclosures" do
24
+ subject { (FinModeling::CompanyFiling.download filing_url).disclosures }
25
+
26
+ it { should be_an_instance_of Array }
27
+ it { should_not be_empty }
28
+ its(:first) { should be_a FinModeling::CompanyFilingCalculation }
29
+ end
30
+ end
@@ -0,0 +1,55 @@
1
+ # company_filings_spec.rb
2
+
3
+ require 'spec_helper'
4
+
5
+ describe FinModeling::CompanyFiling do
6
+ before (:all) do
7
+ @company = FinModeling::Company.find("aapl")
8
+ @filings = FinModeling::CompanyFilings.new(@company.filings_since_date(Time.parse("2010-10-01")))
9
+ end
10
+
11
+ describe "balance_sheet_analyses" do
12
+ subject { @filings.balance_sheet_analyses }
13
+ it { should be_an_instance_of FinModeling::BalanceSheetAnalyses }
14
+ end
15
+
16
+ describe "cash_flow_statement_analyses" do
17
+ subject { @filings.cash_flow_statement_analyses }
18
+ it { should be_an_instance_of FinModeling::CalculationSummary } # FIXME: model this guy the same way...
19
+ end
20
+
21
+ describe "income_statement_analyses" do
22
+ subject { @filings.income_statement_analyses }
23
+ it { should be_an_instance_of FinModeling::IncomeStatementAnalyses }
24
+ end
25
+
26
+ describe "choose_forecasting_policy" do
27
+ context "when one or two filings" do
28
+ let(:filings) { FinModeling::CompanyFilings.new(@filings[-2..-1]) }
29
+ subject { filings.choose_forecasting_policy }
30
+
31
+ it { should be_an_instance_of FinModeling::GenericForecastingPolicy }
32
+ end
33
+ context "when two or more filings" do
34
+ let(:filings) { FinModeling::CompanyFilings.new(@filings[-3..-1]) }
35
+ subject { filings.choose_forecasting_policy }
36
+ it { should be_an_instance_of FinModeling::ConstantForecastingPolicy }
37
+
38
+ let(:isa) { filings.income_statement_analyses }
39
+
40
+ its(:revenue_growth) { should be_within(0.01).of(isa.revenue_growth_row.valid_vals.mean) }
41
+ its(:sales_pm) { should be_within(0.01).of(isa.operating_pm_row.valid_vals.mean) } # FIXME: name mismatch
42
+ its(:sales_over_noa) { should be_within(0.01).of(isa.sales_over_noa_row.valid_vals.mean) }
43
+ its(:fi_over_nfa) { should be_within(0.01).of(isa.fi_over_nfa_row.valid_vals.mean) }
44
+ end
45
+ end
46
+
47
+ describe "forecasts" do
48
+ let(:policy) { @filings.choose_forecasting_policy }
49
+ let(:num_quarters) { 3 }
50
+ subject { @filings.forecasts(policy, num_quarters) }
51
+ it { should be_an_instance_of FinModeling::Forecasts }
52
+ its(:reformulated_income_statements) { should have(num_quarters).items }
53
+ its(:reformulated_balance_sheets) { should have(num_quarters).items }
54
+ end
55
+ end
@@ -0,0 +1,73 @@
1
+ # company_spec.rb
2
+
3
+ require 'spec_helper'
4
+
5
+ describe FinModeling::Company do
6
+ before(:each) do
7
+ end
8
+
9
+ 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
14
+ end
15
+
16
+ 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
19
+ end
20
+ it "returns nil if the stock symbol is invalid" do
21
+ FinModeling::Company.find("bogus symbol").should be_nil
22
+ end
23
+ end
24
+
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
30
+ end
31
+
32
+ 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
42
+ end
43
+
44
+ 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
54
+ end
55
+
56
+ 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
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,37 @@
1
+ # constant_forecasting_policy_spec.rb
2
+
3
+ require 'spec_helper'
4
+
5
+ describe FinModeling::ConstantForecastingPolicy do
6
+ before (:all) do
7
+ @vals = { :revenue_growth => 0.04,
8
+ :sales_pm => 0.20,
9
+ :fi_over_nfa => 0.01,
10
+ :sales_over_noa => 2.00 }
11
+ @policy = FinModeling::ConstantForecastingPolicy.new(@vals)
12
+ end
13
+
14
+ describe ".revenue_growth" do
15
+ subject { @policy.revenue_growth }
16
+ it { should be_an_instance_of Float }
17
+ it { should be_within(0.01).of(@vals[:revenue_growth]) }
18
+ end
19
+
20
+ describe ".sales_pm" do
21
+ subject { @policy.sales_pm }
22
+ it { should be_an_instance_of Float }
23
+ it { should be_within(0.01).of(@vals[:sales_pm]) }
24
+ end
25
+
26
+ describe ".fi_over_nfa" do
27
+ subject { @policy.fi_over_nfa }
28
+ it { should be_an_instance_of Float }
29
+ it { should be_within(0.01).of(@vals[:fi_over_nfa]) }
30
+ end
31
+
32
+ describe ".sales_over_noa" do
33
+ subject { @policy.sales_over_noa }
34
+ it { should be_an_instance_of Float }
35
+ it { should be_within(0.01).of(@vals[:sales_over_noa]) }
36
+ end
37
+ end
@@ -0,0 +1,18 @@
1
+ # factory_spec.rb
2
+
3
+ require 'spec_helper'
4
+
5
+ describe FinModeling::Factory do
6
+ describe "BalanceSheetCalculation" do
7
+ subject { FinModeling::Factory.BalanceSheetCalculation }
8
+
9
+ it { should be_an_instance_of FinModeling::BalanceSheetCalculation }
10
+ end
11
+
12
+ describe "incomeStatementCalculation" do
13
+ subject { FinModeling::Factory.IncomeStatementCalculation }
14
+
15
+ it { should be_an_instance_of FinModeling::IncomeStatementCalculation }
16
+ end
17
+ end
18
+
@@ -0,0 +1,21 @@
1
+ # company_filings_spec.rb
2
+
3
+ require 'spec_helper'
4
+
5
+ describe FinModeling::Forecasts do
6
+ before (:all) do
7
+ @company = FinModeling::Company.find("aapl")
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)
10
+ end
11
+
12
+ describe "balance_sheet_analyses" do
13
+ subject { @forecasts.balance_sheet_analyses(@filings) }
14
+ it { should be_a FinModeling::CalculationSummary }
15
+ end
16
+
17
+ describe "income_statement_analyses" do
18
+ subject { @forecasts.income_statement_analyses(@filings) }
19
+ it { should be_a FinModeling::CalculationSummary }
20
+ end
21
+ end
@@ -0,0 +1,33 @@
1
+ # generic_forecasting_policy_spec.rb
2
+
3
+ require 'spec_helper'
4
+
5
+ describe FinModeling::GenericForecastingPolicy do
6
+ before (:all) do
7
+ @policy = FinModeling::GenericForecastingPolicy.new
8
+ end
9
+
10
+ describe ".revenue_growth" do
11
+ subject { @policy.revenue_growth }
12
+ it { should be_an_instance_of Float }
13
+ it { should be_within(0.01).of(0.04) } # FIXME: Make this not hard-coded
14
+ end
15
+
16
+ describe ".sales_pm" do
17
+ subject { @policy.sales_pm }
18
+ it { should be_an_instance_of Float }
19
+ it { should be_within(0.01).of(0.20) } # FIXME: Make this not hard-coded
20
+ end
21
+
22
+ describe ".fi_over_nfa" do
23
+ subject { @policy.fi_over_nfa }
24
+ it { should be_an_instance_of Float }
25
+ it { should be_within(0.01).of(0.01) } # FIXME: Make this not hard-coded
26
+ end
27
+
28
+ describe ".sales_over_noa" do
29
+ subject { @policy.sales_over_noa }
30
+ it { should be_an_instance_of Float }
31
+ it { should be_within(0.01).of(2.00) } # FIXME: Make this not hard-coded
32
+ end
33
+ end
@@ -0,0 +1,63 @@
1
+ # income_statement_analyses_spec.rb
2
+
3
+ require 'spec_helper'
4
+
5
+ describe FinModeling::IncomeStatementAnalyses do
6
+ before(:all) do
7
+ @summary = FinModeling::CalculationSummary.new
8
+ @summary.title = "Title 123"
9
+ @summary.rows = [ ]
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])
17
+ end
18
+
19
+ describe ".new" do
20
+ subject { FinModeling::IncomeStatementAnalyses.new(@summary) }
21
+
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 }
31
+ its(:totals_row_enabled) { should be_false }
32
+ end
33
+
34
+ describe ".print_extras" do
35
+ subject { FinModeling::IncomeStatementAnalyses.new(@summary) }
36
+
37
+ it { should respond_to(:print_extras) }
38
+ end
39
+
40
+ describe ".revenue_growth_row" do
41
+ subject { FinModeling::IncomeStatementAnalyses.new(@summary).revenue_growth_row }
42
+ it { should be_a FinModeling::CalculationRow }
43
+ its(:key) { should == "Revenue Growth" }
44
+ end
45
+
46
+ describe ".operating_pm_row" do
47
+ subject { FinModeling::IncomeStatementAnalyses.new(@summary).operating_pm_row }
48
+ it { should be_a FinModeling::CalculationRow }
49
+ its(:key) { should == "Operating PM" }
50
+ end
51
+
52
+ describe ".sales_over_noa_row" do
53
+ subject { FinModeling::IncomeStatementAnalyses.new(@summary).sales_over_noa_row }
54
+ it { should be_a FinModeling::CalculationRow }
55
+ its(:key) { should == "Sales / NOA" }
56
+ end
57
+
58
+ describe ".fi_over_nfa_row" do
59
+ subject { FinModeling::IncomeStatementAnalyses.new(@summary).fi_over_nfa_row }
60
+ it { should be_a FinModeling::CalculationRow }
61
+ its(:key) { should == "FI / NFA" }
62
+ end
63
+ end
@@ -0,0 +1,88 @@
1
+ # income_statement_calculation_spec.rb
2
+
3
+ require 'spec_helper'
4
+
5
+ describe FinModeling::IncomeStatementCalculation do
6
+ before(:all) do
7
+ google_2010_q3_rpt = "http://www.sec.gov/Archives/edgar/data/1288776/000119312511282235/0001193125-11-282235-index.htm"
8
+ filing_q3 = FinModeling::AnnualReportFiling.download google_2010_q3_rpt
9
+ @prev_inc_stmt = filing_q3.income_statement
10
+
11
+ google_2011_annual_rpt = "http://www.sec.gov/Archives/edgar/data/1288776/000119312512025336/0001193125-12-025336-index.htm"
12
+ filing = FinModeling::AnnualReportFiling.download google_2011_annual_rpt
13
+ @inc_stmt = filing.income_statement
14
+ @period = @inc_stmt.periods.last
15
+ end
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
24
+ end
25
+
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
51
+ end
52
+
53
+ describe "reformulated" do
54
+ subject { @inc_stmt.reformulated(@period) }
55
+ it { should be_an_instance_of FinModeling::ReformulatedIncomeStatement }
56
+ end
57
+
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 }
61
+ end
62
+
63
+ describe "write_constructor" do
64
+ before(:all) do
65
+ file_name = "/tmp/finmodeling-inc-stmt.rb"
66
+ item_name = "@inc_stmt"
67
+ file = File.open(file_name, "w")
68
+ @inc_stmt.write_constructor(file, item_name)
69
+ file.close
70
+
71
+ eval(File.read(file_name))
72
+
73
+ @loaded_is = eval(item_name)
74
+ end
75
+
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
85
+ end
86
+
87
+ end
88
+
@@ -0,0 +1,86 @@
1
+ # period_array_spec.rb
2
+
3
+ require 'spec_helper'
4
+
5
+ describe FinModeling::IncomeStatementItem do
6
+
7
+ before(:all) do
8
+ #FinModeling::IncomeStatementItem.load_vectors_and_train(FinModeling::IncomeStatementItem::TRAINING_VECTORS)
9
+ end
10
+
11
+ 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
16
+ end
17
+
18
+ describe "train" do
19
+ it "trains the classifier that this ISI is of the given type" do
20
+ FinModeling::IncomeStatementItem.new("provision for income tax").train(:tax)
21
+ end
22
+ end
23
+
24
+ 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
32
+ end
33
+
34
+ describe "classify" do
35
+ it "returns the ISI type with the highest probability estimate" do
36
+ isi = FinModeling::IncomeStatementItem.new("provision for income tax")
37
+ estimates = isi.classification_estimates
38
+ estimates[isi.classify].should be_within(0.1).of(estimates.values.max)
39
+ end
40
+ end
41
+
42
+ 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
49
+ end
50
+
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
54
+ end
55
+
56
+ it "classifies >95% correctly" do
57
+ num_items = 0
58
+ errors = []
59
+ FinModeling::IncomeStatementItem::TRAINING_VECTORS.each do |vector|
60
+ num_items += 1
61
+ isi = FinModeling::IncomeStatementItem.new(vector[:item_string])
62
+ if isi.classify != vector[:klass]
63
+ errors.push({ :isi=>isi.to_s, :expected=>vector[:klass], :got=>isi.classify })
64
+ end
65
+ end
66
+
67
+ pct_errors = errors.length.to_f / num_items
68
+ if pct_errors > 0.05
69
+ puts "errors: " + errors.inspect
70
+ end
71
+ pct_errors.should be < 0.05
72
+
73
+ end
74
+ end
75
+
76
+ describe "tokenize" do
77
+ 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
+ expected_tokens = ["^", "cost", "of", "goods", "sold", "$"]
80
+ expected_tokens += ["^ cost", "cost of", "of goods", "goods sold", "sold $"]
81
+ expected_tokens += ["^ cost of", "cost of goods", "of goods sold", "goods sold $"]
82
+ isi.tokenize.sort.should == expected_tokens.sort
83
+ end
84
+ end
85
+
86
+ end
@@ -0,0 +1,20 @@
1
+ # liabs_and_equity_calculation_spec.rb
2
+
3
+ require 'spec_helper'
4
+
5
+ describe FinModeling::LiabsAndEquityCalculation 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
+ @bal_sheet = filing.balance_sheet
10
+ @period = @bal_sheet.periods.last
11
+ @lse = @bal_sheet.liabs_and_equity_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
+ @lse.summary(:period=>@period).should be_an_instance_of FinModeling::CalculationSummary
17
+ end
18
+ end
19
+ end
20
+
@@ -0,0 +1,66 @@
1
+ # period_array_spec.rb
2
+
3
+ require 'spec_helper'
4
+
5
+ describe FinModeling::LiabsAndEquityItem do
6
+
7
+ before(:all) do
8
+ #FinModeling::LiabsAndEquityItem.load_vectors_and_train(FinModeling::LiabsAndEquityItem::TRAINING_VECTORS)
9
+ end
10
+
11
+ describe "new" do
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
19
+ it "trains the classifier that this LiabsAndEquityItem is of the given type" do
20
+ FinModeling::LiabsAndEquityItem.new("Accounts Payable Current").train(:ol)
21
+ end
22
+ end
23
+
24
+ describe "classification_estimates" do
25
+ it "returns a hash with the confidence in each LiabsAndEquityItem type" do
26
+ laei = FinModeling::LiabsAndEquityItem.new("Accounts Payable Current")
27
+
28
+ FinModeling::LiabsAndEquityItem::TYPES.each do |klass|
29
+ laei.classification_estimates.keys.include?(klass).should be_true
30
+ end
31
+ end
32
+ end
33
+
34
+ describe "classify" do
35
+ it "returns the LiabsAndEquityItem type with the highest probability estimate" do
36
+ laei = FinModeling::LiabsAndEquityItem.new("Accounts Payable Current")
37
+ estimates = laei.classification_estimates
38
+ estimates[laei.classify].should be_within(0.1).of(estimates.values.max)
39
+ end
40
+ end
41
+
42
+ 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 "classifies >95% correctly" do
47
+ num_items = 0
48
+ errors = []
49
+ FinModeling::LiabsAndEquityItem::TRAINING_VECTORS.each do |vector|
50
+ num_items += 1
51
+ laei = FinModeling::LiabsAndEquityItem.new(vector[:item_string])
52
+ if laei.classify != vector[:klass]
53
+ errors.push({ :laei=>laei.to_s, :expected=>vector[:klass], :got=>laei.classify })
54
+ end
55
+ end
56
+
57
+ pct_errors = errors.length.to_f / num_items
58
+ if pct_errors > 0.05
59
+ puts "errors: " + errors.inspect
60
+ end
61
+ pct_errors.should be < 0.05
62
+
63
+ end
64
+ end
65
+
66
+ end
@@ -0,0 +1,10 @@
1
+ module FinModeling
2
+ module Mocks
3
+ class Calculation
4
+ attr_accessor :label
5
+ def initialize
6
+ @label = "Dummy"
7
+ end
8
+ end
9
+ end
10
+ end