源代码:复权.ipynb

导入模块

  1. import QUANTAXIS as QA
  2. import pandas as pd
  1. jqdatasdk not installed
  2. you are using non-interactive mdoel quantaxis
  3. ModuleNotFoundError: No module named 'tkinter'
  4. centos 6: sudo yum install tk-devel tcl-devel sqlite-devel gdbm-devel xz-devel readline-devel
  5. cnetos 7: sudo yum install tk-devel tcl-devel sqlite-devel gdbm-devel xz-devel readline-devel python3-tk
  6. ubuntu: sudo apt install python3-tk
  1. daterange = lambda df, s: df[df.index<=pd.to_datetime(s)][-3:] # 筛选出某个时间往前的三个交易日数据

QA 前复权

查看 2016-06-15 ~ 2016-06-17 时间的前复权数据:

方法一:to_qfq

使用全部历史数据进行复权计算,虽然传入的是历史某段时间,但前复权的基点是最新交易日

  1. QA.QA_fetch_stock_day_adv("000001",'2016-06-15','2016-06-17').to_qfq().data
open high low close volume amount adj
date code
2016-06-15 000001 7.942664 8.057998 7.934975 8.027242 378172.0 394032992.0 0.768893
2016-06-16 000001 8.024901 8.052993 7.987445 8.024901 386670.0 331192000.0 0.936395
2016-06-17 000001 8.024901 8.062357 7.996809 8.034265 315173.0 270236000.0 0.936395

方法二:QA_data_stock_to_fq

to_qfq 等价于下面的方法:先查询所有时间的不复权数据,然后进行复权处理

  1. # 最近数据: 2021-08-25
  2. tmp = QA.QAData.data_fq.QA_data_stock_to_fq(QA.QA_fetch_stock_day_adv("000001",'1990-01-01','2021-08-25').data).reset_index("code")
  3. daterange(tmp, "2016-06-17")
code open high low close volume amount preclose adj
date
2016-06-15 000001 7.942664 8.057998 7.934975 8.027242 378172.0 394032992.0 7.996486 0.768893
2016-06-16 000001 8.024901 8.052993 7.987445 8.024901 386670.0 331192000.0 8.027242 0.936395
2016-06-17 000001 8.024901 8.062357 7.996809 8.034265 315173.0 270236000.0 8.024901 0.936395

方法三:基于 xdxr

往深研究,发现 QA 基于分红与送股的 xrdr 数据进行复权计算的。

  1. hist = QA.QA_fetch_stock_day("000001",'1990-01-01','2021-08-25', "pd")
  2. xdxr = QA.QAFetch.QATdx.QA_fetch_get_stock_xdxr("000001")
  3. tmp = QA.QAData.data_fq._QA_data_stock_to_fq(hist, xdxr, "qfq")
  4. daterange(tmp, "2016-06-17")
  1. QUANTAXIS>> Selecting the Best Server IP of TDX
  2. QUANTAXIS>> === The BEST SERVER ===
  3. stock_ip 117.184.140.156 future_ip 119.97.185.5
  4. USING DEFAULT STOCK IP
  5. USING DEFAULT FUTURE IP
code open high low close volume amount date preclose adj
date
2016-06-15 000001 7.942664 8.057998 7.934975 8.027242 378172.0 394032992.0 2016-06-15 7.996486 0.768893
2016-06-16 000001 8.024901 8.052993 7.987445 8.024901 386670.0 331192000.0 2016-06-16 8.027242 0.936395
2016-06-17 000001 8.024901 8.062357 7.996809 8.034265 315173.0 270236000.0 2016-06-17 8.024901 0.936395

与交易所昨收数据一致

例子:平安银行 2016-06-16 日除权除息,QA 计算的这日昨收(即前收)与交易所官网的数据一致(第三位小数点四舍五入)。
注意区间的右段不能截至最新数据,而应该截至 2016-06-16 至下一除权除息日之间(不包括下一除权除息日那天)。

  1. pingan = QA.QA_fetch_stock_day("000001", "2016-06-15", "2016-06-17", "pd")
  2. display("不复权", pingan)
  3. display("前复权", QA.QAData.data_fq._QA_data_stock_to_fq(pingan, xdxr, "qfq"))
  1. '不复权'
