Skip to content

关于通达信复权算法公式的分享。 #39

Description

@deathknight0718

通达信软件前后复权计算公式

发现该项目的复权数据较为混乱。

经过数据校对,目前已经实现了与通达信的复权数据 100% 匹配。

个人认为由于复权的时间窗口是动态的,所以固定的复权因子是没有什么意义的,必须实时计算。

现在分享一下复权算法与数据核对副本。

选择了两个股票,洋河股份每年大额幅度分红派息,而运达股份含有配股和配股价的权息时间数据。

权息数据

这是通达信的权息数据格式,通过通达信软件或者 GBBQ 解码可以得到相关资料,其中的重点在于送转股、分红、配股、配股价这四个字段,流通股和股本数据对通达信来说不影响复权数据。

权息日 类别 送转股 分红 配股 配股价 流通股变动 后流通股 总股本变动 后总股本
20250707 除权除息 22.600
20240708 除权除息 22.600
20230705 除权除息 25.500
20220711 除权除息 25.400
20210712 除权除息 18.100
20210308 股本变化 0.0 1649103.8 -2110.0 1986852.0
20200615 除权除息 12.600
20190708 除权除息 8.800
20180709 除权除息 9.100
20170710 除权除息 29.700
20160704 除权除息 3.200
20150615 除权除息 7.400
20140714 除权除息 9.100
20131009 股本变化 18000.0 1649103.8 0.0 1988962.0
20130708 除权除息 9.600
20120611 除权除息 9.000
20110613 除权除息 7.500
20101011 股本变化 1451103.8 1631103.8 0.0 1988962.0
20100705 除权除息 5.300
20090622 除权除息 4.600
20080604 除权除息 1.800
20080109 股本变化 54000.0 180000.0 0.0 1988962.0
20071009 股本变化 126000.0 126000.0 180000.0 1988962.0

复权计算

已知集合向量: $ \vec{S} = { \vec{s_0}, \vec{s_1}, ..., \vec{s_n} } = { (t_0, c_0), (t_1, c_1), ..., (t_n, c_n) } $

同时根据权息数据设定权息集合: $ \vec{A} = { (\alpha_i, \beta_i, \gamma_i, \epsilon_i) \mid t_0 \leq t_{\alpha_i} \leq t_n } $

其中:

  • $ \alpha $ 代表分红(如,每 10 股分红 1 元)
  • $ \beta $ 代表送转股(如,每 10 股派发 2 股)
  • $ \gamma $ 代表配股(如,每 10 股配发 2 股)
  • $ \epsilon $ 代表配股价(如,每股配价 9 元)

注意,配股价按每股计算,其他均按每 10 股计算。

前复权计算公式

通达信使用的是累计前复权模式,遵循如下约定。

时间窗口往前递归,即:

$ t_n \rightarrow t_{n-1} \rightarrow ... \rightarrow t_{0} $

分红进行除权累加递归,满足公式:

$ D(t_i) = D(t_{i+1}) \times (1 + 0.1 \cdot \beta_{t_{i+1}}) \times (1 + 0.1 \cdot \gamma_{t_{i+1}}) + \alpha_{t_{i+1}} $

送转股满足公式:

$ B(t_i) = B(t_{i+1}) \times (1 + 0.1\beta_{t_{i+1}}) + \beta_{t_{i+1}} \Rightarrow 1 + 0.1 \cdot B(t_i) = (1 + 0.1 \cdot B(t_{i+1})) \times (1 + 0.1 \cdot \beta_{i+1}) $

配股满足公式:

$ G(t_i) = G(t_{i+1}) \times (1 + 0.1\gamma_{t_{i+1}}) + \gamma_{t_{i+1}} \Rightarrow 1 + 0.1 \cdot G(t_i) = (1 + 0.1 \cdot G(t_{i+1})) \times (1 + 0.1 \cdot \gamma_{i+1}) $

配股价满足公式:

$ E(t_i) = E(t_{i+1}) \times (1 + 0.1\epsilon_{t_{i+1}}) + \epsilon_{t_{i+1}} \Rightarrow 1 + 0.1 \cdot E(t_i) = (1 + 0.1 \cdot E(t_{i+1})) \times (1 + 0.1 \cdot \epsilon_{i+1}) $

对于除权收盘价 $ c'_i $ 满足如下公式:

$ c'_i \times (1 + 0.1 \cdot B(t_i)) \times (1 + 0.1 \cdot G(t_i)) = c_i - 0.1 \cdot D(t_i) + E(t_i) \times 0.1 \cdot G(t_i) $

