0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[決算]1株当たり利益(EPS)を計算する機能を設計 & 実装してみた

Last updated at Posted at 2019-09-04

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時に下限のチェックだけ書いたが本来は上限も設定すべき
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?