code open high low close volume amount date
date
2016-06-15 000001 10.33 10.48 10.32 10.44 378172.0 394032992.0 2016-06-15
2016-06-16 000001 8.57 8.60 8.53 8.57 386670.0 331192000.0 2016-06-16
2016-06-17 000001 8.57 8.61 8.54 8.58 315173.0 270236000.0 2016-06-17
  1. '前复权'
code open high low close volume amount date preclose adj
date
2016-06-15 000001 8.482177 8.605345 8.473966 8.5725 378172.0 394032992.0 2016-06-15 NaN 0.821121
2016-06-16 000001 8.570000 8.600000 8.530000 8.5700 386670.0 331192000.0 2016-06-16 8.5725 1.000000
2016-06-17 000001 8.570000 8.610000 8.540000 8.5800 315173.0 270236000.0 2016-06-17 8.5700 1.000000

深圳交易所数据:

交易日期 证券代码 证券简称 前收 开盘 最高 最低 今收 涨跌幅 (%) 成交量 (万) 成交金额 (万元) 市盈率
2016-06-15 000001 平安银行 10.40 10.33 10.48 10.32 10.44 0.38 3,781.72 39,403.36 6.83
2016-06-16 000001 平安银行 8.57 8.57 8.60 8.53 8.57 0.00 3,866.70 33,119.24 6.73
2016-06-17 000001 平安银行 8.57 8.57 8.61 8.54 8.58 0.12 3,151.73 27,023.66 6.74

剖析 QA 复权源码

如果再往下探索,你会发现 TDX 并没有记录交易所给的“昨收”数据,但是基于 TDX 的 gbbq (股本变迁)文件,
也就是 xdxr ,我们可以推算这个“昨收”。

  1. today = QA.QA_fetch_stock_day("000001",'1990-01-01','2021-08-25', "pd")
  1. # `_QA_data_stock_to_fq` 的参数
  2. fqtype = "qfq"
  3. bfq_data = today.assign(if_trade=1)
  4. info = xdxr.query('category==1')
  1. # 按照时间合并不复权数据和分红送股数据
  2. data = pd.concat([bfq_data, info.loc[bfq_data.index[0] : bfq_data.index[-1], ["category"]]], axis=1)
  3. # 当天是否为交易日:因为除权日不一定是交易日。但是通达信的股票数据的日期一定是交易日
  4. # 检查非交易日的除权日: data[~data.index.isin(today.index)]
  5. data["if_trade"].fillna(value=0, inplace=True)
  6. # 把非交易日的除权除息日数据按照上一个交易日填补完整; category 列第一次除权除息之前依然存在缺失值
  7. data = data.fillna(method="ffill")
  8. # 合并股权信息,缺失值补充方式为填 0
  9. data = pd.concat(
  10. [
  11. data,
  12. info.loc[
  13. bfq_data.index[0] : bfq_data.index[-1],
  14. ["fenhong", "peigu", "peigujia", "songzhuangu"],
  15. ],
  16. ],
  17. axis=1,
  18. )
  19. data = data.fillna(0)
  20. # 昨收:交易所官网是有这个数据的,根据分红送股情况计算的,但是通达信没有,所以需要手动计算
  21. data['preclose'] = (
  22. data['close'].shift(1) * 10 - data['fenhong'] +
  23. data['peigu'] * data['peigujia']
  24. ) / (10 + data['peigu'] + data['songzhuangu'])

  1. # 这个数据只有最后一天数据为缺失值,而且最后一次除权之后、第一次除权之前,这个系数都是 1
  2. tmp = (data['preclose'].shift(-1) / data['close'])#.fillna(1)[::-1].cumprod()
  3. display(daterange(tmp, "2016-06-17"))
  4. # 计算系数:把上面的数据反向累积相乘
  5. tmp = (data['preclose'].shift(-1) / data['close']).fillna(1)[::-1].cumprod()
  6. display(daterange(tmp[::-1], "2016-06-17"))
  1. date
  2. 2016-06-15 0.821121
  3. 2016-06-16 1.000000
  4. 2016-06-17 1.000000
  5. dtype: float64
  6. date
  7. 2016-06-15 0.768893
  8. 2016-06-16 0.936395
  9. 2016-06-17 0.936395
  10. dtype: float64

  1. if fqtype in ['01', 'qfq']:
  2. data['adj'] = (data['preclose'].shift(-1) /
  3. data['close']).fillna(1)[::-1].cumprod()
  4. else:
  5. data['adj'] = (data['close'] /
  6. data['preclose'].shift(-1)).cumprod().shift(1).fillna(1)
  7. for col in ['open', 'high', 'low', 'close', 'preclose']:
  8. data[col] = data[col] * data['adj']
  9. data['volume'] = data['volume'] if 'volume' in data.columns else data['vol']
  10. try:
  11. data['high_limit'] = data['high_limit'] * data['adj']
  12. data['low_limit'] = data['high_limit'] * data['adj']
  13. except:
  14. pass
  15. data.query('if_trade==1 and open != 0').drop(
  16. ['fenhong',
  17. 'peigu',
  18. 'peigujia',
  19. 'songzhuangu',
  20. 'if_trade',
  21. 'category'],
  22. axis=1,
  23. errors='ignore'
  24. )
