fundamentalista 0.2.0 → 0.4.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 36260d14ca8a99975cc3c9717ecd1fa5694f2e2d29144aaeb8d51e9eed3a25a5
4
- data.tar.gz: 920282a4dd650e6ed172638e02b85c37b3ac6e0166af5f7a837b4e69003d1c59
3
+ metadata.gz: 0d6101d9bee968831b3ffb75a7e6663deb2bb1fc925a2c01b7a4cfcfcfb50fd4
4
+ data.tar.gz: 52119b52c85c8bf846516846f9af7e11612bdd6c27048940007c5c798bd368e9
5
5
  SHA512:
6
- metadata.gz: 698002a96de44e7c842e619fbce287758f2478f56f0e0a3c1fcbb131a635e88316b9609bfba710c7223627f6ea7ba67b5a3ad5cc5a9296cf34b061a65b76956f
7
- data.tar.gz: 675e708b8b952f95aa50ef374165b37fa70f9d7d6e217ee8b14fb7a8157fec4c1402f8a0703aba8d4808fa33e0202a75eeafc43e2fd76a8bdba358b63f044763
6
+ metadata.gz: fc67da833989bdf483cc1fdabf382bd0f62cdd64539ae7edca030008c94f5380952224b0f83f1e2adc14651d851e2fcea8b03289e4f731a218432e8ed97f924b
7
+ data.tar.gz: b6125c1afde39ba4ba9298c3080ce4141ed3afeaecab18bd5a8aa1e10fefd939f68bf3f9d3d91ec8b04a1dbfb4822ad8cca5eb4512953101e057bce92d7434fa
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.0
4
+
5
+ - Revenue by product line and by region from FMP as `Segments`, with shares and growth per segment.
6
+ - Greenwald's earnings power value and the residual income model on the valuation.
7
+
8
+ ## 0.3.0
9
+
10
+ - Insurance lines and ratios: premiums, claims, loss, expense and combined ratios, investment yield and the float, read from the concepts property and casualty insurers report.
11
+
3
12
  ## 0.2.0
4
13
 
5
14
  - Daily prices from FMP as a `PriceHistory` with dividend adjusted returns, the valuation of every fiscal year at its closing price, and the company beta.
data/README.md CHANGED
@@ -113,7 +113,22 @@ jpm.ratios.cost_of_risk # credit provisions over average loans
113
113
  jpm.ratios.allowance_to_loans
114
114
  ```
115
115
 
116
- The banking ratios appear in `ratios.to_h` only for banks, and the industrial ones that need inventory, cost of revenue or a classified balance sheet come back `nil` for them. Quarterly ratios are per quarter, not annualized; use the trailing twelve months for returns.
116
+ The banking and insurance ratios appear in `ratios.to_h` only for banks and insurers, and the industrial ones that need inventory, cost of revenue or a classified balance sheet come back `nil` for them. Quarterly ratios are per quarter, not annualized; use the trailing twelve months for returns.
117
+
118
+ ## Insurers
119
+
120
+ Insurers earn premiums and invest the float, so their lines live on `period.insurance`: premiums earned and written, claims incurred, total benefits and expenses, investment income, investments, reserves, unearned premiums, premiums receivable and reinsurance recoverables. `period.insurer?` says whether the company earns premiums, and `insurance.float` is Buffett's: reserves plus unearned premiums, less what policyholders and reinsurers still owe.
121
+
122
+ ```ruby
123
+ trv = Fundamentalista.company("TRV").financials.latest
124
+ trv.ratios.loss_ratio # claims incurred over premiums earned
125
+ trv.ratios.combined_ratio # benefits, losses and expenses over premiums earned; below one, underwriting makes money
126
+ trv.ratios.expense_ratio # the difference between the two
127
+ trv.ratios.investment_yield # investment income over average investments
128
+ trv.ratios.float_to_equity
129
+ ```
130
+
131
+ These are property and casualty concepts. For life insurers, whose revenue is mostly investment income and whose reserves are future policy benefits, the combined ratio and the float mislead; read their investment yield and return on equity instead.
117
132
 
118
133
  ## Ratios
119
134
 
@@ -270,6 +285,26 @@ company.valuation_history(:pe, :pb, :fcf_yield) # {2025 => {price:
270
285
  company.valuation_history(:pe, prices: prices) # with a history you bring
271
286
  ```
272
287
 
288
+ ## Segments
289
+
290
+ FMP serves revenue by product line and by region for every fiscal year, as `Segments`: what the company sells and where, and how each part grows. EDGAR's company facts carry no segments.
291
+
292
+ ```ruby
293
+ segments = company.segments(by: :product) # or :geography
294
+ segments.latest # {"iPhone" => ..., "Service" => ...}
295
+ segments.share # each segment's share of the latest year
296
+ segments.growth("Service", years: 5) # CAGR of one segment
297
+ ```
298
+
299
+ ### More valuation models
300
+
301
+ Two models complement the DCF. Greenwald's earnings power value capitalizes today's after tax EBIT at the discount rate as if the company never grew, so the gap to the price is the growth being paid for. The residual income model starts from book value and adds only the earnings above the cost of equity's charge on it, so it rewards returns on equity above that cost and nothing else.
302
+
303
+ ```ruby
304
+ valuation.earnings_power_value(discount_rate: 0.09)
305
+ valuation.residual_income_value(cost_of_equity: 0.09, growth: 0.04, years: 10)
306
+ ```
307
+
273
308
  ## Providers
274
309
 
275
310
  | | EDGAR | Financial Modeling Prep |
@@ -279,6 +314,7 @@ company.valuation_history(:pe, prices: prices) # with a history y
279
314
  | Quotes | No, pass a price | Yes |
280
315
  | Analyst estimates | No, pass an EPS | Yes |
281
316
  | Prices and beta | No, bring a PriceHistory and a beta | Yes |
317
+ | Revenue segments | No | Yes |
282
318
  | Coverage | Companies filing with the SEC, US GAAP and IFRS, in their reporting currency | Global |
283
319
 
284
320
  EDGAR publishes every value a company ever tagged, restatements included. Fundamentalista reads each line item for the period it describes and keeps the most recently filed value, while the fiscal year label comes from the original filing. Companies tag the same idea under different XBRL concepts, so each line item has an ordered list of concepts in `Providers::Edgar::Tags`, and the first one reported wins. Debt excludes lease obligations, which FMP's `totalDebt` includes.
@@ -12,6 +12,7 @@ module Fundamentalista
12
12
  # company.valuation(price: 320).pe
13
13
  # company.estimate.eps # FMP only
14
14
  # company.valuation_history(:pe) # {2025 => {price:, pe:}, ...}
15
+ # company.segments(by: :geography).share # FMP only
15
16
  #
16
17
  class Company
17
18
  include Inspectable
@@ -65,6 +66,13 @@ module Fundamentalista
65
66
  end.to_h
66
67
  end
67
68
 
69
+ # Returns the revenue Segments by +:product+ or +:geography+. Raises
70
+ # UnsupportedPeriodError on EDGAR, whose company facts carry none.
71
+ def segments(by: :product)
72
+ @segments ||= {}
73
+ @segments[by] ||= provider.segments(self, by: by)
74
+ end
75
+
68
76
  # Returns the trailing twelve months as a Period, built from the last
69
77
  # +quarters+ quarterly periods; eight give it a prior period too.
70
78
  def ttm(quarters: 8)
@@ -92,7 +92,8 @@ module Fundamentalista
92
92
  currency: quarters.first.currency, balance: quarters.first.balance,
93
93
  income: IncomeStatement.sum(quarters.map(&:income)),
94
94
  cash_flow: CashFlowStatement.sum(quarters.map(&:cash_flow)),
95
- banking: Banking.sum(quarters.map(&:banking))
95
+ banking: Banking.sum(quarters.map(&:banking)),
96
+ insurance: Insurance.sum(quarters.map(&:insurance))
96
97
  )
97
98
  end
98
99
 
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fundamentalista
4
+ # The lines an insurer reports that the three classic statements do not
5
+ # carry: premiums, claims, investment income, reserves and the balances
6
+ # behind the float. Empty for companies that are not insurers. All in
7
+ # the reporting currency.
8
+ class Insurance < Statement
9
+ field :premiums_earned, :premiums_written, :claims_incurred, :benefits_and_expenses, :investment_income,
10
+ :investments, :reserves, :unearned_premiums, :premiums_receivable, :reinsurance_recoverables
11
+
12
+ INSTANT = %i[investments reserves unearned_premiums premiums_receivable reinsurance_recoverables].freeze
13
+
14
+ # Adds up +statements+, keeping the latest balances instead.
15
+ def self.sum(statements)
16
+ summed = super
17
+ balances = INSTANT.to_h { |field| [field, statements.first.public_send(field)] }
18
+ new(**summed.to_h, **balances)
19
+ end
20
+
21
+ # Whether the company earns premiums, the mark of an insurer.
22
+ def insurer?
23
+ !premiums_earned.nil? && !premiums_earned.zero?
24
+ end
25
+
26
+ # Buffett's float: reserves and unearned premiums, less what
27
+ # policyholders and reinsurers still owe. The money an insurer invests
28
+ # before it pays claims.
29
+ def float
30
+ return nil if reserves.nil?
31
+
32
+ reserves + (unearned_premiums || 0) - (premiums_receivable || 0) - (reinsurance_recoverables || 0)
33
+ end
34
+ end
35
+ end
@@ -27,6 +27,9 @@ module Fundamentalista
27
27
  # The Banking lines, empty for companies that are not banks.
28
28
  attr_reader :banking
29
29
 
30
+ # The Insurance lines, empty for companies that are not insurers.
31
+ attr_reader :insurance
32
+
30
33
  # Where each line item came from, keyed by item, as Source objects.
31
34
  # Empty when the provider does not say.
32
35
  attr_reader :sources
@@ -37,14 +40,15 @@ module Fundamentalista
37
40
  # The period before this one, or +nil+.
38
41
  attr_accessor :prior
39
42
 
40
- def initialize(fiscal_year:, ended_on:, income:, balance:, cash_flow:, banking: Banking.new, sources: {},
41
- type: :annual, quarter: nil, currency: 'USD', prior: nil)
43
+ def initialize(fiscal_year:, ended_on:, income:, balance:, cash_flow:, banking: Banking.new, insurance: Insurance.new,
44
+ sources: {}, type: :annual, quarter: nil, currency: 'USD', prior: nil)
42
45
  @fiscal_year = fiscal_year
43
46
  @ended_on = ended_on
44
47
  @income = income
45
48
  @balance = balance
46
49
  @cash_flow = cash_flow
47
50
  @banking = banking
51
+ @insurance = insurance
48
52
  @sources = sources
49
53
  @type = type
50
54
  @quarter = quarter
@@ -69,6 +73,11 @@ module Fundamentalista
69
73
  banking.bank?
70
74
  end
71
75
 
76
+ # Whether the company is an insurer, judged by premiums earned.
77
+ def insurer?
78
+ insurance.insurer?
79
+ end
80
+
72
81
  # Returns the Source of the line item +name+, or +nil+.
73
82
  def source(name)
74
83
  sources[name]
@@ -90,7 +99,8 @@ module Fundamentalista
90
99
  # Returns the period and its statements as a Hash.
91
100
  def to_h
92
101
  { fiscal_year: fiscal_year, quarter: quarter, type: type, ended_on: ended_on, currency: currency,
93
- income: income.to_h, balance: balance.to_h, cash_flow: cash_flow.to_h, banking: banking.to_h }
102
+ income: income.to_h, balance: balance.to_h, cash_flow: cash_flow.to_h, banking: banking.to_h,
103
+ insurance: insurance.to_h }
94
104
  end
95
105
 
96
106
  # The days the period spans, for turning turnover into days.
@@ -136,7 +146,7 @@ module Fundamentalista
136
146
  return owner_earnings if name == :owner_earnings
137
147
  return per_share(name.to_s.delete_suffix('_per_share').to_sym) if name.end_with?('_per_share')
138
148
 
139
- statement = [income, balance, cash_flow, banking].find { |candidate| candidate.respond_to?(name) }
149
+ statement = [income, balance, cash_flow, banking, insurance].find { |candidate| candidate.respond_to?(name) }
140
150
  raise ArgumentError, "Unknown metric #{name.inspect}" unless statement
141
151
 
142
152
  statement.public_send(name)
@@ -2,9 +2,9 @@
2
2
 
3
3
  module Fundamentalista
4
4
  # The contract every data source implements: resolve a Company, return
5
- # its Financials, a Quote or +nil+, the analyst Estimates it has, and
6
- # its PriceHistory. Subclasses get an HTTP client with retries and error
7
- # mapping.
5
+ # its Financials, a Quote or +nil+, the analyst Estimates it has, its
6
+ # PriceHistory and its revenue Segments. Subclasses get an HTTP client
7
+ # with retries and error mapping.
8
8
  class Provider
9
9
  class << self
10
10
  # The symbol the provider is registered under.
@@ -39,6 +39,10 @@ module Fundamentalista
39
39
  raise NotImplementedError
40
40
  end
41
41
 
42
+ def segments(company, by:)
43
+ raise NotImplementedError
44
+ end
45
+
42
46
  private
43
47
 
44
48
  def get(url, params = {}, headers: {})
@@ -111,7 +111,22 @@ module Fundamentalista
111
111
  # by fiscal year end, used when the balance sheet lacks the item.
112
112
  COVER = { shares_outstanding: 'EntityCommonStockSharesOutstanding' }.freeze
113
113
 
114
- INSTANT = (BALANCE.keys + Banking::INSTANT).freeze
114
+ INSURANCE = {
115
+ premiums_earned: %w[PremiumsEarnedNet DirectPremiumsEarned],
116
+ premiums_written: %w[PremiumsWrittenNet DirectPremiumsWritten],
117
+ claims_incurred: %w[PolicyholderBenefitsAndClaimsIncurredNet
118
+ LiabilityForUnpaidClaimsAndClaimsAdjustmentExpenseIncurredClaims1],
119
+ benefits_and_expenses: %w[BenefitsLossesAndExpenses],
120
+ investment_income: %w[NetInvestmentIncome GrossInvestmentIncomeOperating],
121
+ investments: %w[Investments],
122
+ reserves: %w[LiabilityForFuturePolicyBenefitsAndUnpaidClaimsAndClaimsAdjustmentExpense
123
+ LiabilityForClaimsAndClaimsAdjustmentExpense LiabilityForFuturePolicyBenefits],
124
+ unearned_premiums: %w[UnearnedPremiums],
125
+ premiums_receivable: %w[PremiumsReceivableAtCarryingValue],
126
+ reinsurance_recoverables: %w[ReinsuranceRecoverablesOnPaidAndUnpaidLosses]
127
+ }.freeze
128
+
129
+ INSTANT = (BALANCE.keys + Banking::INSTANT + Insurance::INSTANT).freeze
115
130
  end
116
131
  end
117
132
  end
@@ -44,10 +44,15 @@ module Fundamentalista
44
44
  raise QuoteUnavailableError, "EDGAR has no prices for #{company.ticker}; pass a PriceHistory"
45
45
  end
46
46
 
47
+ def segments(company, **)
48
+ raise UnsupportedPeriodError, "EDGAR's company facts carry no segments for #{company.ticker}"
49
+ end
50
+
47
51
  private
48
52
 
49
53
  STATEMENTS = { income: [IncomeStatement, Tags::INCOME], balance: [BalanceSheet, Tags::BALANCE],
50
- cash_flow: [CashFlowStatement, Tags::CASH_FLOW], banking: [Banking, Tags::BANKING] }.freeze
54
+ cash_flow: [CashFlowStatement, Tags::CASH_FLOW], banking: [Banking, Tags::BANKING],
55
+ insurance: [Insurance, Tags::INSURANCE] }.freeze
51
56
  private_constant :STATEMENTS
52
57
 
53
58
  def annual_period(facts, year, ended_on)
@@ -4,7 +4,7 @@ module Fundamentalista
4
4
  module Providers
5
5
  # Financial Modeling Prep. Needs Configuration#fmp_api_key. Serves
6
6
  # annual and quarterly periods, company profiles with beta, live
7
- # quotes, analyst estimates and daily prices.
7
+ # quotes, analyst estimates, daily prices and revenue segments.
8
8
  class FMP < Provider
9
9
  BASE_URL = 'https://financialmodelingprep.com/stable/'
10
10
 
@@ -39,6 +39,14 @@ module Fundamentalista
39
39
  PriceHistory.new(points)
40
40
  end
41
41
 
42
+ SEGMENTS = { product: 'revenue-product-segmentation', geography: 'revenue-geographic-segments' }.freeze
43
+
44
+ def segments(company, by: :product)
45
+ endpoint = SEGMENTS.fetch(by) { raise ArgumentError, "Segments come by :product or :geography, not #{by.inspect}" }
46
+ rows = fetch(endpoint, symbol: company.ticker, period: 'annual')
47
+ Segments.new(rows.to_h { |row| [row['fiscalYear'].to_i, row['data'] || {}] })
48
+ end
49
+
42
50
  def estimates(company)
43
51
  fetch('analyst-estimates', symbol: company.ticker, period: 'annual', limit: 10).map do |row|