附带 POSTGRES 实时查询语法

    WITH RECURSIVE tdays AS (
        SELECT
            dt.code_, dt.date_,
            dt.open_, dt.high_, dt.low_, dt.close_,
            dt.amount_, dt.volume_,
            COALESCE(fc.val0_, 0.0) AS dividend_,  -- 分红,每 10 股派现金 x 元
            COALESCE(fc.val1_, 0.0) AS allotment_, -- 配股价,每股配股价 x 元
            COALESCE(fc.val2_, 0.0) AS bonus_,     -- 送转股,每 10 股送转股比例 x 股
            COALESCE(fc.val3_, 0.0) AS rights_,    -- 配股,每 10 股配股比例 x 股
            ROW_NUMBER() OVER (ORDER BY dt.date_ DESC) AS rn
        FROM qaas.fli_day_trade dt
        LEFT JOIN qaas.fli_capital fc ON fc.exchange_ = dt.exchange_ AND fc.code_ = dt.code_ AND fc.date_ = dt.date_ AND fc.category_ = 1
        WHERE dt.exchange_ = :exchange AND dt.code_ = :code AND dt.date_ <= :date ORDER BY dt.date_ DESC LIMIT :days
    ),
    accumulated AS (
        SELECT
            ds.code_, ds.date_, ds.rn,
            ds.open_, ds.high_, ds.low_, ds.close_,
            ds.amount_, ds.volume_,
            ds.dividend_, ds.dividend_ AS dividend_acc_,
            ds.allotment_, ds.allotment_ AS allotment_acc_,
            ds.bonus_, ds.bonus_ AS bonus_acc_,
            ds.rights_, ds.rights_ AS rights_acc_
        FROM tdays ds
        WHERE ds.rn = 1
        UNION ALL
        SELECT
            ds.code_, ds.date_, ds.rn,
            ds.open_, ds.high_, ds.low_, ds.close_,
            ds.amount_, ds.volume_,
            ds.dividend_, ac.dividend_acc_ * (1 + ac.rights_ * 0.1) * (1 + ac.bonus_ * 0.1) + ac.dividend_ AS dividend_acc_,
            ds.allotment_, ac.allotment_ + ac.allotment_acc_ * (1 + ac.rights_ * 0.1) * (1 + ac.bonus_ * 0.1) AS allotment_acc_,
            ds.bonus_, ac.bonus_acc_ * (1 + ac.bonus_ * 0.1) + ac.bonus_ AS bonus_acc_,
            ds.rights_, ac.rights_acc_ * (1 + ac.rights_ * 0.1) + ac.rights_ AS rights_acc_
        FROM tdays ds
        JOIN accumulated ac ON ds.rn = ac.rn + 1
    ),
    factors AS (
        SELECT
            ac.code_, ac.date_,
            ac.amount_, ac.volume_,
            (ac.open_ - ac.dividend_acc_ * 0.1 + ac.allotment_acc_ * ac.rights_acc_ * 0.1) / ((1 + ac.bonus_acc_ * 0.1) * (1 + ac.rights_acc_ * 0.1)) AS open_,
            (ac.high_ - ac.dividend_acc_ * 0.1 + ac.allotment_acc_ * ac.rights_acc_ * 0.1) / ((1 + ac.bonus_acc_ * 0.1) * (1 + ac.rights_acc_ * 0.1)) AS high_,
            (ac.low_ - ac.dividend_acc_ * 0.1 + ac.allotment_acc_ * ac.rights_acc_ * 0.1) / ((1 + ac.bonus_acc_ * 0.1) * (1 + ac.rights_acc_ * 0.1)) AS low_,
            (ac.close_ - ac.dividend_acc_ * 0.1 + ac.allotment_acc_ * ac.rights_acc_ * 0.1) / ((1 + ac.bonus_acc_ * 0.1) * (1 + ac.rights_acc_ * 0.1)) AS close_
        FROM accumulated ac
    )
    SELECT
        fs.code_, fs.date_,
        fs.open_, fs.high_, fs.low_, fs.close_,
        fs.amount_, fs.volume_
    FROM factors fs
    ORDER BY fs.date_ ASC;

后复权计算公式

时间窗口往后递归:

$ t_0 \rightarrow t_1 \rightarrow ... \rightarrow t_{n} $

分红进行除权累加递归,满足公式:

$ D(t_{i+1}) = D(t_i) + (1 + 0.1 \cdot B_{t_{i}}) \times (1 + 0.1 \cdot G_{t_{i}}) \times \alpha_{t_{i+1}} $

送转股满足公式:

$ 1 + 0.1 \cdot B(t_{i+1}) = (1 + 0.1 \cdot B(t_i)) \times (1 + 0.1 \cdot \beta_{t_{i+1}}) $

配股满足公式:

$ 1 + 0.1 \cdot G(t_{i+1}) = (1 + 0.1 \cdot G(t_i)) \times (1 + 0.1 \cdot \gamma_{t_{i+1}}) $