code open high low close volume amount date preclose adj
date
1991-04-03 000001 0.187452 0.187452 0.187452 0.187452 32768.5 5.000000e+03 1991-04-03 NaN 0.003826
1991-04-04 000001 0.186534 0.186534 0.186534 0.186534 4098.0 1.500000e+04 1991-04-04 0.187452 0.003826
1991-04-05 000001 0.185616 0.185616 0.185616 0.185616 2.0 1.000000e+04 1991-04-05 0.186534 0.003826
1991-04-06 000001 0.184698 0.184698 0.184698 0.184698 7.0 3.400000e+04 1991-04-06 0.185616 0.003826
1991-04-08 000001 0.183780 0.183780 0.183780 0.183780 2.0 1.000000e+04 1991-04-08 0.184698 0.003826
2021-08-19 000001 20.480000 20.620000 20.020000 20.340000 649975.0 1.315320e+09 2021-08-19 20.620000 1.000000
2021-08-20 000001 19.970000 20.070000 18.700000 19.420000 1614628.0 3.119153e+09 2021-08-20 20.340000 1.000000
2021-08-23 000001 19.500000 20.050000 19.110000 19.300000 1191849.0 2.315872e+09 2021-08-23 19.420000 1.000000
2021-08-24 000001 19.350000 19.970000 19.210000 19.360000 868953.0 1.690761e+09 2021-08-24 19.300000 1.000000
2021-08-25 000001 19.420000 19.470000 18.950000 19.160000 698083.0 1.341330e+09 2021-08-25 19.360000 1.000000

7228 rows × 10 columns

涨跌幅复权

以收盘价为例:
不复权收盘价/不复权的前收盘价 = 前复权收盘价/前复权的前收盘价 => 核心:复权与不复,股票价格的涨跌幅不变
前复权收盘价 = 不复权收盘价 * (前复权的前收盘价/不复权的前收盘价)
前复权的前收盘价:一般都是前一天的收盘价,但是在除权日那天,是除权后的前收盘价

方法一:计算复权因子

复权因子:假设上市日以 1 单位(比如 元)购买股票(注意不是按数量购买,而是按价格购买),相当于上市日初始的 1 单位资产,每个交易日会导致这 1 单位资产随涨跌幅变化,这个相对于初始资产的变化就是复权因子。
复权:利用某个日期的实际价格
优点:

  1. 一套逻辑,从而很方便地计算 前后复权、定点复权
  2. 当日为除权日时,无需修改历史复权因子
  3. 有些基于涨跌幅的指标可以无需计算前后复权,直接使用复权因子

缺点:因为完全基于收盘价,所以计算任何交易时刻的复权价之前需要先计算这个时刻与收盘价的比值

拓展资料:量化投资中,如何最准确的计算前后复权价(附代码)【邢不行】

  1. # 每日涨跌幅,注意前收盘价必须是除权的前收盘价
  2. factor = (data.close/data.preclose).fillna(1).cumprod()

前复权

  1. # 前复权
  2. qfq_multi = data.close[-1]/factor[-1] # 这一步其实就是定点复权:取最新一天就是前复权;取上市日就是后复权
  3. factor * qfq_multi
  1. date
  2. 1991-04-03 0.187452
  3. 1991-04-04 0.186534
  4. 1991-04-05 0.185616
  5. 1991-04-06 0.184698
  6. 1991-04-08 0.183780
  7. ...
  8. 2021-08-19 20.340000
  9. 2021-08-20 19.420000
  10. 2021-08-23 19.300000
  11. 2021-08-24 19.360000
  12. 2021-08-25 19.160000
  13. Length: 7229, dtype: float64
  1. # 前复权 + 开盘价
  2. data.open / data.close * factor * qfq_multi
  1. date
  2. 1991-04-03 0.187452
  3. 1991-04-04 0.186534
  4. 1991-04-05 0.185616
  5. 1991-04-06 0.184698
  6. 1991-04-08 0.183780
  7. ...
  8. 2021-08-19 20.480000
  9. 2021-08-20 19.970000
  10. 2021-08-23 19.500000
  11. 2021-08-24 19.350000
  12. 2021-08-25 19.420000
  13. Length: 7229, dtype: float64