44
52
  Estimate.new(fiscal_year_end: Date.parse(row['date']), eps: row['epsAvg'], eps_low: row['epsLow'],
@@ -24,7 +24,8 @@ module Fundamentalista
24
24
  cash_conversion_cycle].freeze
25
25
  CASH = %i[fcf_margin cash_conversion capex_to_revenue accrual_ratio].freeze
26
26
  BANKING = %i[net_interest_margin efficiency_ratio loan_to_deposit cost_of_risk allowance_to_loans].freeze
27
- ALL = (PROFITABILITY + LIQUIDITY + LEVERAGE + EFFICIENCY + WORKING_CAPITAL + CASH + BANKING).freeze
27
+ INSURANCE = %i[loss_ratio expense_ratio combined_ratio investment_yield float_to_equity].freeze
28
+ ALL = (PROFITABILITY + LIQUIDITY + LEVERAGE + EFFICIENCY + WORKING_CAPITAL + CASH + BANKING + INSURANCE).freeze
28
29
 
29
30
  attr_reader :period
30
31
 
@@ -182,15 +183,42 @@ module Fundamentalista
182
183
  Decimal.ratio(banking.allowance_for_credit_losses, banking.loans)
183
184
  end
184
185
 
186
+ # Claims incurred over premiums earned.
187
+ def loss_ratio
188
+ Decimal.ratio(insurance.claims_incurred, insurance.premiums_earned)
189
+ end
190
+
191
+ # Everything the insurer spends beyond claims, over premiums earned.
192
+ def expense_ratio
193
+ Decimal.subtract(combined_ratio, loss_ratio)
194
+ end
195
+
196
+ # Benefits, losses and expenses over premiums earned; below one the
197
+ # underwriting itself makes money.
198
+ def combined_ratio
199
+ Decimal.ratio(insurance.benefits_and_expenses, insurance.premiums_earned)
200
+ end
201
+
202
+ # Net investment income over average investments.
203
+ def investment_yield
204
+ Decimal.ratio(insurance.investment_income, average(:investments, of: :insurance))
205
+ end
206
+
207
+ # The float over equity, the leverage an insurer's investing runs on.
208
+ def float_to_equity
209
+ Decimal.ratio(insurance.float, balance.equity)
210
+ end
211
+
185
212
  # The DuPont decomposition of ROE: net margin times asset turnover times
186
213
  # the equity multiplier.
187
214
  def dupont
188
215
  { net_margin: net_margin, asset_turnover: asset_turnover, equity_multiplier: equity_multiplier, roe: roe }
189
216
  end
190
217
 
191
- # Returns every ratio keyed by name; the banking ones only for banks.
218
+ # Returns every ratio keyed by name; the banking ones only for banks,
219
+ # the insurance ones only for insurers.
192
220
  def to_h
193
- names = period.bank? ? ALL : ALL - BANKING
221
+ names = ALL - (period.bank? ? [] : BANKING) - (period.insurer? ? [] : INSURANCE)
194
222
  names.to_h { |name| [name, public_send(name)] }
195
223
  end
196
224
 
@@ -216,6 +244,10 @@ module Fundamentalista
216
244
  period.banking
217
245
  end
218
246
 
247
+ def insurance
248
+ period.insurance
249
+ end
250
+
219
251
  def average(field, of: :balance)
220
252
  current = period.public_send(of).public_send(field)
