import numpy as np
import pandas as pd
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
%matplotlib inline
from scipy import stats
import matplotlib
import tushare as ts
#指定默认字体
matplotlib.rcParams['font.sans-serif'] = ['SimHei']
matplotlib.rcParams['font.family']='sans-serif'
#解决负号'-'显示为方块的问题
matplotlib.rcParams['axes.unicode_minus'] = False
pro = ts.pro_api('my_token')
df = pro.daily(ts_code='600519.SH', start_date='20190101', end_date='20210220')
stock = df.set_index(["trade_date"])
stock.head(10)
ts_code | open | high | low | close | pre_close | change | pct_chg | vol | amount | |
---|---|---|---|---|---|---|---|---|---|---|
trade_date | ||||||||||
20210219 | 600519.SH | 2451.16 | 2496.66 | 2381.6 | 2460.00 | 2471.00 | -11.00 | -0.4452 | 59385.46 | 1.452516e+07 |
20210218 | 600519.SH | 2587.98 | 2627.88 | 2465.0 | 2471.00 | 2601.00 | -130.00 | -4.9981 | 65912.32 | 1.670414e+07 |
20210210 | 600519.SH | 2485.00 | 2601.20 | 2485.0 | 2601.00 | 2456.43 | 144.57 | 5.8854 | 61370.57 | 1.567593e+07 |
20210209 | 600519.SH | 2368.80 | 2456.43 | 2350.0 | 2456.43 | 2368.80 | 87.63 | 3.6993 | 33296.55 | 7.972930e+06 |
20210208 | 600519.SH | 2337.00 | 2378.88 | 2313.0 | 2368.80 | 2313.00 | 55.80 | 2.4125 | 35785.73 | 8.420759e+06 |
20210205 | 600519.SH | 2325.00 | 2364.60 | 2291.0 | 2313.00 | 2320.85 | -7.85 | -0.3382 | 39729.06 | 9.253566e+06 |
20210204 | 600519.SH | 2191.00 | 2330.00 | 2191.0 | 2320.85 | 2189.91 | 130.94 | 5.9792 | 63851.01 | 1.459693e+07 |
20210203 | 600519.SH | 2150.00 | 2198.27 | 2140.0 | 2189.91 | 2145.00 | 44.91 | 2.0937 | 38714.15 | 8.384049e+06 |
20210202 | 600519.SH | 2112.22 | 2149.99 | 2102.1 | 2145.00 | 2109.32 | 35.68 | 1.6915 | 34582.25 | 7.372234e+06 |
20210201 | 600519.SH | 2130.00 | 2160.00 | 2095.0 | 2109.32 | 2116.18 | -6.86 | -0.3242 | 29340.59 | 6.219639e+06 |
close=stock.close
close.head(10)
trade_date
20210219 2460.00
20210218 2471.00
20210210 2601.00
20210209 2456.43
20210208 2368.80
20210205 2313.00
20210204 2320.85
20210203 2189.91
20210202 2145.00
20210201 2109.32
Name: close, dtype: float64
close.index=pd.to_datetime(close.index) #转换为时间索引
close.index.name="Date" #日期
close.head(10)
Date
2021-02-19 2460.00
2021-02-18 2471.00
2021-02-10 2601.00
2021-02-09 2456.43
2021-02-08 2368.80
2021-02-05 2313.00
2021-02-04 2320.85
2021-02-03 2189.91
2021-02-02 2145.00
2021-02-01 2109.32
Name: close, dtype: float64
lagclose=close.shift(1) #设置第一行为nan.,数据往下移动
lagclose.head(10)
Date
2021-02-19 NaN
2021-02-18 2460.00
2021-02-10 2471.00
2021-02-09 2601.00
2021-02-08 2456.43
2021-02-05 2368.80
2021-02-04 2313.00
2021-02-03 2320.85
2021-02-02 2189.91
2021-02-01 2145.00
Name: close, dtype: float64
close.head(10)
Date
2021-02-19 2460.00
2021-02-18 2471.00
2021-02-10 2601.00
2021-02-09 2456.43
2021-02-08 2368.80
2021-02-05 2313.00
2021-02-04 2320.85
2021-02-03 2189.91
2021-02-02 2145.00
2021-02-01 2109.32
Name: close, dtype: float64
lagclose.head(10)
Date
2021-02-19 NaN
2021-02-18 2460.00
2021-02-10 2471.00
2021-02-09 2601.00
2021-02-08 2456.43
2021-02-05 2368.80
2021-02-04 2313.00
2021-02-03 2320.85
2021-02-02 2189.91
2021-02-01 2145.00
Name: close, dtype: float64
CalcClose=pd.DataFrame({
"close":close,"lagclose":lagclose})
CalcClose.head(10)
close | lagclose | |
---|---|---|
Date | ||
2021-02-19 | 2460.00 | NaN |
2021-02-18 | 2471.00 | 2460.00 |
2021-02-10 | 2601.00 | 2471.00 |
2021-02-09 | 2456.43 | 2601.00 |
2021-02-08 | 2368.80 | 2456.43 |
2021-02-05 | 2313.00 | 2368.80 |
2021-02-04 | 2320.85 | 2313.00 |
2021-02-03 | 2189.91 | 2320.85 |
2021-02-02 | 2145.00 | 2189.91 |
2021-02-01 | 2109.32 | 2145.00 |
dayret1=(close-lagclose)/lagclose
dayret1.name="dayret1"
dayret1.head(10)
Date
2021-02-19 NaN
2021-02-18 0.004472
2021-02-10 0.052610
2021-02-09 -0.055582
2021-02-08 -0.035674
2021-02-05 -0.023556
2021-02-04 0.003394
2021-02-03 -0.056419
2021-02-02 -0.020508
2021-02-01 -0.016634
Name: dayret1, dtype: float64
calcret=pd.merge(CalcClose,pd.DataFrame(dayret1),left_index=True,right_index=True)#数据归并了
calcret.head(10)
close | lagclose | dayret1 | |
---|---|---|---|
Date | |||
2021-02-19 | 2460.00 | NaN | NaN |
2021-02-18 | 2471.00 | 2460.00 | 0.004472 |
2021-02-10 | 2601.00 | 2471.00 | 0.052610 |
2021-02-09 | 2456.43 | 2601.00 | -0.055582 |
2021-02-08 | 2368.80 | 2456.43 | -0.035674 |
2021-02-05 | 2313.00 | 2368.80 | -0.023556 |
2021-02-04 | 2320.85 | 2313.00 | 0.003394 |
2021-02-03 | 2189.91 | 2320.85 | -0.056419 |
2021-02-02 | 2145.00 | 2189.91 | -0.020508 |
2021-02-01 | 2109.32 | 2145.00 | -0.016634 |
dayret2=(close - close.shift(2))/close.shift(2)
dayret2.name="dayret2"
calcret["dayret2"]=dayret2
calcret.head(10)
close | lagclose | dayret1 | dayret2 | |
---|---|---|---|---|
Date | ||||
2021-02-19 | 2460.00 | NaN | NaN | NaN |
2021-02-18 | 2471.00 | 2460.00 | 0.004472 | NaN |
2021-02-10 | 2601.00 | 2471.00 | 0.052610 | 0.057317 |
2021-02-09 | 2456.43 | 2601.00 | -0.055582 | -0.005896 |
2021-02-08 | 2368.80 | 2456.43 | -0.035674 | -0.089273 |
2021-02-05 | 2313.00 | 2368.80 | -0.023556 | -0.058390 |
2021-02-04 | 2320.85 | 2313.00 | 0.003394 | -0.020242 |
2021-02-03 | 2189.91 | 2320.85 | -0.056419 | -0.053217 |
2021-02-02 | 2145.00 | 2189.91 | -0.020508 | -0.075770 |
2021-02-01 | 2109.32 | 2145.00 | -0.016634 | -0.036801 |
calcret.iloc[5,:]
close 2313.000000
lagclose 2368.800000
dayret1 -0.023556
dayret2 -0.058390
Name: 2021-02-05 00:00:00, dtype: float64
calcret.iloc[10,:]
close 2116.180000
lagclose 2109.320000
dayret1 0.003252
dayret2 -0.013436
Name: 2021-01-29 00:00:00, dtype: float64
calcret.iloc[10,-2:] #查看收益
dayret1 0.003252
dayret2 -0.013436
Name: 2021-01-29 00:00:00, dtype: float64
import ffn
ffnSimpleret=ffn.to_returns(close) #计算简单收益
ffnSimpleret.name="ffnSimpleret"
ffnSimpleret
Date
2021-02-19 NaN
2021-02-18 0.004472
2021-02-10 0.052610
2021-02-09 -0.055582
2021-02-08 -0.035674
...
2019-01-08 -0.018389
2019-01-07 0.001157
2019-01-04 -0.005764
2019-01-03 -0.019934
2019-01-02 0.015220
Name: ffnSimpleret, Length: 517, dtype: float64
#假定每年有245个交易日
annualize=(1+dayret1).cumprod()[-1]**(245/517)-1
annualize #通过某一天的收益,计算年化
-0.48801288886576977
def annualizecom(returns ,period): #根据年月日季度计算收益
if period=="day":
annualize=(1+returns).cumprod()[-1]**(245/len(returns))-1
return annualize
elif period=="month":
annualize=(1+returns).cumprod()[-1]**(12/len(returns))-1
return annualize
pass
elif period=="quarter":
annualize=(1+returns).cumprod()[-1]**(4/len(returns))-1
return annualize
elif period=="year":
annualize=(1+returns).cumprod()[-1]**(1/len(returns))-1
return annualize
else:
raise Excetion("error ")
用构造的函数分别计算几种收益率:
annualizecom(dayret1 ,"year")
-0.002728742960895203
annualizecom(dayret1 ,"month")
-0.032257919719301587
annualizecom(dayret1 ,"quarter")
-0.010870376832554318
annualizecom(dayret1 ,"day")
-0.48801288886576977
comporet=np.log(close/lagclose)#计算指数收益
comporet.name="comporet" #连续收益计算
comporet.head(10)
Date
2021-02-19 NaN
2021-02-18 0.004462
2021-02-10 0.051273
2021-02-09 -0.057187
2021-02-08 -0.036326
2021-02-05 -0.023838
2021-02-04 0.003388
2021-02-03 -0.058073
2021-02-02 -0.020721
2021-02-01 -0.016774
Name: comporet, dtype: float64
查看第六项收益率:
comporet[5]
-0.023838114555942435
ffnComporet=ffn.to_log_returns(close) #计算出指数收益率
ffnComporet.head(10)
Date
2021-02-19 NaN
2021-02-18 0.004462
2021-02-10 0.051273
2021-02-09 -0.057187
2021-02-08 -0.036326
2021-02-05 -0.023838
2021-02-04 0.003388
2021-02-03 -0.058073
2021-02-02 -0.020721
2021-02-01 -0.016774
Name: close, dtype: float64
comporet2=np.log(close/close.shift(2))#计算指数收益
comporet2.name="comporet" #连续收益计算
comporet2 .head(10)
Date
2021-02-19 NaN
2021-02-18 NaN
2021-02-10 0.055735
2021-02-09 -0.005914
2021-02-08 -0.093512
2021-02-05 -0.060164
2021-02-04 -0.020450
2021-02-03 -0.054685
2021-02-02 -0.078794
2021-02-01 -0.037495
Name: comporet, dtype: float64
comporet.plot()
comporet2=comporet.dropna()#删除无效数据
comporet2.plot()
sumcomporet=comporet+comporet.shift(1)
sumcomporet.plot()
comporet.plot()
(dayret2+1).cumprod()
Date
2021-02-19 NaN
2021-02-18 NaN
2021-02-10 1.057317
2021-02-09 1.051083
2021-02-08 0.957249
...
2019-01-08 0.061300
2019-01-07 0.060243
2019-01-04 0.059965
2019-01-03 0.058431
2019-01-02 0.058138
Name: dayret2, Length: 517, dtype: float64
((dayret2+1).cumprod()-1).plot()#累积收益
SAPower代表“航天动力”股票,DalianRP代表“大橡塑”股票,下面比较两只股票风险的的大小
pro = ts.pro_api('4c1fa508ab60e554d221c55a47dfa557a6c7bfd0c239b45657c2262d')
df = pro.daily(ts_code='600343.SH', start_date='20200101', end_date='20201231')
df
ts_code | trade_date | open | high | low | close | pre_close | change | pct_chg | vol | amount | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 600343.SH | 20201231 | 9.39 | 9.76 | 9.36 | 9.76 | 9.42 | 0.34 | 3.6093 | 198064.73 | 190630.321 |
1 | 600343.SH | 20201230 | 9.29 | 9.46 | 9.02 | 9.42 | 9.31 | 0.11 | 1.1815 | 104800.25 | 97214.206 |
2 | 600343.SH | 20201229 | 9.40 | 9.59 | 9.29 | 9.31 | 9.46 | -0.15 | -1.5856 | 80965.77 | 76417.065 |
3 | 600343.SH | 20201228 | 9.55 | 9.71 | 9.34 | 9.46 | 9.55 | -0.09 | -0.9424 | 92974.76 | 88318.154 |
4 | 600343.SH | 20201225 | 9.33 | 9.59 | 9.21 | 9.55 | 9.40 | 0.15 | 1.5957 | 103874.88 | 97898.896 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
238 | 600343.SH | 20200108 | 9.52 | 9.83 | 9.47 | 9.74 | 9.49 | 0.25 | 2.6344 | 160906.15 | 155266.627 |
239 | 600343.SH | 20200107 | 9.55 | 9.56 | 9.40 | 9.49 | 9.58 | -0.09 | -0.9395 | 97338.88 | 92170.541 |
240 | 600343.SH | 20200106 | 9.50 | 9.68 | 9.43 | 9.58 | 9.44 | 0.14 | 1.4831 | 144709.76 | 138248.280 |
241 | 600343.SH | 20200103 | 9.25 | 9.55 | 9.24 | 9.44 | 9.24 | 0.20 | 2.1645 | 130065.35 | 122290.734 |
242 | 600343.SH | 20200102 | 9.20 | 9.30 | 9.16 | 9.24 | 9.15 | 0.09 | 0.9836 | 73605.63 | 68052.974 |
243 rows × 11 columns
SAPower = df.set_index(["trade_date"])
SAPower.index = pd.to_datetime(SAPower.index)
SAPower.head(10)
ts_code | open | high | low | close | pre_close | change | pct_chg | vol | amount | |
---|---|---|---|---|---|---|---|---|---|---|
trade_date | ||||||||||
2019-12-31 | 600343.SH | 9.17 | 9.21 | 9.07 | 9.15 | 9.21 | -0.06 | -0.6515 | 39672.46 | 36228.607 |
2019-12-30 | 600343.SH | 9.19 | 9.22 | 9.00 | 9.21 | 9.19 | 0.02 | 0.2176 | 49130.47 | 44842.067 |
2019-12-27 | 600343.SH | 9.10 | 9.28 | 9.08 | 9.19 | 9.10 | 0.09 | 0.9890 | 80067.92 | 73673.092 |
2019-12-26 | 600343.SH | 9.01 | 9.10 | 8.99 | 9.10 | 9.05 | 0.05 | 0.5525 | 41205.66 | 37261.199 |
2019-12-25 | 600343.SH | 9.06 | 9.07 | 9.00 | 9.05 | 9.07 | -0.02 | -0.2205 | 30857.58 | 27893.791 |
2019-12-24 | 600343.SH | 8.99 | 9.09 | 8.97 | 9.07 | 9.00 | 0.07 | 0.7778 | 35425.88 | 31933.627 |
2019-12-23 | 600343.SH | 9.07 | 9.24 | 8.99 | 9.00 | 9.05 | -0.05 | -0.5525 | 58083.95 | 52851.891 |
2019-12-20 | 600343.SH | 9.26 | 9.28 | 9.05 | 9.05 | 9.25 | -0.20 | -2.1622 | 50570.17 | 46290.687 |
2019-12-19 | 600343.SH | 9.24 | 9.29 | 9.16 | 9.25 | 9.21 | 0.04 | 0.4343 | 53179.06 | 49060.929 |
2019-12-18 | 600343.SH | 9.34 | 9.35 | 9.21 | 9.21 | 9.27 | -0.06 | -0.6472 | 81377.10 | 75335.475 |
data = pro.daily(ts_code='600346.SH', start_date='20200101', end_date='20201231')
DalianPR = data.set_index(["trade_date"])
DalianPR.index=pd.to_datetime(DalianPR.index) #时间类型
DalianPR.head(10)
ts_code | open | high | low | close | pre_close | change | pct_chg | vol | amount | |
---|---|---|---|---|---|---|---|---|---|---|
trade_date | ||||||||||
2020-12-31 | 600346.SH | 28.27 | 28.47 | 27.37 | 27.97 | 28.27 | -0.30 | -1.0612 | 271347.44 | 754838.563 |
2020-12-30 | 600346.SH | 28.50 | 28.70 | 27.70 | 28.27 | 28.27 | 0.00 | 0.0000 | 253733.66 | 712823.643 |
2020-12-29 | 600346.SH | 28.05 | 28.50 | 27.91 | 28.27 | 28.52 | -0.25 | -0.8766 | 249460.56 | 702195.348 |
2020-12-28 | 600346.SH | 28.14 | 28.98 | 27.89 | 28.52 | 28.20 | 0.32 | 1.1348 | 389506.55 | 1105593.177 |
2020-12-25 | 600346.SH | 27.85 | 28.40 | 26.90 | 28.20 | 27.84 | 0.36 | 1.2931 | 320665.63 | 888150.676 |
2020-12-24 | 600346.SH | 25.83 | 27.88 | 25.66 | 27.84 | 25.83 | 2.01 | 7.7816 | 422756.21 | 1145436.858 |
2020-12-23 | 600346.SH | 24.89 | 25.88 | 24.40 | 25.83 | 24.91 | 0.92 | 3.6933 | 440258.29 | 1110170.102 |
2020-12-22 | 600346.SH | 24.99 | 25.98 | 24.90 | 24.91 | 25.27 | -0.36 | -1.4246 | 384294.95 | 975570.605 |
2020-12-21 | 600346.SH | 25.60 | 25.61 | 24.83 | 25.27 | 25.60 | -0.33 | -1.2891 | 383929.24 | 968798.982 |
2020-12-18 | 600346.SH | 25.13 | 25.90 | 25.05 | 25.60 | 25.10 | 0.50 | 1.9920 | 341813.44 | 871416.769 |
import ffn
returnS=ffn.to_returns(SAPower.close).dropna() #航天收盘列
returnD=ffn.to_returns(DalianPR.close).dropna() #d大橡塑收盘列
returnS.std()
0.021819530993107
returnD.std()
0.025298264541065586
经比较发现,大橡塑股票风险更大
def cal_half_dev(returns):
mu=returns.mean() #平均值
temp=returns[returns <mu] #小于平均值的数据
half_deviation=(sum((mu-temp)**2)/len(returns))**0.5
return half_deviation
cal_half_dev(returnS)
0.0157115665441478
cal_half_dev(returnD) #下行风险
0.018292505958246514
比较发现,大橡塑股票的风险更大
风险价值度量法分为历史模拟法、协方差矩阵法和蒙特卡罗方法
returnS.quantile(0.05) #历史模拟
-0.03534861369190922
returnD.quantile(0.05)
-0.04523544479325154
可见大橡塑股票的最大可能损失更大
#协方差
from scipy.stats import norm
norm.ppf(0.05,returnS.mean(),returnS.std())
-0.03639441747346504
norm.ppf(0.05,returnD.mean(),returnD.std())
-0.043578796055109476
可见大橡塑股票的最大可能损失更大.此结果与课本不同,是因为选取的时间段不同。
returnS[returnS<=returnS.quantile(0.05)].mean()
-0.052289418772132064
returnD[returnD<=returnD.quantile(0.05)].mean()
-0.060541230656241445
因为是vaR的变形。,所以比较时与之前结论一致。
假设某资产(初始值为1元)从第一期到第六期的收益率分别为0,0.10,-0.10,-0.01,0.01,0.02,要计算各期的回撤及第六期的最大回撤
输入收益率并假设时间
import datetime
r=pd.Series([0,0.1,-0.1,-0.01,0.01,0.02],index=[datetime.date(2017,12,x) for x in range(3,9)])
r
2017-12-03 0.00
2017-12-04 0.10
2017-12-05 -0.10
2017-12-06 -0.01
2017-12-07 0.01
2017-12-08 0.02
dtype: float64
value=(1+r).cumprod() #收益
value
2017-12-03 1.000000
2017-12-04 1.100000
2017-12-05 0.990000
2017-12-06 0.980100
2017-12-07 0.989901
2017-12-08 1.009699
dtype: float64
D=value.cummax()-value # 差异
D
2017-12-03 0.000000
2017-12-04 0.000000
2017-12-05 0.110000
2017-12-06 0.119900
2017-12-07 0.110099
2017-12-08 0.090301
dtype: float64
d=D/(D+value)
d
2017-12-03 0.000000
2017-12-04 0.000000
2017-12-05 0.100000
2017-12-06 0.109000
2017-12-07 0.100090
2017-12-08 0.082092
dtype: float64
MDD=D.max()
MDD
0.1199
mdd=d.max()
mdd
0.109
### ffn包calc_max_drawdown函数计算回撤值、回撤率及其最大值
ffn.calc_max_drawdown(value)
-0.10899999999999999
ffn.calc_max_drawdown((1+returnS).cumprod()) #最高峰的值与当下数据的对比,差距越大,风险越高
-0.3877388535031847
ffn.calc_max_drawdown((1+returnD).cumprod())
-0.5753856942496501