finmodeling 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +3 -0
- data/Gemfile +10 -0
- data/README.md +292 -0
- data/Rakefile +6 -0
- data/TODO.txt +36 -0
- data/examples/dump_report.rb +33 -0
- data/examples/lists/nasdaq-mid-to-mega-tech-symbols.txt +226 -0
- data/examples/show_report.rb +218 -0
- data/examples/show_reports.rb +77 -0
- data/finmodeling.gemspec +31 -0
- data/lib/finmodeling/annual_report_filing.rb +104 -0
- data/lib/finmodeling/array_with_stats.rb +22 -0
- data/lib/finmodeling/assets_calculation.rb +36 -0
- data/lib/finmodeling/assets_item.rb +14 -0
- data/lib/finmodeling/assets_item_vectors.rb +638 -0
- data/lib/finmodeling/balance_sheet_analyses.rb +33 -0
- data/lib/finmodeling/balance_sheet_calculation.rb +68 -0
- data/lib/finmodeling/calculation_summary.rb +148 -0
- data/lib/finmodeling/can_cache_classifications.rb +36 -0
- data/lib/finmodeling/can_cache_summaries.rb +16 -0
- data/lib/finmodeling/can_classify_rows.rb +54 -0
- data/lib/finmodeling/cash_change_calculation.rb +67 -0
- data/lib/finmodeling/cash_change_item.rb +14 -0
- data/lib/finmodeling/cash_change_item_vectors.rb +241 -0
- data/lib/finmodeling/cash_flow_statement_calculation.rb +85 -0
- data/lib/finmodeling/classifiers.rb +11 -0
- data/lib/finmodeling/company.rb +102 -0
- data/lib/finmodeling/company_filing.rb +64 -0
- data/lib/finmodeling/company_filing_calculation.rb +75 -0
- data/lib/finmodeling/company_filings.rb +100 -0
- data/lib/finmodeling/config.rb +37 -0
- data/lib/finmodeling/constant_forecasting_policy.rb +23 -0
- data/lib/finmodeling/factory.rb +27 -0
- data/lib/finmodeling/float_helpers.rb +17 -0
- data/lib/finmodeling/forecasts.rb +48 -0
- data/lib/finmodeling/generic_forecasting_policy.rb +19 -0
- data/lib/finmodeling/has_string_classifer.rb +96 -0
- data/lib/finmodeling/income_statement_analyses.rb +74 -0
- data/lib/finmodeling/income_statement_calculation.rb +71 -0
- data/lib/finmodeling/income_statement_item.rb +14 -0
- data/lib/finmodeling/income_statement_item_vectors.rb +654 -0
- data/lib/finmodeling/liabs_and_equity_calculation.rb +36 -0
- data/lib/finmodeling/liabs_and_equity_item.rb +14 -0
- data/lib/finmodeling/liabs_and_equity_item_vectors.rb +1936 -0
- data/lib/finmodeling/net_income_calculation.rb +41 -0
- data/lib/finmodeling/paths.rb +5 -0
- data/lib/finmodeling/period_array.rb +24 -0
- data/lib/finmodeling/quarterly_report_filing.rb +23 -0
- data/lib/finmodeling/rate.rb +20 -0
- data/lib/finmodeling/ratio.rb +20 -0
- data/lib/finmodeling/reformulated_balance_sheet.rb +176 -0
- data/lib/finmodeling/reformulated_cash_flow_statement.rb +140 -0
- data/lib/finmodeling/reformulated_income_statement.rb +436 -0
- data/lib/finmodeling/string_helpers.rb +26 -0
- data/lib/finmodeling/version.rb +3 -0
- data/lib/finmodeling.rb +70 -0
- data/spec/annual_report_filing_spec.rb +68 -0
- data/spec/assets_calculation_spec.rb +21 -0
- data/spec/assets_item_spec.rb +66 -0
- data/spec/balance_sheet_analyses_spec.rb +43 -0
- data/spec/balance_sheet_calculation_spec.rb +91 -0
- data/spec/calculation_summary_spec.rb +63 -0
- data/spec/can_classify_rows_spec.rb +86 -0
- data/spec/cash_change_calculation_spec.rb +56 -0
- data/spec/cash_change_item_spec.rb +66 -0
- data/spec/cash_flow_statement_calculation_spec.rb +108 -0
- data/spec/company_filing_calculation_spec.rb +74 -0
- data/spec/company_filing_spec.rb +30 -0
- data/spec/company_filings_spec.rb +55 -0
- data/spec/company_spec.rb +73 -0
- data/spec/constant_forecasting_policy_spec.rb +37 -0
- data/spec/factory_spec.rb +18 -0
- data/spec/forecasts_spec.rb +21 -0
- data/spec/generic_forecasting_policy_spec.rb +33 -0
- data/spec/income_statement_analyses_spec.rb +63 -0
- data/spec/income_statement_calculation_spec.rb +88 -0
- data/spec/income_statement_item_spec.rb +86 -0
- data/spec/liabs_and_equity_calculation_spec.rb +20 -0
- data/spec/liabs_and_equity_item_spec.rb +66 -0
- data/spec/mocks/calculation.rb +10 -0
- data/spec/mocks/income_statement_analyses.rb +93 -0
- data/spec/mocks/sec_query.rb +31 -0
- data/spec/net_income_calculation_spec.rb +23 -0
- data/spec/period_array.rb +52 -0
- data/spec/quarterly_report_filing_spec.rb +69 -0
- data/spec/rate_spec.rb +33 -0
- data/spec/ratio_spec.rb +33 -0
- data/spec/reformulated_balance_sheet_spec.rb +146 -0
- data/spec/reformulated_cash_flow_statement_spec.rb +174 -0
- data/spec/reformulated_income_statement_spec.rb +293 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/string_helpers_spec.rb +23 -0
- data/tools/create_balance_sheet_training_vectors.rb +65 -0
- data/tools/create_cash_change_training_vectors.rb +48 -0
- data/tools/create_credit_debit_training_vectors.rb +51 -0
- data/tools/create_income_statement_training_vectors.rb +48 -0
- metadata +289 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
# reformulated_income_statement_spec.rb
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe FinModeling::ReformulatedIncomeStatement do
|
|
6
|
+
before(:all) do
|
|
7
|
+
google_2009_annual_rpt = "http://www.sec.gov/Archives/edgar/data/1288776/000119312510030774/0001193125-10-030774-index.htm"
|
|
8
|
+
@filing_2009 = FinModeling::AnnualReportFiling.download google_2009_annual_rpt
|
|
9
|
+
|
|
10
|
+
google_2011_annual_rpt = "http://www.sec.gov/Archives/edgar/data/1288776/000119312512025336/0001193125-12-025336-index.htm"
|
|
11
|
+
@filing_2011 = FinModeling::AnnualReportFiling.download google_2011_annual_rpt
|
|
12
|
+
|
|
13
|
+
@years_between_stmts = 2.0
|
|
14
|
+
|
|
15
|
+
@inc_stmt_2009 = @filing_2009.income_statement
|
|
16
|
+
is_period_2009 = @inc_stmt_2009.periods.last
|
|
17
|
+
@reformed_inc_stmt_2009 = @inc_stmt_2009.reformulated(is_period_2009)
|
|
18
|
+
|
|
19
|
+
@bal_sheet_2009 = @filing_2009.balance_sheet
|
|
20
|
+
bs_period_2009 = @bal_sheet_2009.periods.last
|
|
21
|
+
@reformed_bal_sheet_2009 = @bal_sheet_2009.reformulated(bs_period_2009)
|
|
22
|
+
|
|
23
|
+
@inc_stmt_2011 = @filing_2011.income_statement
|
|
24
|
+
is_period_2011 = @inc_stmt_2011.periods.last
|
|
25
|
+
@reformed_inc_stmt_2011 = @inc_stmt_2011.reformulated(is_period_2011)
|
|
26
|
+
|
|
27
|
+
@bal_sheet_2011 = @filing_2011.balance_sheet
|
|
28
|
+
bs_period_2011 = @bal_sheet_2011.periods.last
|
|
29
|
+
@reformed_bal_sheet_2011 = @bal_sheet_2011.reformulated(bs_period_2011)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
describe "operating_revenues" do
|
|
33
|
+
subject { @reformed_inc_stmt_2011.operating_revenues }
|
|
34
|
+
it { should be_an_instance_of FinModeling::CalculationSummary }
|
|
35
|
+
its(:total) { should be_within(0.1).of(37905000000.0) }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
describe "cost_of_revenues" do
|
|
39
|
+
subject { @reformed_inc_stmt_2011.cost_of_revenues }
|
|
40
|
+
it { should be_an_instance_of FinModeling::CalculationSummary }
|
|
41
|
+
its(:total) { should be_within(0.1).of(-13188000000.0) }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
describe "gross_revenue" do
|
|
45
|
+
subject { @reformed_inc_stmt_2011.gross_revenue }
|
|
46
|
+
it { should be_an_instance_of FinModeling::CalculationSummary }
|
|
47
|
+
its(:total) { should be_within(0.1).of( @reformed_inc_stmt_2011.operating_revenues.total +
|
|
48
|
+
@reformed_inc_stmt_2011.cost_of_revenues.total) }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
describe "operating_expenses" do
|
|
52
|
+
subject { @reformed_inc_stmt_2011.operating_expenses }
|
|
53
|
+
it { should be_an_instance_of FinModeling::CalculationSummary }
|
|
54
|
+
its(:total) { should be_within(0.1).of(-12475000000.0) }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
describe "income_from_sales_before_tax" do
|
|
58
|
+
subject { @reformed_inc_stmt_2011.income_from_sales_before_tax }
|
|
59
|
+
it { should be_an_instance_of FinModeling::CalculationSummary }
|
|
60
|
+
its(:total) { should be_within(0.1).of( @reformed_inc_stmt_2011.gross_revenue.total +
|
|
61
|
+
@reformed_inc_stmt_2011.operating_expenses.total) }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
describe "income_from_sales_after_tax" do
|
|
65
|
+
subject { @reformed_inc_stmt_2011.income_from_sales_after_tax }
|
|
66
|
+
it { should be_an_instance_of FinModeling::CalculationSummary }
|
|
67
|
+
its(:total) { should be_within(0.1).of(9682400000.0) }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
describe "operating_income_after_tax" do
|
|
71
|
+
subject { @reformed_inc_stmt_2011.operating_income_after_tax }
|
|
72
|
+
it { should be_an_instance_of FinModeling::CalculationSummary }
|
|
73
|
+
its(:total) { should be_within(0.1).of(9357400000.0) }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
describe "net_financing_income" do
|
|
77
|
+
subject { @reformed_inc_stmt_2011.net_financing_income }
|
|
78
|
+
it { should be_an_instance_of FinModeling::CalculationSummary }
|
|
79
|
+
its(:total) { should be_within(0.1).of(379600000.0) }
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
describe "comprehensive_income" do
|
|
83
|
+
subject { @reformed_inc_stmt_2011.comprehensive_income }
|
|
84
|
+
it { should be_an_instance_of FinModeling::CalculationSummary }
|
|
85
|
+
its(:total) { should be_within(0.1).of(9737000000.0) }
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
describe "gross_margin" do
|
|
89
|
+
subject { @reformed_inc_stmt_2011.gross_margin }
|
|
90
|
+
it { should be_an_instance_of Float }
|
|
91
|
+
it { should be_within(0.1).of(@reformed_inc_stmt_2011.gross_revenue.total / @reformed_inc_stmt_2011.operating_revenues.total) }
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
describe "sales_profit_margin" do
|
|
95
|
+
subject { @reformed_inc_stmt_2011.sales_profit_margin }
|
|
96
|
+
it { should be_an_instance_of Float }
|
|
97
|
+
it { should be_within(0.1).of(@reformed_inc_stmt_2011.income_from_sales_after_tax.total / @reformed_inc_stmt_2011.operating_revenues.total) }
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
describe "operating_profit_margin" do
|
|
101
|
+
subject { @reformed_inc_stmt_2011.operating_profit_margin }
|
|
102
|
+
it { should be_an_instance_of Float }
|
|
103
|
+
it { should be_within(0.1).of(@reformed_inc_stmt_2011.operating_income_after_tax.total / @reformed_inc_stmt_2011.operating_revenues.total) }
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
describe "fi_over_sales" do
|
|
107
|
+
subject { @reformed_inc_stmt_2011.fi_over_sales }
|
|
108
|
+
it { should be_an_instance_of Float }
|
|
109
|
+
it { should be_within(0.1).of(@reformed_inc_stmt_2011.net_financing_income.total / @reformed_inc_stmt_2011.operating_revenues.total) }
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
describe "ni_over_sales" do
|
|
113
|
+
subject { @reformed_inc_stmt_2011.ni_over_sales }
|
|
114
|
+
it { should be_an_instance_of Float }
|
|
115
|
+
it { should be_within(0.1).of(@reformed_inc_stmt_2011.comprehensive_income.total / @reformed_inc_stmt_2011.operating_revenues.total) }
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
describe "sales_over_noa" do
|
|
119
|
+
subject { @reformed_inc_stmt_2011.sales_over_noa(@reformed_bal_sheet_2011) }
|
|
120
|
+
it { should be_an_instance_of Float }
|
|
121
|
+
it { should be_within(0.1).of(@reformed_inc_stmt_2011.operating_revenues.total / @reformed_bal_sheet_2011.net_operating_assets.total) }
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
describe "fi_over_nfa" do
|
|
125
|
+
subject { @reformed_inc_stmt_2011.fi_over_nfa(@reformed_bal_sheet_2011) }
|
|
126
|
+
it { should be_an_instance_of Float }
|
|
127
|
+
it { should be_within(0.1).of(@reformed_inc_stmt_2011.net_financing_income.total / @reformed_bal_sheet_2011.net_financial_assets.total) }
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
describe "revenue_growth" do
|
|
131
|
+
before(:all) do
|
|
132
|
+
rev0 = @reformed_inc_stmt_2009.operating_revenues.total
|
|
133
|
+
rev1 = @reformed_inc_stmt_2011.operating_revenues.total
|
|
134
|
+
@expected_growth = (rev1 / rev0)**(1.0/@years_between_stmts) - 1.0
|
|
135
|
+
end
|
|
136
|
+
subject { @reformed_inc_stmt_2011.revenue_growth(@reformed_inc_stmt_2009) }
|
|
137
|
+
it { should be_an_instance_of Float }
|
|
138
|
+
it { should be_within(0.1).of(@expected_growth) }
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
describe "core_oi_growth" do
|
|
142
|
+
before(:all) do
|
|
143
|
+
core_oi0 = @reformed_inc_stmt_2009.income_from_sales_after_tax.total
|
|
144
|
+
core_oi1 = @reformed_inc_stmt_2011.income_from_sales_after_tax.total
|
|
145
|
+
@expected_growth = (core_oi1 / core_oi0)**(1.0/@years_between_stmts) - 1.0
|
|
146
|
+
end
|
|
147
|
+
subject { @reformed_inc_stmt_2011.core_oi_growth(@reformed_inc_stmt_2009) }
|
|
148
|
+
it { should be_an_instance_of Float }
|
|
149
|
+
it { should be_within(0.1).of(@expected_growth) }
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
describe "oi_growth" do
|
|
153
|
+
before(:all) do
|
|
154
|
+
core_oi0 = @reformed_inc_stmt_2009.operating_income_after_tax.total
|
|
155
|
+
core_oi1 = @reformed_inc_stmt_2011.operating_income_after_tax.total
|
|
156
|
+
@expected_growth = (core_oi1 / core_oi0)**(1.0/@years_between_stmts) - 1.0
|
|
157
|
+
end
|
|
158
|
+
subject { @reformed_inc_stmt_2011.oi_growth(@reformed_inc_stmt_2009) }
|
|
159
|
+
it { should be_an_instance_of Float }
|
|
160
|
+
it { should be_within(0.1).of(@expected_growth) }
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
describe "re_oi" do
|
|
164
|
+
before(:all) do
|
|
165
|
+
@expected_rate_of_return = 0.10
|
|
166
|
+
@expected_re_oi = 6868337409.999998
|
|
167
|
+
end
|
|
168
|
+
subject { @reformed_inc_stmt_2011.re_oi(@reformed_bal_sheet_2009, @expected_rate_of_return) }
|
|
169
|
+
it { should be_an_instance_of Float }
|
|
170
|
+
it { should be_within(0.1).of(@expected_re_oi) }
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
describe "analysis" do
|
|
174
|
+
subject {@reformed_inc_stmt_2011.analysis(@reformed_bal_sheet_2011, @reformed_inc_stmt_2009, @reformed_bal_sheet_2009) }
|
|
175
|
+
|
|
176
|
+
it { should be_an_instance_of FinModeling::CalculationSummary }
|
|
177
|
+
it "contains the expected rows" do
|
|
178
|
+
expected_keys = [ "Revenue ($MM)", "Core OI ($MM)", "OI ($MM)", "FI ($MM)",
|
|
179
|
+
"NI ($MM)", "Gross Margin", "Sales PM", "Operating PM",
|
|
180
|
+
"FI / Sales", "NI / Sales", "Sales / NOA", "FI / NFA",
|
|
181
|
+
"Revenue Growth", "Core OI Growth", "OI Growth", "ReOI ($MM)" ]
|
|
182
|
+
|
|
183
|
+
subject.rows.map{ |row| row.key }.should == expected_keys
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
describe "-" do
|
|
188
|
+
before(:all) do
|
|
189
|
+
google_2011_q3_rpt = "http://www.sec.gov/Archives/edgar/data/1288776/000119312511282235/0001193125-11-282235-index.htm"
|
|
190
|
+
@filing_2011_q3 = FinModeling::AnnualReportFiling.download google_2011_q3_rpt
|
|
191
|
+
|
|
192
|
+
@inc_stmt_2011_q3 = @filing_2011_q3.income_statement
|
|
193
|
+
is_period_2011_q3 = @inc_stmt_2011_q3.periods.threequarterly.last
|
|
194
|
+
@reformed_inc_stmt_2011_q3 = @inc_stmt_2011_q3.reformulated(is_period_2011_q3)
|
|
195
|
+
|
|
196
|
+
@diff = @reformed_inc_stmt_2011 - @reformed_inc_stmt_2011_q3
|
|
197
|
+
end
|
|
198
|
+
subject { @diff }
|
|
199
|
+
|
|
200
|
+
it { should be_an_instance_of FinModeling::ReformulatedIncomeStatement }
|
|
201
|
+
its(:period) { should_not be_nil } # FIXME
|
|
202
|
+
|
|
203
|
+
it "returns the difference between the two re_is's for each calculation" do
|
|
204
|
+
methods = [ :operating_revenues, :cost_of_revenues, :gross_revenue,
|
|
205
|
+
:operating_expenses, :income_from_sales_before_tax,
|
|
206
|
+
:income_from_sales_after_tax, :operating_income_after_tax,
|
|
207
|
+
:net_financing_income, :comprehensive_income ]
|
|
208
|
+
#:gross_margin, :sales_profit_margin, :operating_profit_margin,
|
|
209
|
+
#:fi_over_sales, :ni_over_sales, :sales_over_noa,
|
|
210
|
+
#:fi_over_nfa, :revenue_growth, :core_oi_growth,
|
|
211
|
+
#:oi_growth, :re_oi ]
|
|
212
|
+
|
|
213
|
+
methods.each do |method|
|
|
214
|
+
expected_val = @reformed_inc_stmt_2011.send(method).total - @reformed_inc_stmt_2011_q3.send(method).total
|
|
215
|
+
@diff.send(method).total.should be_within(1.0).of(expected_val)
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
it "returns values that are close to 1/4th of the annual value" do
|
|
220
|
+
methods = [ :operating_revenues, :cost_of_revenues, :gross_revenue,
|
|
221
|
+
:operating_expenses, :income_from_sales_before_tax,
|
|
222
|
+
:income_from_sales_after_tax, :operating_income_after_tax,
|
|
223
|
+
#:net_financing_income,
|
|
224
|
+
:comprehensive_income ]
|
|
225
|
+
#:gross_margin, :sales_profit_margin, :operating_profit_margin,
|
|
226
|
+
#:fi_over_sales, :ni_over_sales, :sales_over_noa,
|
|
227
|
+
#:fi_over_nfa, :revenue_growth, :core_oi_growth,
|
|
228
|
+
#:oi_growth, :re_oi ]
|
|
229
|
+
|
|
230
|
+
methods.each do |method|
|
|
231
|
+
orig = @reformed_inc_stmt_2011.send(method).total
|
|
232
|
+
max = (orig > 0) ? (0.35 * orig) : (0.15 * orig)
|
|
233
|
+
min = (orig > 0) ? (0.15 * orig) : (0.35 * orig)
|
|
234
|
+
actual = @diff.send(method).total
|
|
235
|
+
if (actual < min) || (actual > max)
|
|
236
|
+
err = "#{method} returns #{actual.to_nearest_thousand.to_s.with_thousands_separators}, "
|
|
237
|
+
err += "which is outside bounds: [#{min.to_nearest_thousand.to_s.with_thousands_separators}, "
|
|
238
|
+
err += "#{max.to_nearest_thousand.to_s.with_thousands_separators}]"
|
|
239
|
+
puts err
|
|
240
|
+
end
|
|
241
|
+
@diff.send(method).total.should be > min
|
|
242
|
+
@diff.send(method).total.should be < max
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
describe "#forecast_next" do
|
|
248
|
+
before (:all) do
|
|
249
|
+
@company = FinModeling::Company.find("aapl")
|
|
250
|
+
@filings = FinModeling::CompanyFilings.new(@company.filings_since_date(Time.parse("2010-10-01")))
|
|
251
|
+
@policy = FinModeling::GenericForecastingPolicy.new
|
|
252
|
+
|
|
253
|
+
prev_bs_period = @filings.last.balance_sheet.periods.last
|
|
254
|
+
next_bs_period_value = prev_bs_period.value.next_month.next_month.next_month
|
|
255
|
+
@next_bs_period = Xbrlware::Context::Period.new(next_bs_period_value)
|
|
256
|
+
|
|
257
|
+
next_is_period_value = {"start_date" => prev_bs_period.value,
|
|
258
|
+
"end_date" => prev_bs_period.value.next_month.next_month.next_month }
|
|
259
|
+
@next_is_period = Xbrlware::Context::Period.new(next_is_period_value)
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
let(:last_re_bs) { @filings.last.balance_sheet.reformulated(@filings.last.balance_sheet.periods.last) }
|
|
263
|
+
let(:last_re_is) { @filings.last.income_statement.latest_quarterly_reformulated(nil) }
|
|
264
|
+
let(:next_re_is) { FinModeling::ReformulatedIncomeStatement.forecast_next(@next_is_period, @policy, last_re_bs, last_re_is) }
|
|
265
|
+
let(:next_re_bs) { FinModeling::ReformulatedBalanceSheet.forecast_next(@next_bs_period, @policy, last_re_bs, next_re_is) }
|
|
266
|
+
|
|
267
|
+
subject { next_re_is }
|
|
268
|
+
|
|
269
|
+
it { should be_a_kind_of FinModeling::ReformulatedIncomeStatement }
|
|
270
|
+
it "should have the given period" do
|
|
271
|
+
subject.period.to_pretty_s == @next_is_period.to_pretty_s
|
|
272
|
+
end
|
|
273
|
+
it "should set operating_revenue to last year's revenue times the revenue growth" do
|
|
274
|
+
expected_val = last_re_is.operating_revenues.total * (1.0 + FinModeling::Rate.new(@policy.revenue_growth).annualize(from=365, to=365/4.0))
|
|
275
|
+
subject.operating_revenues.total.should == expected_val
|
|
276
|
+
end
|
|
277
|
+
it "should set OISAT to operating revenue times sales PM" do
|
|
278
|
+
expected_val = subject.operating_revenues.total * @policy.sales_pm
|
|
279
|
+
subject.income_from_sales_after_tax.total.should == expected_val
|
|
280
|
+
end
|
|
281
|
+
it "should set NFI to fi_over_nfa times last year's NFA" do
|
|
282
|
+
expected_val = last_re_bs.net_financial_assets.total * (@policy.fi_over_nfa/4)
|
|
283
|
+
subject.net_financing_income.total.should == expected_val
|
|
284
|
+
end
|
|
285
|
+
it "should set comprehensive income to OISAT plus NFI" do
|
|
286
|
+
expected_val = subject.income_from_sales_after_tax.total + subject.net_financing_income.total
|
|
287
|
+
subject.comprehensive_income.total.should == expected_val
|
|
288
|
+
end
|
|
289
|
+
it "should have an empty analysis (with the same rows)" do
|
|
290
|
+
subject.analysis(next_re_bs, last_re_is, last_re_bs)
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# string_helpers_spec.rb
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe String do
|
|
6
|
+
describe "matches_regexes?" do
|
|
7
|
+
it "returns false if no regexes are provided" do
|
|
8
|
+
s = "asdfasdf"
|
|
9
|
+
regexes = []
|
|
10
|
+
s.matches_regexes?(regexes).should be_false
|
|
11
|
+
end
|
|
12
|
+
it "returns false if the string does not match any of the regexes" do
|
|
13
|
+
s = "asdfasdf"
|
|
14
|
+
regexes = [/\d/, /[A-Z]/]
|
|
15
|
+
s.matches_regexes?(regexes).should be_false
|
|
16
|
+
end
|
|
17
|
+
it "returns true if the string matches one or more of the regexes" do
|
|
18
|
+
s = "asdfasdf"
|
|
19
|
+
regexes = [/sdf/, /ddd/, /af+/]
|
|
20
|
+
s.matches_regexes?(regexes).should be_true
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
$LOAD_PATH << "."
|
|
4
|
+
|
|
5
|
+
require 'finmodeling'
|
|
6
|
+
|
|
7
|
+
def get_args
|
|
8
|
+
if ARGV.length != 2
|
|
9
|
+
puts "usage #{__FILE__} <stock symbol or report URL> <assets | liabilities>"
|
|
10
|
+
exit
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
args = { :stock_symbol => nil, :filing_url => nil, :assets_or_liabs => nil }
|
|
14
|
+
arg = ARGV[0]
|
|
15
|
+
if arg =~ /http/
|
|
16
|
+
args[:filing_url] = arg
|
|
17
|
+
else
|
|
18
|
+
args[:stock_symbol] = arg.downcase
|
|
19
|
+
end
|
|
20
|
+
case ARGV[1]
|
|
21
|
+
when "assets"
|
|
22
|
+
args[:assets_or_liabs] = "assets"
|
|
23
|
+
when "liabilities"
|
|
24
|
+
args[:assets_or_liabs] = "liabilities"
|
|
25
|
+
else
|
|
26
|
+
puts "usage #{__FILE__} <stock symbol or report URL> <assets | liabilities>"
|
|
27
|
+
exit
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
return args
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def get_company_filing_url(stock_symbol)
|
|
34
|
+
company = FinModeling::Company.find(stock_symbol)
|
|
35
|
+
raise RuntimeError.new("couldn't find company") if company.nil?
|
|
36
|
+
raise RuntimeError.new("company has no annual reports") if company.annual_reports.length == 0
|
|
37
|
+
filing_url = company.annual_reports.last.link
|
|
38
|
+
|
|
39
|
+
return filing_url
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def get_filing(filing_url)
|
|
43
|
+
filing = FinModeling::AnnualReportFiling.download(filing_url)
|
|
44
|
+
return filing
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def print_assets(filing)
|
|
48
|
+
period = filing.balance_sheet.assets_calculation.periods.last
|
|
49
|
+
items = filing.balance_sheet.assets_calculation.leaf_items(period)
|
|
50
|
+
items.each { |item| puts item.pretty_name }
|
|
51
|
+
puts
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def print_liabilities(filing)
|
|
55
|
+
period = filing.balance_sheet.liabs_and_equity_calculation.periods.last
|
|
56
|
+
items = filing.balance_sheet.liabs_and_equity_calculation.leaf_items(period)
|
|
57
|
+
items.each { |item| puts item.pretty_name }
|
|
58
|
+
puts
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
args = get_args
|
|
62
|
+
filing_url = args[:filing_url] || get_company_filing_url(args[:stock_symbol])
|
|
63
|
+
filing = get_filing(filing_url)
|
|
64
|
+
print_assets(filing) if args[:assets_or_liabs] == "assets"
|
|
65
|
+
print_liabilities(filing) if args[:assets_or_liabs] == "liabilities"
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
$LOAD_PATH << "."
|
|
4
|
+
|
|
5
|
+
require 'finmodeling'
|
|
6
|
+
|
|
7
|
+
def get_args
|
|
8
|
+
if ARGV.length != 1
|
|
9
|
+
puts "usage #{__FILE__} <stock symbol or report URL>"
|
|
10
|
+
exit
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
args = { :stock_symbol => nil, :filing_url => nil }
|
|
14
|
+
arg = ARGV[0]
|
|
15
|
+
if arg =~ /http/
|
|
16
|
+
args[:filing_url] = arg
|
|
17
|
+
else
|
|
18
|
+
args[:stock_symbol] = arg.downcase
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
return args
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def get_company_filing_url(stock_symbol)
|
|
25
|
+
company = FinModeling::Company.find(stock_symbol)
|
|
26
|
+
raise RuntimeError.new("couldn't find company") if company.nil?
|
|
27
|
+
raise RuntimeError.new("company has no annual reports") if company.annual_reports.length == 0
|
|
28
|
+
filing_url = company.annual_reports.last.link
|
|
29
|
+
|
|
30
|
+
return filing_url
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def get_filing(filing_url)
|
|
34
|
+
filing = FinModeling::AnnualReportFiling.download(filing_url)
|
|
35
|
+
return filing
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def print_items(filing)
|
|
39
|
+
items = filing.cash_flow_statement.cash_change_calculation.leaf_items
|
|
40
|
+
items.each do |item|
|
|
41
|
+
puts " { :cci_type=>:c, :item_string=>\"#{item.pretty_name}\" },"
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
args = get_args
|
|
46
|
+
filing_url = args[:filing_url] || get_company_filing_url(args[:stock_symbol])
|
|
47
|
+
filing = get_filing(filing_url)
|
|
48
|
+
print_items(filing)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
$LOAD_PATH << "."
|
|
4
|
+
|
|
5
|
+
require 'finmodeling'
|
|
6
|
+
|
|
7
|
+
def get_args
|
|
8
|
+
if ARGV.length != 1
|
|
9
|
+
puts "usage #{__FILE__} <stock symbol or report URL>"
|
|
10
|
+
exit
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
args = { :stock_symbol => nil, :filing_url => nil }
|
|
14
|
+
arg = ARGV[0]
|
|
15
|
+
if arg =~ /http/
|
|
16
|
+
args[:filing_url] = arg
|
|
17
|
+
else
|
|
18
|
+
args[:stock_symbol] = arg.downcase
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
return args
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def get_company_filing_url(stock_symbol)
|
|
25
|
+
company = FinModeling::Company.find(stock_symbol)
|
|
26
|
+
raise RuntimeError.new("couldn't find company") if company.nil?
|
|
27
|
+
raise RuntimeError.new("company has no annual reports") if company.annual_reports.length == 0
|
|
28
|
+
filing_url = company.annual_reports.last.link
|
|
29
|
+
|
|
30
|
+
return filing_url
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def get_filing(filing_url)
|
|
34
|
+
filing = FinModeling::AnnualReportFiling.download(filing_url)
|
|
35
|
+
return filing
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def print_items(filing)
|
|
39
|
+
items = filing.balance_sheet.leaf_items
|
|
40
|
+
items += filing.income_statement.leaf_items
|
|
41
|
+
items.each do |item|
|
|
42
|
+
if !item.def.nil?
|
|
43
|
+
puts "{ :balance_defn=>:#{item.def["xbrli:balance"]}, :item_string=>\"#{item.pretty_name}\" },"
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
args = get_args
|
|
49
|
+
filing_url = args[:filing_url] || get_company_filing_url(args[:stock_symbol])
|
|
50
|
+
filing = get_filing(filing_url)
|
|
51
|
+
print_items(filing)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
$LOAD_PATH << "."
|
|
4
|
+
|
|
5
|
+
require 'finmodeling'
|
|
6
|
+
|
|
7
|
+
def get_args
|
|
8
|
+
if ARGV.length != 1
|
|
9
|
+
puts "usage #{__FILE__} <stock symbol or report URL>"
|
|
10
|
+
exit
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
args = { :stock_symbol => nil, :filing_url => nil }
|
|
14
|
+
arg = ARGV[0]
|
|
15
|
+
if arg =~ /http/
|
|
16
|
+
args[:filing_url] = arg
|
|
17
|
+
else
|
|
18
|
+
args[:stock_symbol] = arg.downcase
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
return args
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def get_company_filing_url(stock_symbol)
|
|
25
|
+
company = FinModeling::Company.find(stock_symbol)
|
|
26
|
+
raise RuntimeError.new("couldn't find company") if company.nil?
|
|
27
|
+
raise RuntimeError.new("company has no annual reports") if company.annual_reports.length == 0
|
|
28
|
+
filing_url = company.annual_reports.last.link
|
|
29
|
+
|
|
30
|
+
return filing_url
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def get_filing(filing_url)
|
|
34
|
+
filing = FinModeling::AnnualReportFiling.download(filing_url)
|
|
35
|
+
return filing
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def print_income_statement(filing)
|
|
39
|
+
period = filing.income_statement.net_income_calculation.periods.yearly.last
|
|
40
|
+
items = filing.income_statement.net_income_calculation.leaf_items(period)
|
|
41
|
+
items.each { |item| puts item.pretty_name }
|
|
42
|
+
puts
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
args = get_args
|
|
46
|
+
filing_url = args[:filing_url] || get_company_filing_url(args[:stock_symbol])
|
|
47
|
+
filing = get_filing(filing_url)
|
|
48
|
+
print_income_statement(filing) #if filing.is_valid?
|