源代码:复权.ipynb
导入模块
import QUANTAXIS as QA
import pandas as pd
jqdatasdk not installed
you are using non-interactive mdoel quantaxis
ModuleNotFoundError: No module named 'tkinter'
centos 6: sudo yum install tk-devel tcl-devel sqlite-devel gdbm-devel xz-devel readline-devel
cnetos 7: sudo yum install tk-devel tcl-devel sqlite-devel gdbm-devel xz-devel readline-devel python3-tk
ubuntu: sudo apt install python3-tk
daterange = lambda df, s: df[df.index<=pd.to_datetime(s)][-3:] # 筛选出某个时间往前的三个交易日数据
QA 前复权
查看 2016-06-15 ~ 2016-06-17 时间的前复权数据:
方法一:to_qfq
使用全部历史数据进行复权计算,虽然传入的是历史某段时间,但前复权的基点是最新交易日
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
等价于下面的方法:先查询所有时间的不复权数据,然后进行复权处理
# 最近数据: 2021-08-25
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")
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 数据进行复权计算的。
hist = QA.QA_fetch_stock_day("000001",'1990-01-01','2021-08-25', "pd")
xdxr = QA.QAFetch.QATdx.QA_fetch_get_stock_xdxr("000001")
tmp = QA.QAData.data_fq._QA_data_stock_to_fq(hist, xdxr, "qfq")
daterange(tmp, "2016-06-17")
QUANTAXIS>> Selecting the Best Server IP of TDX
QUANTAXIS>> === The BEST SERVER ===
stock_ip 117.184.140.156 future_ip 119.97.185.5
USING DEFAULT STOCK IP
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 至下一除权除息日之间(不包括下一除权除息日那天)。
pingan = QA.QA_fetch_stock_day("000001", "2016-06-15", "2016-06-17", "pd")
display("不复权", pingan)
display("前复权", QA.QAData.data_fq._QA_data_stock_to_fq(pingan, xdxr, "qfq"))
'不复权'
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 |
'前复权'
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
,我们可以推算这个“昨收”。
today = QA.QA_fetch_stock_day("000001",'1990-01-01','2021-08-25', "pd")
# `_QA_data_stock_to_fq` 的参数
fqtype = "qfq"
bfq_data = today.assign(if_trade=1)
info = xdxr.query('category==1')
# 按照时间合并不复权数据和分红送股数据
data = pd.concat([bfq_data, info.loc[bfq_data.index[0] : bfq_data.index[-1], ["category"]]], axis=1)
# 当天是否为交易日:因为除权日不一定是交易日。但是通达信的股票数据的日期一定是交易日
# 检查非交易日的除权日: data[~data.index.isin(today.index)]
data["if_trade"].fillna(value=0, inplace=True)
# 把非交易日的除权除息日数据按照上一个交易日填补完整; category 列第一次除权除息之前依然存在缺失值
data = data.fillna(method="ffill")
# 合并股权信息,缺失值补充方式为填 0
data = pd.concat(
[
data,
info.loc[
bfq_data.index[0] : bfq_data.index[-1],
["fenhong", "peigu", "peigujia", "songzhuangu"],
],
],
axis=1,
)
data = data.fillna(0)
# 昨收:交易所官网是有这个数据的,根据分红送股情况计算的,但是通达信没有,所以需要手动计算
data['preclose'] = (
data['close'].shift(1) * 10 - data['fenhong'] +
data['peigu'] * data['peigujia']
) / (10 + data['peigu'] + data['songzhuangu'])
# 这个数据只有最后一天数据为缺失值,而且最后一次除权之后、第一次除权之前,这个系数都是 1
tmp = (data['preclose'].shift(-1) / data['close'])#.fillna(1)[::-1].cumprod()
display(daterange(tmp, "2016-06-17"))
# 计算系数:把上面的数据反向累积相乘
tmp = (data['preclose'].shift(-1) / data['close']).fillna(1)[::-1].cumprod()
display(daterange(tmp[::-1], "2016-06-17"))
date
2016-06-15 0.821121
2016-06-16 1.000000
2016-06-17 1.000000
dtype: float64
date
2016-06-15 0.768893
2016-06-16 0.936395
2016-06-17 0.936395
dtype: float64
if fqtype in ['01', 'qfq']:
data['adj'] = (data['preclose'].shift(-1) /
data['close']).fillna(1)[::-1].cumprod()
else:
data['adj'] = (data['close'] /
data['preclose'].shift(-1)).cumprod().shift(1).fillna(1)
for col in ['open', 'high', 'low', 'close', 'preclose']:
data[col] = data[col] * data['adj']
data['volume'] = data['volume'] if 'volume' in data.columns else data['vol']
try:
data['high_limit'] = data['high_limit'] * data['adj']
data['low_limit'] = data['high_limit'] * data['adj']
except:
pass
data.query('if_trade==1 and open != 0').drop(
['fenhong',
'peigu',
'peigujia',
'songzhuangu',
'if_trade',
'category'],
axis=1,
errors='ignore'
)
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 |
涨跌幅复权
以收盘价为例:
不复权收盘价/不复权的前收盘价 = 前复权收盘价/前复权的前收盘价 => 核心:复权与不复,股票价格的涨跌幅不变
前复权收盘价 = 不复权收盘价 * (前复权的前收盘价/不复权的前收盘价)
前复权的前收盘价:一般都是前一天的收盘价,但是在除权日那天,是除权后的前收盘价
方法一:计算复权因子
复权因子:假设上市日以 1 单位(比如 元)购买股票(注意不是按数量购买,而是按价格购买),相当于上市日初始的 1 单位资产,每个交易日会导致这 1 单位资产随涨跌幅变化,这个相对于初始资产的变化就是复权因子。
复权:利用某个日期的实际价格
优点:
- 一套逻辑,从而很方便地计算 前后复权、定点复权
- 当日为除权日时,无需修改历史复权因子
- 有些基于涨跌幅的指标可以无需计算前后复权,直接使用复权因子
缺点:因为完全基于收盘价,所以计算任何交易时刻的复权价之前需要先计算这个时刻与收盘价的比值
拓展资料:量化投资中,如何最准确的计算前后复权价(附代码)【邢不行】
# 每日涨跌幅,注意前收盘价必须是除权的前收盘价
factor = (data.close/data.preclose).fillna(1).cumprod()
前复权
# 前复权
qfq_multi = data.close[-1]/factor[-1] # 这一步其实就是定点复权:取最新一天就是前复权;取上市日就是后复权
factor * qfq_multi
date
1991-04-03 0.187452
1991-04-04 0.186534
1991-04-05 0.185616
1991-04-06 0.184698
1991-04-08 0.183780
...
2021-08-19 20.340000
2021-08-20 19.420000
2021-08-23 19.300000
2021-08-24 19.360000
2021-08-25 19.160000
Length: 7229, dtype: float64
# 前复权 + 开盘价
data.open / data.close * factor * qfq_multi
date
1991-04-03 0.187452
1991-04-04 0.186534
1991-04-05 0.185616
1991-04-06 0.184698
1991-04-08 0.183780
...
2021-08-19 20.480000
2021-08-20 19.970000
2021-08-23 19.500000
2021-08-24 19.350000
2021-08-25 19.420000
Length: 7229, dtype: float64
后复权
# 后复权
hfq_multi = data.close[0]/factor[0]
factor * hfq_multi
date
1991-04-03 0.187452
1991-04-04 0.186534
1991-04-05 0.185616
1991-04-06 0.184698
1991-04-08 0.183780
...
2021-08-19 20.340000
2021-08-20 19.420000
2021-08-23 19.300000
2021-08-24 19.360000
2021-08-25 19.160000
Length: 7229, dtype: float64
定点复权
# 定点复权:以 2016-06-16 除权日为基准,改日往前至上市日是前复权,往后至今是后复权
base = (data.index<=pd.to_datetime("2016-06-16")).sum()-1
multi = data.close[base]/factor[base]
factor * multi
date
1991-04-03 0.187452
1991-04-04 0.186534
1991-04-05 0.185616
1991-04-06 0.184698
1991-04-08 0.183780
...
2021-08-19 20.340000
2021-08-20 19.420000
2021-08-23 19.300000
2021-08-24 19.360000
2021-08-25 19.160000
Length: 7229, dtype: float64
a = factor * multi
a[:base][-3:]
date
2016-06-13 7.942664
2016-06-14 7.996486
2016-06-15 8.027242
dtype: float64
# 定点复权:以 2016-06-16 除权日为基准 + 开盘价
data.open / data.close * factor * multi
date
1991-04-03 0.187452
1991-04-04 0.186534
1991-04-05 0.185616
1991-04-06 0.184698
1991-04-08 0.183780
...
2021-08-19 20.480000
2021-08-20 19.970000
2021-08-23 19.500000
2021-08-24 19.350000
2021-08-25 19.420000
Length: 7229, dtype: float64
定点复权与前后复权的关系
# 定点复权:以最后交易日为基准 <=> 前复权
base = (data.index<=pd.to_datetime("2021-08-24")).sum()-1
multi = data.close[base]/factor[base]
factor * multi
date
1991-04-03 0.187452
1991-04-04 0.186534
1991-04-05 0.185616
1991-04-06 0.184698
1991-04-08 0.183780
...
2021-08-19 20.340000
2021-08-20 19.420000
2021-08-23 19.300000
2021-08-24 19.360000
2021-08-25 19.160000
Length: 7229, dtype: float64
# 定点复权:以上市日为基准 <=> 后复权
base = (data.index<=pd.to_datetime("1991-04-03")).sum()-1
multi = data.close[base]/factor[base]
factor * multi
date
1991-04-03 0.187452
1991-04-04 0.186534
1991-04-05 0.185616
1991-04-06 0.184698
1991-04-08 0.183780
...
2021-08-19 20.340000
2021-08-20 19.420000
2021-08-23 19.300000
2021-08-24 19.360000
2021-08-25 19.160000
Length: 7229, dtype: float64
方法二:计算调整系数
优点:方便计算任何交易时刻的复权价,比如直接把开盘价乘以调整系数就能得到复权的开盘价
缺点:需要维护三套代码来计算前后复权、定点复权的调整系数;当日为除权日时,更新历史系数
QUANTAXIS 计算复权的代码
前复权
# 前复权
data.close * (data['preclose'].shift(-1) / data['close']).fillna(1)[::-1].cumprod()
date
1991-04-03 0.187452
1991-04-04 0.186534
1991-04-05 0.185616
1991-04-06 0.184698
1991-04-08 0.183780
...
2021-08-19 20.340000
2021-08-20 19.420000
2021-08-23 19.300000
2021-08-24 19.360000
2021-08-25 19.160000
Length: 7229, dtype: float64
后复权
# 后复权
data.close * (data['close'] / data['preclose'].shift(-1)).cumprod().shift(1).fillna(1)
date
1991-04-03 0.187452
1991-04-04 0.186534
1991-04-05 0.185616
1991-04-06 0.184698
1991-04-08 0.183780
...
2021-08-19 20.340000
2021-08-20 19.420000
2021-08-23 19.300000
2021-08-24 19.360000
2021-08-25 19.160000
Length: 7229, dtype: float64
定点复权
# 定点复权
def ddfq(col="close", base=data.shape[0]):
data_before = data[:base]
data_after = data[base:]
b = data_before[col] * (data_before.preclose.shift(-1) / data_before.close).fillna(1)[::-1].cumprod()
a = data_after[col] * (data_after.close / data_after.preclose.shift(-1)).cumprod().shift(1).fillna(1)
return pd.concat([b, a])
# 定点复权:以 2016-06-16 除权日为基准
ddfq("close", (data.index<=pd.to_datetime("2016-06-16")).sum())
date
1991-04-03 0.187452
1991-04-04 0.186534
1991-04-05 0.185616
1991-04-06 0.184698
1991-04-08 0.183780
...
2021-08-19 20.340000
2021-08-20 19.420000
2021-08-23 19.300000
2021-08-24 19.360000
2021-08-25 19.160000
Length: 7229, dtype: float64
# 定点复权:以 2016-06-16 除权日为基准 + 开盘价
ddfq("open", (data.index<=pd.to_datetime("2016-06-16")).sum())
date
1991-04-03 0.187452
1991-04-04 0.186534
1991-04-05 0.185616
1991-04-06 0.184698
1991-04-08 0.183780
...
2021-08-19 20.480000
2021-08-20 19.970000
2021-08-23 19.500000
2021-08-24 19.350000
2021-08-25 19.420000
Length: 7229, dtype: float64
定点复权与前后复权的关系
# 定点复权:以最后交易日为基准 <=> 前复权
base = (data.index<=pd.to_datetime("2021-08-24")).sum()
ddfq("close", base)
date
1991-04-03 0.187452
1991-04-04 0.186534
1991-04-05 0.185616
1991-04-06 0.184698
1991-04-08 0.183780
...
2021-08-19 20.340000
2021-08-20 19.420000
2021-08-23 19.300000
2021-08-24 19.360000
2021-08-25 19.160000
Length: 7229, dtype: float64
# 定点复权:以上市日为基准 <=> 后复权
base = (data.index<=pd.to_datetime("1991-04-03")).sum()
ddfq("close", base)
date
1991-04-03 0.187452
1991-04-04 0.186534
1991-04-05 0.185616
1991-04-06 0.184698
1991-04-08 0.183780
...
2021-08-19 20.340000
2021-08-20 19.420000
2021-08-23 19.300000
2021-08-24 19.360000
2021-08-25 19.160000
Length: 7229, dtype: float64