后复权

  1. # 后复权
  2. hfq_multi = data.close[0]/factor[0]
  3. factor * hfq_multi
  1. date
  2. 1991-04-03 0.187452
  3. 1991-04-04 0.186534
  4. 1991-04-05 0.185616
  5. 1991-04-06 0.184698
  6. 1991-04-08 0.183780
  7. ...
  8. 2021-08-19 20.340000
  9. 2021-08-20 19.420000
  10. 2021-08-23 19.300000
  11. 2021-08-24 19.360000
  12. 2021-08-25 19.160000
  13. Length: 7229, dtype: float64

定点复权

  1. # 定点复权:以 2016-06-16 除权日为基准,改日往前至上市日是前复权,往后至今是后复权
  2. base = (data.index<=pd.to_datetime("2016-06-16")).sum()-1
  3. multi = data.close[base]/factor[base]
  4. factor * multi
  1. date
  2. 1991-04-03 0.187452
  3. 1991-04-04 0.186534
  4. 1991-04-05 0.185616
  5. 1991-04-06 0.184698
  6. 1991-04-08 0.183780
  7. ...
  8. 2021-08-19 20.340000
  9. 2021-08-20 19.420000
  10. 2021-08-23 19.300000
  11. 2021-08-24 19.360000
  12. 2021-08-25 19.160000
  13. Length: 7229, dtype: float64
  1. a = factor * multi
  2. a[:base][-3:]
  1. date
  2. 2016-06-13 7.942664
  3. 2016-06-14 7.996486
  4. 2016-06-15 8.027242
  5. dtype: float64
  1. # 定点复权:以 2016-06-16 除权日为基准 + 开盘价
  2. data.open / data.close * factor * multi
  1. date
  2. 1991-04-03 0.187452
  3. 1991-04-04 0.186534
  4. 1991-04-05 0.185616
  5. 1991-04-06 0.184698
  6. 1991-04-08 0.183780
  7. ...
  8. 2021-08-19 20.480000
  9. 2021-08-20 19.970000
  10. 2021-08-23 19.500000
  11. 2021-08-24 19.350000
  12. 2021-08-25 19.420000
  13. Length: 7229, dtype: float64

定点复权与前后复权的关系

  1. # 定点复权:以最后交易日为基准 <=> 前复权
  2. base = (data.index<=pd.to_datetime("2021-08-24")).sum()-1
  3. multi = data.close[base]/factor[base]
  4. factor * multi
  1. date
  2. 1991-04-03 0.187452
  3. 1991-04-04 0.186534
  4. 1991-04-05 0.185616
  5. 1991-04-06 0.184698
  6. 1991-04-08 0.183780
  7. ...
  8. 2021-08-19 20.340000
  9. 2021-08-20 19.420000
  10. 2021-08-23 19.300000
  11. 2021-08-24 19.360000
  12. 2021-08-25 19.160000
  13. Length: 7229, dtype: float64
  1. # 定点复权:以上市日为基准 <=> 后复权
  2. base = (data.index<=pd.to_datetime("1991-04-03")).sum()-1
  3. multi = data.close[base]/factor[base]
  4. factor * multi
  1. date
  2. 1991-04-03 0.187452
  3. 1991-04-04 0.186534
  4. 1991-04-05 0.185616
  5. 1991-04-06 0.184698
  6. 1991-04-08 0.183780
  7. ...
  8. 2021-08-19 20.340000
  9. 2021-08-20 19.420000
  10. 2021-08-23 19.300000
  11. 2021-08-24 19.360000
  12. 2021-08-25 19.160000
  13. Length: 7229, dtype: float64

方法二:计算调整系数

优点:方便计算任何交易时刻的复权价,比如直接把开盘价乘以调整系数就能得到复权的开盘价
缺点:需要维护三套代码来计算前后复权、定点复权的调整系数;当日为除权日时,更新历史系数
QUANTAXIS 计算复权的代码