221
253
  return current unless period.prior
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fundamentalista
4
+ # Revenue by product line or by region over the years: what a company
5
+ # actually sells and where. Amounts are in the reporting currency,
6
+ # keyed by fiscal year, newest first.
7
+ #
8
+ # segments = company.segments(by: :product) # FMP
9
+ # segments.latest # => {"iPhone" => 0.209586e12, "Service" => 0.109158e12, ...}
10
+ # segments.share # => {"iPhone" => 0.503e0, ...}
11
+ # segments.growth("Service", years: 5)
12
+ #
13
+ class Segments
14
+ include Inspectable
15
+ include Serializable
16
+
17
+ def initialize(years)
18
+ @years = years.sort_by { |year, _| -year }.to_h do |year, amounts|
19
+ [year, amounts.transform_values do |amount|
20
+ Decimal.wrap(amount)
21
+ end]
22
+ end
23
+ end
24
+
25
+ # The fiscal years on file, newest first.
26
+ def years
27
+ @years.keys
28
+ end
29
+
30
+ # Every segment name that appears in any year.
31
+ def names
32
+ @years.values.flat_map(&:keys).uniq
33
+ end
34
+
35
+ # The amounts of +year+, the latest by default.
36
+ def [](year)
37
+ @years[year]
38
+ end
39
+
40
+ def latest
41
+ @years.values.first
42
+ end
43
+
44
+ # Returns each segment's share of the year's total, as rates.
45
+ def share(year = years.first)
46
+ amounts = @years.fetch(year, {})
47
+ total = amounts.values.sum(BigDecimal('0'))
48
+ amounts.transform_values { |amount| Decimal.ratio(amount, total) }
49
+ end
50
+
51
+ # Returns the compound annual growth of +name+ from the year +years+
52
+ # back to the latest, or over the whole history by default.
53
+ def growth(name, years: nil)
54
+ span = years ? [years, self.years.size - 1].min : self.years.size - 1
55
+ return nil if span < 1
56
+
57
+ first = self.years[span]
58
+ Decimal.cagr(@years.dig(first, name), @years.dig(self.years.first, name), self.years.first - first)
59
+ end
60
+
61
+ def to_h
62
+ @years
63
+ end
64
+
65
+ def inspect_attributes # :nodoc:
66
+ { years: years.size, latest: years.first, segments: names.size }
67
+ end
68
+ end
69
+ end
@@ -179,6 +179,38 @@ module Fundamentalista
179
179
  ((market_cap * equity_cost) + (debt * debt_cost)) / capital
180
180
  end
181
181
 
182
+ # Greenwald's earnings power value per share: the after tax EBIT the
183
+ # business earns today, capitalized at the +discount_rate+ as if it
184
+ # never grew, net of debt. What the company is worth with no growth
185
+ # priced in; compare it with the price to see how much growth is.
186
+ def earnings_power_value(discount_rate:)
187
+ nopat = income.ebit && income.tax_rate && (income.ebit * (1 - income.tax_rate))
188
+ return nil if nopat.nil? || shares_outstanding.nil?
189
+
190
+ equity = (nopat / Decimal.wrap(discount_rate)) - (balance.net_debt || 0)
191
+ equity / shares_outstanding
192
+ end
193
+
194
+ # The residual income value per share: book value plus the present
195
+ # value of the earnings above the +cost_of_equity+ charge on it, grown
196
+ # at +growth+ for +years+ and then fading to nothing. Rewards returns
197
+ # on equity above the cost of equity, and nothing else.
198
+ def residual_income_value(cost_of_equity:, growth: BigDecimal('0'), years: 10)
199
+ book = balance.equity
200
+ earnings = income.net_income
201
+ return nil if book.nil? || earnings.nil? || shares_outstanding.nil?
202
+
203
+ rate = Decimal.wrap(cost_of_equity)
204
+ value = book
205
+ (1..years).each do |year|
206
+ residual = earnings - (rate * book)
207
+ value += residual / ((1 + rate)**year)
208
+ earnings *= 1 + Decimal.wrap(growth)
209
+ book += earnings * (1 - (payout_ratio || 0))
210
+ end
211
+ value / shares_outstanding
212
+ end
213
+
182
214
  # The reverse DCF: the yearly free cash flow growth the price implies
183
215
  # under the given +discount_rate+ and the other DCF keywords. +nil+
184
216
  # when no growth between -50% and +100% reproduces the price.
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Fundamentalista
4
4
  # The version of the fundamentalista gem, as a string.
5
- VERSION = '0.2.0'
5
+ VERSION = '0.4.0'
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fundamentalista
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bruno Costanzo
@@ -92,6 +92,7 @@ files:
92
92
  - lib/fundamentalista/financials.rb
93
93
  - lib/fundamentalista/income_statement.rb
94
94
  - lib/fundamentalista/inspectable.rb
95
+ - lib/fundamentalista/insurance.rb
95
96
  - lib/fundamentalista/period.rb
96
97
  - lib/fundamentalista/price_history.rb
97
98
  - lib/fundamentalista/provider.rb
@@ -104,6 +105,7 @@ files:
104
105
  - lib/fundamentalista/scores/altman_z.rb
105
106
  - lib/fundamentalista/scores/beneish.rb
106
107
  - lib/fundamentalista/scores/piotroski.rb
108
+ - lib/fundamentalista/segments.rb
107
109
  - lib/fundamentalista/serializable.rb
108
110
  - lib/fundamentalista/source.rb
109
111
  - lib/fundamentalista/statement.rb