Calculate stock price with dividends

分红除权

如图所示,位置P后续发生了两次除权除息(分红):S1,S2,那么对于P点的前复权价格该如何计算呢?

  • 价格计算的原理
假设对于任意一次复权S,对于原始价格到复权后的价格Z的函数为f(P,S),那么根据前复权发生的次序

第一次复权的价格为
    Z1 = f(P,S1)

第二次的复权价格为
    Z2 = f(Z1,S2)
也就是
    Z2 = f(f(P,S1), S2)

如果此时我们知道前复权的P2的价格,需要反算原始价格P,该如何计算呢?
我们知道 P => Z 的函数为 f,可以求出其反函数 g,也就是通过 Z 计算 P。

根据
a) Z2 = f(Z1,S2)
就有
b) Z1 = g(Z2,S2)

再根据
c) Z1 = f(P,S1)
又有
d) P = g(Z1,S1)

把(b)代入(d)中
e) P = g(g(Z2,S2),S1)

对于前复权和后复权,两者对于除权除息的计算顺序也不太一样,但是有一点是一样的:就是按照除权点相对于P点发生的顺序从近往远应用除权。但是,对于通过复权后价格计算原始价格,其顺序又需要反过来。
  • 除权和配股
除权和配股在本质上相同,除权是配股的一种特殊形式,只不过其配股价格为0而已。
对于任意一笔除权分红(包括配股),我们将其组织为:

M : 基数(一般10股为单位)
C : 分红金额
N : 配股数量
A : 配股价格

我们假设在发生配股前的价格为P,现在要计算发生除权分红后的价格Q

Q = (M × P - C + N × A) ÷ (M + N)

以恺英网络的数据为例,在2017-09-26收盘价为36.55,并在2017-09-27发生除权分红:10转10派1.0元,那么2017-09-27的理论开盘价应该是:

(10 × 36.55 - 1.0) ÷ (10 + 10) = 18.225 ≈ 18.23

与实际相符。
  • 价格转换函数
# --------------------------------------------------

# Function: calculate

# 根据给定价格计算复权价格

# --------------------------------------------------

def calculate(price, cur_mode, new_mode)
    assert { price.is_a?(Numeric) }
    return price if cur_mode == new_mode

    # first, calculate the origin price

    case cur_mode
    when Modes::NONE        # 不复权

        origin_price = price
    when Modes::BACKWARD    # 前复权

        origin_price = (price * (BASE_SHARES + shares) + cash - shares * at_price) / BASE_SHARES
    when Modes::FORWARD     # 后复权

        origin_price = (price * BASE_SHARES + shares * at_price - cash) / (BASE_SHARES + shares)
    else
        raise 'Invalid current mode'
    end

    # then, calculate the new price

    case new_mode
    when Modes::NONE        # 不复权

        new_price = origin_price
    when Modes::BACKWARD    # 前复权

        new_price = (origin_price * BASE_SHARES - cash + shares * at_price) / (BASE_SHARES + shares)
    when Modes::FORWARD     # 后复权

        new_price = (origin_price * (BASE_SHARES + shares) - shares * at_price + cash) / BASE_SHARES
    else
        raise 'Invalid new mode'
    end
    return new_price
end