前复权

  1. # 前复权
  2. data.close * (data['preclose'].shift(-1) / data['close']).fillna(1)[::-1].cumprod()
  1. date
  2. 1991-04-03 0.187452
  3. 1991-04-04 0.186534
  4. 1991-04-05 0.185616
  5. 1991-04-06 0.184698
  6. 1991-04-08 0.183780
  7. ...
  8. 2021-08-19 20.340000
  9. 2021-08-20 19.420000
  10. 2021-08-23 19.300000
  11. 2021-08-24 19.360000
  12. 2021-08-25 19.160000
  13. Length: 7229, dtype: float64

后复权

  1. # 后复权
  2. data.close * (data['close'] / data['preclose'].shift(-1)).cumprod().shift(1).fillna(1)
  1. date
  2. 1991-04-03 0.187452
  3. 1991-04-04 0.186534
  4. 1991-04-05 0.185616
  5. 1991-04-06 0.184698
  6. 1991-04-08 0.183780
  7. ...
  8. 2021-08-19 20.340000
  9. 2021-08-20 19.420000
  10. 2021-08-23 19.300000
  11. 2021-08-24 19.360000
  12. 2021-08-25 19.160000
  13. Length: 7229, dtype: float64

定点复权

  1. # 定点复权
  2. def ddfq(col="close", base=data.shape[0]):
  3. data_before = data[:base]
  4. data_after = data[base:]
  5. b = data_before[col] * (data_before.preclose.shift(-1) / data_before.close).fillna(1)[::-1].cumprod()
  6. a = data_after[col] * (data_after.close / data_after.preclose.shift(-1)).cumprod().shift(1).fillna(1)
  7. return pd.concat([b, a])
  1. # 定点复权:以 2016-06-16 除权日为基准
  2. ddfq("close", (data.index<=pd.to_datetime("2016-06-16")).sum())
  1. date
  2. 1991-04-03 0.187452
  3. 1991-04-04 0.186534
  4. 1991-04-05 0.185616
  5. 1991-04-06 0.184698
  6. 1991-04-08 0.183780
  7. ...
  8. 2021-08-19 20.340000
  9. 2021-08-20 19.420000
  10. 2021-08-23 19.300000
  11. 2021-08-24 19.360000
  12. 2021-08-25 19.160000
  13. Length: 7229, dtype: float64
  1. # 定点复权:以 2016-06-16 除权日为基准 + 开盘价
  2. ddfq("open", (data.index<=pd.to_datetime("2016-06-16")).sum())
  1. date
  2. 1991-04-03 0.187452
  3. 1991-04-04 0.186534
  4. 1991-04-05 0.185616
  5. 1991-04-06 0.184698
  6. 1991-04-08 0.183780
  7. ...
  8. 2021-08-19 20.480000
  9. 2021-08-20 19.970000
  10. 2021-08-23 19.500000
  11. 2021-08-24 19.350000
  12. 2021-08-25 19.420000
  13. Length: 7229, dtype: float64

定点复权与前后复权的关系

  1. # 定点复权:以最后交易日为基准 <=> 前复权
  2. base = (data.index<=pd.to_datetime("2021-08-24")).sum()
  3. ddfq("close", base)
  1. date
  2. 1991-04-03 0.187452
  3. 1991-04-04 0.186534
  4. 1991-04-05 0.185616
  5. 1991-04-06 0.184698
  6. 1991-04-08 0.183780
  7. ...
  8. 2021-08-19 20.340000
  9. 2021-08-20 19.420000
  10. 2021-08-23 19.300000
  11. 2021-08-24 19.360000
  12. 2021-08-25 19.160000
  13. Length: 7229, dtype: float64
  1. # 定点复权:以上市日为基准 <=> 后复权
  2. base = (data.index<=pd.to_datetime("1991-04-03")).sum()
  3. ddfq("close", base)
  1. date
  2. 1991-04-03 0.187452
  3. 1991-04-04 0.186534
  4. 1991-04-05 0.185616
  5. 1991-04-06 0.184698
  6. 1991-04-08 0.183780
  7. ...
  8. 2021-08-19 20.340000
  9. 2021-08-20 19.420000
  10. 2021-08-23 19.300000
  11. 2021-08-24 19.360000
  12. 2021-08-25 19.160000
  13. Length: 7229, dtype: float64