配股价满足公式:

$ E(t_{i+1}) = E(t_i) + \epsilon_{t_{i+1}} \times (1 + 0.1 \cdot B(t_i)) \times (1 + 0.1 \cdot G(t_i)) $

对于除权收盘价 $ c'_i $ 满足如下公式:

$ c'i = c_i \cdot (1 + 0.1 \cdot B(t{i+1})) \cdot (1 + 0.1 \cdot G(t_{i+1})) + 0.1 \cdot D(t_{i+1}) - E(t_{i+1}) \times 0.1 \cdot G(t_{i+1}) $

附带 POSTGRES 实时查询语法

    WITH RECURSIVE tdays AS (
        SELECT
            dt.code_, dt.date_,
            dt.open_, dt.high_, dt.low_, dt.close_,
            dt.amount_, dt.volume_,
            COALESCE(fc.val0_, 0.0) AS dividend_,  -- 分红,每 10 股派现金 x 元
            COALESCE(fc.val1_, 0.0) AS allotment_, -- 配股价,每股配股价 x 元
            COALESCE(fc.val2_, 0.0) AS bonus_,     -- 送转股,每 10 股送转股比例 x 股
            COALESCE(fc.val3_, 0.0) AS rights_,    -- 配股,每 10 股配股比例 x 股
            ROW_NUMBER() OVER (ORDER BY dt.date_) AS rn
        FROM qaas.fli_day_trade dt
        LEFT JOIN qaas.fli_capital fc ON fc.exchange_ = dt.exchange_ AND fc.code_ = dt.code_ AND fc.date_ = dt.date_ AND fc.category_ = 1
        WHERE dt.exchange_ = :exchange AND dt.code_ = :code AND dt.date_ >= :date ORDER BY dt.date_ LIMIT :days
    ),
    accumulated AS (
        SELECT
            ds.code_, ds.date_, ds.rn,
            ds.open_, ds.high_, ds.low_, ds.close_,
            ds.amount_, ds.volume_,
            ds.dividend_, ds.dividend_ AS dividend_acc_,
            ds.allotment_, ds.allotment_ AS allotment_acc_,
            ds.bonus_, ds.bonus_ AS bonus_acc_,
            ds.rights_, ds.rights_ AS rights_acc_
        FROM tdays ds
        WHERE ds.rn = 1
        UNION ALL
        SELECT
            ds.code_, ds.date_, ds.rn,
            ds.open_, ds.high_, ds.low_, ds.close_,
            ds.amount_, ds.volume_,
            ds.dividend_, ac.dividend_acc_ + ds.dividend_ * (1 + ac.bonus_acc_ * 0.1) * (1 + ac.rights_acc_ * 0.1) AS dividend_acc_,
            ds.allotment_, ac.allotment_acc_ + ds.allotment_ * (1 + ac.bonus_acc_ * 0.1) * (1 + ac.rights_acc_ * 0.1) AS allotment_acc_,
            ds.bonus_, ((1 + ac.bonus_acc_ * 0.1) * (1 + ds.bonus_ * 0.1) - 1) * 10 AS bonus_acc_,
            ds.rights_, ((1 + ac.rights_acc_ * 0.1) * (1 + ds.rights_ * 0.1) - 1) * 10 AS rights_acc_
        FROM tdays ds
        JOIN accumulated ac ON ds.rn = ac.rn + 1
    ),
    factors AS (
        SELECT
            ac.code_, ac.date_,
            ac.amount_, ac.volume_,
            ac.open_ * (1 + ac.bonus_acc_ * 0.1) * (1 + ac.rights_acc_ * 0.1) + ac.dividend_acc_ * 0.1 - ac.allotment_acc_ * ac.rights_acc_ * 0.1 AS open_,
            ac.high_ * (1 + ac.bonus_acc_ * 0.1) * (1 + ac.rights_acc_ * 0.1) + ac.dividend_acc_ * 0.1 - ac.allotment_acc_ * ac.rights_acc_ * 0.1 AS high_,
            ac.low_ * (1 + ac.bonus_acc_ * 0.1) * (1 + ac.rights_acc_ * 0.1) + ac.dividend_acc_ * 0.1 - ac.allotment_acc_ * ac.rights_acc_ * 0.1 AS low_,
            ac.close_ * (1 + ac.bonus_acc_ * 0.1) * (1 + ac.rights_acc_ * 0.1) + ac.dividend_acc_ * 0.1 - ac.allotment_acc_ * ac.rights_acc_ * 0.1 AS close_
            FROM accumulated ac
    )
    SELECT
        fs.code_, fs.date_,
        fs.open_, fs.high_, fs.low_, fs.close_,
        fs.amount_, fs.volume_
    FROM factors fs;

002304.xlsx
300772.xlsx

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions