1株当たり利益(EPS:Earnings Per Share)の計算方法
EPS(円) = 当期純利益 ÷ 発行済株式数
こうなった
生のデータや計算ロジックを外部に晒さないように気をつけて実装してみた
数値の基底クラス
numerical_value.rb
require 'bigdecimal'
class NumericalValue
# @param [BigDecimal, Integer] value
def initialize(value)
@value = value
raise TypeError, '値をセットしてください' if @value.nil? # 値必須ということにするのでチェック
post_initialize(@value) # 継承先での実装を強制し必要に応じてチェックを行う
end
# 計算に使う
# 新しいオブジェクトを返すので@valueが変わってしまう心配は無い
def to_d
BigDecimal(@value.to_s)
end
end
当期純利益クラス
net_income.rb
class NetIncome < NumericalValue
# @param [Integer] value (単位:百万円)
def post_initialize(value)
raise TypeError, '当期純利益を指定してください' if value.nil?
end
# EPSを計算して返す
# @param [OutstandingShares] outstanding_shares
def eps(outstanding_shares)
raise TypeError, 'OutstandingSharesを指定してください' unless outstanding_shares.kind_of?(OutstandingShares)
eps = to_d * 1_000_000 / outstanding_shares.to_d
EPS.new(eps)
end
end
発行済み株数
outstanding_shares.rb
class OutstandingShares < NumericalValue
# @param [Integer] value (単位:株)
def post_initialize(value)
raise TypeError, '発行済み株数は1以上で指定してください' unless value.positive?
end
end
一株当たり純利益
eps.rb
class EPS < NumericalValue
# @param [BigDecimal] value (単位:円)
def post_initialize(value)
# 特に無し
end
end
テストコード
net_income_spec.rb
require 'spec_helper'
RSpec.describe NetIncome do
describe 'EPSを計算' do
before do
net_income = described_class.new(net_income_value)
outstanding_shares = OutstandingShares.new(outstanding_shares_value)
@eps = net_income.eps(outstanding_shares)
end
context '純資産がプラスの場合(Softbank:9984)' do
let(:net_income_value) { 1_411_199 }
let(:outstanding_shares_value) { 1_100_660_365 }
it { expect(@eps.to_d).to be_within(0.01).of(1282.13) }
end
context '純資産がマイナスの場合(住石HD:1514)' do
let(:net_income_value) { -3806 }
let(:outstanding_shares_value) { 76_629_000 }
it { expect(@eps.to_d).to be_within(0.01).of(-49.66) }
end
context '純資産が0の場合(新内外綿:3125)' do
let(:net_income_value) { 0 }
let(:outstanding_shares_value) { 1_955_569 }
it { expect(@eps.to_d).to be_zero }
end
end
describe 'EPSを計算(想定外)' do
context '発行済み株式数オブジェクトでない場合' do
before do
@net_income = described_class.new(1)
@dummy_outstanding_shares = BigDecimal('1')
end
it { expect { @net_income.eps(@dummy_outstanding_shares) }.to raise_error(TypeError) }
end
end
end
感想
まだそんなにメリットを感じてない、がIntegerやBigDecimalをそのまま扱うよりははるかにマシだとは思う
環境
Ruby 2.7.0-dev
補足
- 今回initialize時に下限のチェックだけ書いたが本来は上限も設定すべき