finmodeling 0.1

Sign up to get free protection for your applications and to get access to all the features.
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