siat 3.10.130__py3-none-any.whl → 3.10.132__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- build/lib/build/lib/siat/__init__.py +75 -0
- build/lib/build/lib/siat/allin.py +137 -0
- build/lib/build/lib/siat/assets_liquidity.py +915 -0
- build/lib/build/lib/siat/beta_adjustment.py +1058 -0
- build/lib/build/lib/siat/beta_adjustment_china.py +548 -0
- build/lib/build/lib/siat/blockchain.py +143 -0
- build/lib/build/lib/siat/bond.py +2900 -0
- build/lib/build/lib/siat/bond_base.py +992 -0
- build/lib/build/lib/siat/bond_china.py +100 -0
- build/lib/build/lib/siat/bond_zh_sina.py +143 -0
- build/lib/build/lib/siat/capm_beta.py +783 -0
- build/lib/build/lib/siat/capm_beta2.py +887 -0
- build/lib/build/lib/siat/common.py +5360 -0
- build/lib/build/lib/siat/compare_cross.py +642 -0
- build/lib/build/lib/siat/copyrights.py +18 -0
- build/lib/build/lib/siat/cryptocurrency.py +667 -0
- build/lib/build/lib/siat/economy.py +1471 -0
- build/lib/build/lib/siat/economy2.py +1853 -0
- build/lib/build/lib/siat/esg.py +536 -0
- build/lib/build/lib/siat/event_study.py +815 -0
- build/lib/build/lib/siat/fama_french.py +1521 -0
- build/lib/build/lib/siat/fin_stmt2_yahoo.py +982 -0
- build/lib/build/lib/siat/financial_base.py +1160 -0
- build/lib/build/lib/siat/financial_statements.py +598 -0
- build/lib/build/lib/siat/financials.py +2339 -0
- build/lib/build/lib/siat/financials2.py +1278 -0
- build/lib/build/lib/siat/financials_china.py +4433 -0
- build/lib/build/lib/siat/financials_china2.py +2212 -0
- build/lib/build/lib/siat/fund.py +629 -0
- build/lib/build/lib/siat/fund_china.py +3307 -0
- build/lib/build/lib/siat/future_china.py +551 -0
- build/lib/build/lib/siat/google_authenticator.py +47 -0
- build/lib/build/lib/siat/grafix.py +3636 -0
- build/lib/build/lib/siat/holding_risk.py +867 -0
- build/lib/build/lib/siat/luchy_draw.py +638 -0
- build/lib/build/lib/siat/market_china.py +1168 -0
- build/lib/build/lib/siat/markowitz.py +2363 -0
- build/lib/build/lib/siat/markowitz2.py +3150 -0
- build/lib/build/lib/siat/markowitz2_20250704.py +2969 -0
- build/lib/build/lib/siat/markowitz2_20250705.py +3158 -0
- build/lib/build/lib/siat/markowitz_simple.py +373 -0
- build/lib/build/lib/siat/ml_cases.py +2291 -0
- build/lib/build/lib/siat/ml_cases_example.py +60 -0
- build/lib/build/lib/siat/option_china.py +3069 -0
- build/lib/build/lib/siat/option_pricing.py +1925 -0
- build/lib/build/lib/siat/other_indexes.py +409 -0
- build/lib/build/lib/siat/risk_adjusted_return.py +1576 -0
- build/lib/build/lib/siat/risk_adjusted_return2.py +1900 -0
- build/lib/build/lib/siat/risk_evaluation.py +2218 -0
- build/lib/build/lib/siat/risk_free_rate.py +351 -0
- build/lib/build/lib/siat/sector_china.py +4140 -0
- build/lib/build/lib/siat/security_price2.py +727 -0
- build/lib/build/lib/siat/security_prices.py +3408 -0
- build/lib/build/lib/siat/security_trend.py +402 -0
- build/lib/build/lib/siat/security_trend2.py +646 -0
- build/lib/build/lib/siat/stock.py +4284 -0
- build/lib/build/lib/siat/stock_advice_linear.py +934 -0
- build/lib/build/lib/siat/stock_base.py +26 -0
- build/lib/build/lib/siat/stock_china.py +2095 -0
- build/lib/build/lib/siat/stock_prices_kneighbors.py +910 -0
- build/lib/build/lib/siat/stock_prices_linear.py +386 -0
- build/lib/build/lib/siat/stock_profile.py +707 -0
- build/lib/build/lib/siat/stock_technical.py +3305 -0
- build/lib/build/lib/siat/stooq.py +74 -0
- build/lib/build/lib/siat/transaction.py +347 -0
- build/lib/build/lib/siat/translate.py +5183 -0
- build/lib/build/lib/siat/valuation.py +1378 -0
- build/lib/build/lib/siat/valuation_china.py +2076 -0
- build/lib/build/lib/siat/var_model_validation.py +444 -0
- build/lib/build/lib/siat/yf_name.py +811 -0
- build/lib/siat/__init__.py +75 -0
- build/lib/siat/allin.py +137 -0
- build/lib/siat/assets_liquidity.py +915 -0
- build/lib/siat/beta_adjustment.py +1058 -0
- build/lib/siat/beta_adjustment_china.py +548 -0
- build/lib/siat/blockchain.py +143 -0
- build/lib/siat/bond.py +2900 -0
- build/lib/siat/bond_base.py +992 -0
- build/lib/siat/bond_china.py +100 -0
- build/lib/siat/bond_zh_sina.py +143 -0
- build/lib/siat/capm_beta.py +783 -0
- build/lib/siat/capm_beta2.py +887 -0
- build/lib/siat/common.py +5360 -0
- build/lib/siat/compare_cross.py +642 -0
- build/lib/siat/copyrights.py +18 -0
- build/lib/siat/cryptocurrency.py +667 -0
- build/lib/siat/economy.py +1471 -0
- build/lib/siat/economy2.py +1853 -0
- build/lib/siat/esg.py +536 -0
- build/lib/siat/event_study.py +815 -0
- build/lib/siat/fama_french.py +1521 -0
- build/lib/siat/fin_stmt2_yahoo.py +982 -0
- build/lib/siat/financial_base.py +1160 -0
- build/lib/siat/financial_statements.py +598 -0
- build/lib/siat/financials.py +2339 -0
- build/lib/siat/financials2.py +1278 -0
- build/lib/siat/financials_china.py +4433 -0
- build/lib/siat/financials_china2.py +2212 -0
- build/lib/siat/fund.py +629 -0
- build/lib/siat/fund_china.py +3307 -0
- build/lib/siat/future_china.py +551 -0
- build/lib/siat/google_authenticator.py +47 -0
- build/lib/siat/grafix.py +3636 -0
- build/lib/siat/holding_risk.py +867 -0
- build/lib/siat/luchy_draw.py +638 -0
- build/lib/siat/market_china.py +1168 -0
- build/lib/siat/markowitz.py +2363 -0
- build/lib/siat/markowitz2.py +3150 -0
- build/lib/siat/markowitz2_20250704.py +2969 -0
- build/lib/siat/markowitz2_20250705.py +3158 -0
- build/lib/siat/markowitz_simple.py +373 -0
- build/lib/siat/ml_cases.py +2291 -0
- build/lib/siat/ml_cases_example.py +60 -0
- build/lib/siat/option_china.py +3069 -0
- build/lib/siat/option_pricing.py +1925 -0
- build/lib/siat/other_indexes.py +409 -0
- build/lib/siat/risk_adjusted_return.py +1576 -0
- build/lib/siat/risk_adjusted_return2.py +1900 -0
- build/lib/siat/risk_evaluation.py +2218 -0
- build/lib/siat/risk_free_rate.py +351 -0
- build/lib/siat/sector_china.py +4140 -0
- build/lib/siat/security_price2.py +727 -0
- build/lib/siat/security_prices.py +3408 -0
- build/lib/siat/security_trend.py +402 -0
- build/lib/siat/security_trend2.py +646 -0
- build/lib/siat/stock.py +4284 -0
- build/lib/siat/stock_advice_linear.py +934 -0
- build/lib/siat/stock_base.py +26 -0
- build/lib/siat/stock_china.py +2095 -0
- build/lib/siat/stock_prices_kneighbors.py +910 -0
- build/lib/siat/stock_prices_linear.py +386 -0
- build/lib/siat/stock_profile.py +707 -0
- build/lib/siat/stock_technical.py +3305 -0
- build/lib/siat/stooq.py +74 -0
- build/lib/siat/transaction.py +347 -0
- build/lib/siat/translate.py +5183 -0
- build/lib/siat/valuation.py +1378 -0
- build/lib/siat/valuation_china.py +2076 -0
- build/lib/siat/var_model_validation.py +444 -0
- build/lib/siat/yf_name.py +811 -0
- siat/__init__.py +0 -0
- siat/allin.py +0 -0
- siat/assets_liquidity.py +0 -0
- siat/beta_adjustment.py +0 -0
- siat/beta_adjustment_china.py +0 -0
- siat/blockchain.py +0 -0
- siat/bond.py +0 -0
- siat/bond_base.py +0 -0
- siat/bond_china.py +0 -0
- siat/bond_zh_sina.py +0 -0
- siat/capm_beta.py +0 -0
- siat/capm_beta2.py +0 -0
- siat/common.py +94 -30
- siat/compare_cross.py +0 -0
- siat/copyrights.py +0 -0
- siat/cryptocurrency.py +0 -0
- siat/economy.py +0 -0
- siat/economy2.py +0 -0
- siat/esg.py +0 -0
- siat/event_study.py +0 -0
- siat/fama_french.py +0 -0
- siat/fin_stmt2_yahoo.py +0 -0
- siat/financial_base.py +0 -0
- siat/financial_statements.py +0 -0
- siat/financials.py +0 -0
- siat/financials2.py +0 -0
- siat/financials_china.py +0 -0
- siat/financials_china2.py +0 -0
- siat/fund.py +0 -0
- siat/fund_china.py +0 -0
- siat/future_china.py +0 -0
- siat/google_authenticator.py +0 -0
- siat/grafix.py +1 -1
- siat/holding_risk.py +0 -0
- siat/luchy_draw.py +0 -0
- siat/market_china.py +7 -1
- siat/markowitz.py +0 -0
- siat/markowitz2.py +240 -39
- siat/markowitz2_20250704.py +2969 -0
- siat/markowitz2_20250705.py +3158 -0
- siat/markowitz_simple.py +0 -0
- siat/ml_cases.py +0 -0
- siat/ml_cases_example.py +0 -0
- siat/option_china.py +0 -0
- siat/option_pricing.py +0 -0
- siat/other_indexes.py +0 -0
- siat/risk_adjusted_return.py +0 -0
- siat/risk_adjusted_return2.py +0 -0
- siat/risk_evaluation.py +0 -0
- siat/risk_free_rate.py +0 -0
- siat/sector_china.py +0 -0
- siat/security_price2.py +0 -0
- siat/security_prices.py +3 -1
- siat/security_trend.py +0 -0
- siat/security_trend2.py +1 -1
- siat/stock.py +4 -2
- siat/stock_advice_linear.py +0 -0
- siat/stock_base.py +0 -0
- siat/stock_china.py +0 -0
- siat/stock_prices_kneighbors.py +0 -0
- siat/stock_prices_linear.py +0 -0
- siat/stock_profile.py +0 -0
- siat/stock_technical.py +0 -0
- siat/stooq.py +0 -0
- siat/transaction.py +0 -0
- siat/translate.py +11 -11
- siat/valuation.py +0 -0
- siat/valuation_china.py +0 -0
- siat/var_model_validation.py +0 -0
- siat/yf_name.py +0 -0
- {siat-3.10.130.dist-info → siat-3.10.132.dist-info}/METADATA +11 -11
- siat-3.10.132.dist-info/RECORD +218 -0
- siat-3.10.132.dist-info/top_level.txt +4 -0
- siat-3.10.130.dist-info/RECORD +0 -76
- siat-3.10.130.dist-info/top_level.txt +0 -1
- {siat-3.10.130.dist-info → siat-3.10.132.dist-info}/WHEEL +0 -0
- {siat-3.10.130.dist-info → siat-3.10.132.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,2218 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
"""
|
3
|
+
本模块功能:单项证券的VaR(在险价值)和ES(预期损失)计算函数包
|
4
|
+
所属工具包:证券投资分析工具SIAT
|
5
|
+
SIAT:Security Investment Analysis Tool
|
6
|
+
创建日期:2018年10月10日
|
7
|
+
最新修订日期:2019年10月10日
|
8
|
+
作者:王德宏 (WANG Dehong, Peter)
|
9
|
+
作者单位:北京外国语大学国际商学院
|
10
|
+
作者邮件:wdehong2000@163.com
|
11
|
+
版权所有:王德宏
|
12
|
+
用途限制:仅限研究与教学使用,不可商用!商用需要额外授权。
|
13
|
+
特别声明:作者不对使用本工具进行证券投资导致的任何损益负责!
|
14
|
+
"""
|
15
|
+
#==============================================================================
|
16
|
+
#统一屏蔽一般性警告
|
17
|
+
import warnings; warnings.filterwarnings("ignore")
|
18
|
+
from siat.common import *
|
19
|
+
from siat.translate import *
|
20
|
+
from siat.security_prices import *
|
21
|
+
from siat.security_price2 import *
|
22
|
+
#==============================================================================
|
23
|
+
import matplotlib.pyplot as plt
|
24
|
+
|
25
|
+
#处理绘图汉字乱码问题
|
26
|
+
import sys; czxt=sys.platform
|
27
|
+
if czxt in ['win32','win64']:
|
28
|
+
plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置默认字体
|
29
|
+
mpfrc={'font.family': 'SimHei'}
|
30
|
+
|
31
|
+
if czxt in ['darwin']: #MacOSX
|
32
|
+
plt.rcParams['font.family']= ['Heiti TC']
|
33
|
+
mpfrc={'font.family': 'Heiti TC'}
|
34
|
+
|
35
|
+
if czxt in ['linux']: #website Jupyter
|
36
|
+
plt.rcParams['font.family']= ['Heiti TC']
|
37
|
+
mpfrc={'font.family':'Heiti TC'}
|
38
|
+
|
39
|
+
# 解决保存图像时'-'显示为方块的问题
|
40
|
+
plt.rcParams['axes.unicode_minus'] = False
|
41
|
+
#==============================================================================
|
42
|
+
#统一设定绘制的图片大小:数值为英寸,1英寸=100像素
|
43
|
+
#plt.rcParams['figure.figsize']=(12.8,7.2)
|
44
|
+
plt.rcParams['figure.figsize']=(12.8,6.4)
|
45
|
+
plt.rcParams['figure.dpi']=300
|
46
|
+
plt.rcParams['font.size'] = 13
|
47
|
+
plt.rcParams['xtick.labelsize']=11 #横轴字体大小
|
48
|
+
plt.rcParams['ytick.labelsize']=11 #纵轴字体大小
|
49
|
+
|
50
|
+
title_txt_size=16
|
51
|
+
ylabel_txt_size=14
|
52
|
+
xlabel_txt_size=14
|
53
|
+
legend_txt_size=14
|
54
|
+
|
55
|
+
#设置绘图风格:网格虚线
|
56
|
+
plt.rcParams['axes.grid']=True
|
57
|
+
#plt.rcParams['grid.color']='steelblue'
|
58
|
+
#plt.rcParams['grid.linestyle']='dashed'
|
59
|
+
#plt.rcParams['grid.linewidth']=0.5
|
60
|
+
#plt.rcParams['axes.facecolor']='whitesmoke'
|
61
|
+
#==============================================================================
|
62
|
+
if __name__ == '__main__':
|
63
|
+
ticker='BABA'
|
64
|
+
start_date='2021-11-1'
|
65
|
+
end_date='2021-11-10'
|
66
|
+
|
67
|
+
def get_stock_quotes(ticker,start_date,end_date):
|
68
|
+
"""
|
69
|
+
输入参数:股票代码,开始日期,结束日期
|
70
|
+
输出参数:
|
71
|
+
股票价格序列(日期,开盘价,最高价,最低价,收盘价,调整收盘价,交易量)
|
72
|
+
"""
|
73
|
+
|
74
|
+
df=get_prices(ticker,start_date,end_date)
|
75
|
+
|
76
|
+
if df is None:
|
77
|
+
print(" #Error(get_stock_quotes): retrieving prices failed for",ticker,start_date,end_date)
|
78
|
+
return None
|
79
|
+
if len(df) == 0:
|
80
|
+
print(" #Error(get_stock_quotes): zero record found for",ticker,start_date,end_date)
|
81
|
+
return None
|
82
|
+
|
83
|
+
return df
|
84
|
+
|
85
|
+
if __name__ == '__main__':
|
86
|
+
get_stock_quotes('BABA','2021-11-1','2021-11-10')
|
87
|
+
#==============================================================================
|
88
|
+
def get_end_price(stock_quotes):
|
89
|
+
"""
|
90
|
+
输入参数:股票价格序列
|
91
|
+
输出参数:最新股价(金额)
|
92
|
+
"""
|
93
|
+
end_price=stock_quotes['Close'][-1]
|
94
|
+
return end_price
|
95
|
+
|
96
|
+
if __name__ == '__main__':
|
97
|
+
get_end_price(stock_quotes)
|
98
|
+
#==============================================================================
|
99
|
+
def get_ret_series(stock_quotes):
|
100
|
+
"""
|
101
|
+
输入参数:股票价格序列
|
102
|
+
输出参数:股票日收益率序列(注意不是DataFrame)
|
103
|
+
"""
|
104
|
+
stock_quotes['ret']=stock_quotes['Close'].pct_change()
|
105
|
+
stock_quotes=stock_quotes.dropna()
|
106
|
+
ret_series=stock_quotes['ret']
|
107
|
+
return ret_series
|
108
|
+
# ret_series是序列类型
|
109
|
+
|
110
|
+
if __name__ == '__main__':
|
111
|
+
ret_series=get_ret_series(stock_quotes)
|
112
|
+
#==============================================================================
|
113
|
+
def VaR_normal_standard(position,ret_series,future_days=1,alpha=0.99):
|
114
|
+
|
115
|
+
"""
|
116
|
+
标准正太法VaR基本算法
|
117
|
+
输入参数:当前持有头寸金额,收益率序列(非百分比),未来持有时间(天),置信度
|
118
|
+
输出参数:VaR(金额,单位与当前头寸的金额单位相同),负数
|
119
|
+
"""
|
120
|
+
#去掉空值
|
121
|
+
r=ret_series[~ret_series.isnull()]
|
122
|
+
|
123
|
+
import numpy as np
|
124
|
+
from scipy import stats
|
125
|
+
|
126
|
+
z=stats.norm.ppf(1-alpha)
|
127
|
+
miu_daily=np.mean(r)
|
128
|
+
miu_days=np.power(miu_daily+1,future_days)-1
|
129
|
+
sigma_daily=np.std(r)
|
130
|
+
sigma_days=np.sqrt(future_days)*sigma_daily
|
131
|
+
|
132
|
+
ratio=miu_days+z*sigma_days
|
133
|
+
VaR_days=position*ratio
|
134
|
+
|
135
|
+
return VaR_days
|
136
|
+
|
137
|
+
if __name__ == '__main__':
|
138
|
+
VaR_normal_standard(10000,ret_series,future_days=1,alpha=0.99)
|
139
|
+
#==============================================================================
|
140
|
+
def stock_VaR(ticker,shares,today,future_days=1,alpha=0.99, \
|
141
|
+
pastyears=1,printout=True,random=10000,mctype='random', \
|
142
|
+
model="normal_standard"):
|
143
|
+
"""
|
144
|
+
功能:持有股票的VaR,支持选择四种模型
|
145
|
+
输入参数:股票代码,持有股数,当前日期,未来持有时间(天),置信度,
|
146
|
+
使用历史数据的年数,是否打印结果,模型方法
|
147
|
+
模型方法:标准正态法normal_standard,修正正态法normal_modified,
|
148
|
+
蒙特卡洛模拟法montecarlo,历史排序模拟法historical
|
149
|
+
输出:VaR(负数金额,单位与股价的金额单位相同),VaR比率
|
150
|
+
"""
|
151
|
+
# 检查支持的模型
|
152
|
+
modellist=['normal_standard','normal_modified','montecarlo', \
|
153
|
+
'historical','allmodels']
|
154
|
+
if not (model in modellist):
|
155
|
+
print(" #Error(stock_VaR): unsupported model",model)
|
156
|
+
print(" Supported models:",modellist)
|
157
|
+
return None,None
|
158
|
+
|
159
|
+
if model in ['normal_standard']:
|
160
|
+
VaR,VaR_ratio=stock_VaR_normal_standard(ticker=ticker,shares=shares, \
|
161
|
+
today=today,future_days=future_days,alpha=alpha, \
|
162
|
+
pastyears=pastyears,printout=printout)
|
163
|
+
return VaR,VaR_ratio
|
164
|
+
|
165
|
+
if model in ['normal_modified']:
|
166
|
+
VaR,VaR_ratio=stock_VaR_normal_modified(ticker=ticker,shares=shares, \
|
167
|
+
today=today,future_days=future_days,alpha=alpha, \
|
168
|
+
pastyears=pastyears,printout=printout)
|
169
|
+
return VaR,VaR_ratio
|
170
|
+
|
171
|
+
if model in ['montecarlo']:
|
172
|
+
VaR,VaR_ratio=stock_VaR_montecarlo(ticker=ticker,shares=shares, \
|
173
|
+
today=today,future_days=future_days,alpha=alpha, \
|
174
|
+
pastyears=pastyears,random=random,mctype=mctype, \
|
175
|
+
printout=printout)
|
176
|
+
return VaR,VaR_ratio
|
177
|
+
|
178
|
+
if model in ['historical']:
|
179
|
+
VaR,VaR_ratio=stock_VaR_historical_grouping(ticker=ticker,shares=shares, \
|
180
|
+
today=today,future_days=future_days,alpha=alpha, \
|
181
|
+
pastyears=pastyears, \
|
182
|
+
printout=printout)
|
183
|
+
return VaR,VaR_ratio
|
184
|
+
|
185
|
+
if model in ['allmodels']:
|
186
|
+
get_VaR_allmodels(ticker=ticker,shares=shares,today=today, \
|
187
|
+
future_days=future_days,alpha=alpha,pastyears=pastyears)
|
188
|
+
|
189
|
+
return
|
190
|
+
|
191
|
+
#==============================================================================
|
192
|
+
def security_VaR(ticker,shares,today,future_days=1,alpha=0.99, \
|
193
|
+
pastyears=1,printout=True,random=10000,mctype='random', \
|
194
|
+
model="normal_standard"):
|
195
|
+
"""
|
196
|
+
功能:持有证券的VaR,支持选择四种模型
|
197
|
+
输入参数:证券代码,持有股数,当前日期,未来持有时间(天),置信度,
|
198
|
+
使用历史数据的年数,是否打印结果,模型方法
|
199
|
+
模型方法:标准正态法normal_standard,修正正态法normal_modified,
|
200
|
+
蒙特卡洛模拟法montecarlo,历史排序模拟法historical
|
201
|
+
输出:VaR(负数金额,单位与股价的金额单位相同),VaR比率
|
202
|
+
说明:套壳函数stock_VaR
|
203
|
+
"""
|
204
|
+
stock_VaR(ticker=ticker,shares=shares,today=today,future_days=future_days,alpha=alpha, \
|
205
|
+
pastyears=pastyears,printout=printout,random=random,mctype=mctype, \
|
206
|
+
model=model)
|
207
|
+
|
208
|
+
return
|
209
|
+
|
210
|
+
#==============================================================================
|
211
|
+
if __name__ == '__main__':
|
212
|
+
ticker='BABA'
|
213
|
+
shares=10000
|
214
|
+
today='2021-11-18'
|
215
|
+
future_days=1
|
216
|
+
alpha=0.99
|
217
|
+
pastyears=1
|
218
|
+
printout=True
|
219
|
+
|
220
|
+
def stock_VaR_normal_standard(ticker,shares,today, \
|
221
|
+
future_days=1,alpha=0.99,pastyears=1,printout=True):
|
222
|
+
"""
|
223
|
+
功能:持有股票的VaR,标准正态法
|
224
|
+
输入参数:股票代码,持有股数,当前日期,未来持有时间(天),置信度,使用历史数据的年数
|
225
|
+
输出:VaR(负数金额,单位与股价的金额单位相同),VaR比率
|
226
|
+
"""
|
227
|
+
start=get_start_date(today,pastyears)
|
228
|
+
p=get_stock_quotes(ticker,start,today)
|
229
|
+
if p is None:
|
230
|
+
print(" #Error(stock_VaR_normal_standard): no obs retrieved.")
|
231
|
+
return None,None
|
232
|
+
if len(p)==0:
|
233
|
+
print(" #Error(stock_VaR_normal_standard): zero obs retrieved.")
|
234
|
+
return None,None
|
235
|
+
|
236
|
+
r=get_ret_series(p)
|
237
|
+
p_end=get_end_price(p)
|
238
|
+
position=shares*p_end
|
239
|
+
VaR=VaR_normal_standard(position,r,future_days,alpha)
|
240
|
+
if VaR is None: return None,None
|
241
|
+
#最大为全损
|
242
|
+
if abs(VaR) > position: VaR=-position
|
243
|
+
|
244
|
+
VaR_ratio=abs(VaR/position)
|
245
|
+
|
246
|
+
#from rich import print as rprint
|
247
|
+
import pandas as pd
|
248
|
+
disp_df=pd.DataFrame(columns=['Item','Value'])
|
249
|
+
if printout == True:
|
250
|
+
"""
|
251
|
+
print("\n=== 计算在险价值:标准正态模型 ===")
|
252
|
+
print("持有股票 :",ticker_name(ticker))
|
253
|
+
print("持有股数 :",format(shares,','))
|
254
|
+
print("持有日期 :",today)
|
255
|
+
print("预计持有天数:",future_days)
|
256
|
+
print("置信度 : ",alpha*100,'%',sep='')
|
257
|
+
print("在险价值VaR :",format(round(VaR,2),','))
|
258
|
+
print("VaR比率 : ",round(VaR_ratio*100,2),'%',sep='')
|
259
|
+
|
260
|
+
import datetime as dt; today=dt.date.today()
|
261
|
+
footnote="*数据来源:新浪/stooq,"+str(today)
|
262
|
+
print(footnote)
|
263
|
+
"""
|
264
|
+
titletxt="在险价值:标准正态模型"
|
265
|
+
import datetime as dt; todaydt=dt.date.today()
|
266
|
+
footnote="数据来源:新浪/stooq,"+str(todaydt)
|
267
|
+
"""
|
268
|
+
s=pd.Series({'Item':'持有股票','Value':ticker_name(ticker)})
|
269
|
+
disp_df=disp_df._append(s,ignore_index=True)
|
270
|
+
|
271
|
+
s=pd.Series({'Item':'持有股数','Value':format(shares,',')})
|
272
|
+
disp_df=disp_df._append(s,ignore_index=True)
|
273
|
+
|
274
|
+
s=pd.Series({'Item':'持有日期','Value':today})
|
275
|
+
disp_df=disp_df._append(s,ignore_index=True)
|
276
|
+
|
277
|
+
s=pd.Series({'Item':'预计持有天数','Value':future_days})
|
278
|
+
disp_df=disp_df._append(s,ignore_index=True)
|
279
|
+
|
280
|
+
s=pd.Series({'Item':'置信度','Value':str(alpha*100)+'%'})
|
281
|
+
disp_df=disp_df._append(s,ignore_index=True)
|
282
|
+
|
283
|
+
s=pd.Series({'Item':'在险价值VaR','Value':format(round(VaR,2),',')})
|
284
|
+
disp_df=disp_df._append(s,ignore_index=True)
|
285
|
+
|
286
|
+
s=pd.Series({'Item':'VaR比率','Value':str(round(VaR_ratio*100,2))+'%'})
|
287
|
+
disp_df=disp_df._append(s,ignore_index=True)
|
288
|
+
|
289
|
+
df_display_CSS(disp_df,titletxt=titletxt,footnote=footnote,facecolor='papayawhip',decimals=2, \
|
290
|
+
hide_columns=True,
|
291
|
+
first_col_align='left',second_col_align='right', \
|
292
|
+
last_col_align='right',other_col_align='right', \
|
293
|
+
titile_font_size='14px',footnote_font_size='12px')
|
294
|
+
"""
|
295
|
+
|
296
|
+
data_dict={'持有股票:':ticker_name(ticker), \
|
297
|
+
'持有股数:':format(shares,','), \
|
298
|
+
'持有日期:':today, \
|
299
|
+
'预计持有天数:':future_days, \
|
300
|
+
'置信度:':str(alpha*100)+'%', \
|
301
|
+
'在险价值VaR金额:':format(round(VaR,2),','), \
|
302
|
+
'在险价值VaR比率:':str(round(VaR_ratio*100,2))+'%'}
|
303
|
+
|
304
|
+
print2CSS(data_dict,titletxt=titletxt,footnote=footnote,
|
305
|
+
#facecolor='whitesmoke',
|
306
|
+
)
|
307
|
+
|
308
|
+
return VaR,VaR_ratio
|
309
|
+
|
310
|
+
if __name__ == '__main__':
|
311
|
+
var1,ratio1=stock_VaR_normal_standard('BABA',10000,'2019-08-08',1,0.99)
|
312
|
+
|
313
|
+
#==============================================================================
|
314
|
+
def series_VaR_normal_standard(ticker,shares,datelist, \
|
315
|
+
future_days=1,alpha=0.99,pastyears=1,printout=True):
|
316
|
+
"""
|
317
|
+
功能:一步计算在多个日期持有一定天数股票资产的VaR
|
318
|
+
输入参数:股票代码,持有股数,日期列表(日期型列表),未来持有时间(天数),置信度,使用历史数据的年数
|
319
|
+
输出:多个日期的VaR(日期,股票代码,VaR金额,VaR比率(即单位头寸的VaR))
|
320
|
+
# datelist是datetime类型日期的列表
|
321
|
+
"""
|
322
|
+
|
323
|
+
import pandas as pd
|
324
|
+
result=pd.DataFrame(columns=['date','ticker','VaR','ratio'])
|
325
|
+
for d in datelist:
|
326
|
+
VaR,ratio=stock_VaR_normal_standard(ticker,shares,d, \
|
327
|
+
future_days,alpha,pastyears,printout=False)
|
328
|
+
if (VaR is None) or (ratio is None): continue
|
329
|
+
|
330
|
+
s = pd.Series({'date':d,'ticker':ticker,'VaR':VaR,'ratio':ratio})
|
331
|
+
try:
|
332
|
+
result=result.append(s,ignore_index=True)
|
333
|
+
except:
|
334
|
+
result=result._append(s,ignore_index=True)
|
335
|
+
|
336
|
+
result2=result.set_index(['date'])
|
337
|
+
# result2是dateframe类型
|
338
|
+
|
339
|
+
if printout == False: return result2
|
340
|
+
|
341
|
+
#打印
|
342
|
+
result3=result2.copy()
|
343
|
+
result3['VaR金额']=round(result3['VaR'],2)
|
344
|
+
result3['VaR比例%']=round(result3['ratio']*100,2)
|
345
|
+
result3.drop(columns=['ticker','VaR','ratio'],inplace=True)
|
346
|
+
|
347
|
+
text1="= "+ticker_name(ticker)+": VaR比例,持有"+str(future_days)+"天 ="
|
348
|
+
print(text1)
|
349
|
+
print(result3)
|
350
|
+
import datetime as dt; today=dt.date.today()
|
351
|
+
print("数据来源:新浪/stooq,"+str(today))
|
352
|
+
|
353
|
+
#绘图
|
354
|
+
#VaR金额绘图
|
355
|
+
plt.plot(result3['VaR金额'],c='r',lw=2)
|
356
|
+
title1=ticker_name(ticker)+": VaR金额的变化,持有"+str(future_days)+"天"
|
357
|
+
plt.title(title1)
|
358
|
+
plt.ylabel('VaR金额')
|
359
|
+
#plt.gcf().autofmt_xdate() # 优化标注(自动倾斜)
|
360
|
+
plt.xticks(rotation=30)
|
361
|
+
|
362
|
+
import datetime as dt; today=dt.date.today()
|
363
|
+
footnote="数据来源:新浪/stooq,"+str(today)
|
364
|
+
plt.xlabel(footnote)
|
365
|
+
|
366
|
+
plt.gca().set_facecolor('whitesmoke')
|
367
|
+
plt.show()
|
368
|
+
|
369
|
+
plt.plot(result3['VaR比例%'],c='r',lw=2)
|
370
|
+
title2=ticker_name(ticker)+": VaR比例的变化, 持有"+str(future_days)+"天"
|
371
|
+
plt.title(title2)
|
372
|
+
plt.ylabel('VaR比例%')
|
373
|
+
#plt.gcf().autofmt_xdate() # 优化标注(自动倾斜)
|
374
|
+
plt.xticks(rotation=30)
|
375
|
+
plt.xlabel(footnote)
|
376
|
+
|
377
|
+
plt.gca().set_facecolor('whitesmoke')
|
378
|
+
plt.show()
|
379
|
+
|
380
|
+
return result2
|
381
|
+
|
382
|
+
if __name__ == '__main__':
|
383
|
+
datelist=['2018-01-01','2018-04-01','2018-07-01','2018-10-01', \
|
384
|
+
'2019-01-01','2019-04-01','2019-07-01']
|
385
|
+
result=series_VaR_normal_standard('BABA',10000,datelist,1,0.99)
|
386
|
+
|
387
|
+
|
388
|
+
#==============================================================================
|
389
|
+
def compare_VaR(ticker,shares,datelist, \
|
390
|
+
future_days=1,alpha=0.99,pastyears=1,model='normal_standard'):
|
391
|
+
"""
|
392
|
+
功能:比较多个日期持有一定天数股票资产的VaR高低
|
393
|
+
输入参数:股票列表,持有股数,日期列表(日期型列表),未来持有时间(天数),
|
394
|
+
置信度,使用历史数据的年数,选择的模型方法
|
395
|
+
模型方法:目前仅支持normal_standard和normal_modified
|
396
|
+
输出:无
|
397
|
+
显示:折线图,各个资产的VaR金额和比率对比
|
398
|
+
"""
|
399
|
+
tickerlist=ticker
|
400
|
+
|
401
|
+
modellist=['normal_standard','normal_modified']
|
402
|
+
if not (model in modellist):
|
403
|
+
print(" #Error(compare_VaR): unsupported model",model)
|
404
|
+
print(" Supported models at present:",modellist)
|
405
|
+
return
|
406
|
+
|
407
|
+
if model in ['normal_standard']:
|
408
|
+
compare_VaR_normal_standard(tickerlist=tickerlist,shares=shares, \
|
409
|
+
datelist=datelist,future_days=future_days,alpha=alpha, \
|
410
|
+
pastyears=pastyears)
|
411
|
+
return
|
412
|
+
|
413
|
+
if model in ['normal_modified']:
|
414
|
+
compare_VaR_normal_modified(tickerlist=tickerlist,shares=shares, \
|
415
|
+
datelist=datelist,future_days=future_days,alpha=alpha, \
|
416
|
+
pastyears=pastyears)
|
417
|
+
return
|
418
|
+
|
419
|
+
|
420
|
+
|
421
|
+
|
422
|
+
#==============================================================================
|
423
|
+
if __name__ == '__main__':
|
424
|
+
tickerlist=['BABA','PDD','JD']
|
425
|
+
shares=10000
|
426
|
+
datelist=['2019-01-01','2019-02-01','2019-03-01','2019-04-01', \
|
427
|
+
'2019-05-01','2019-06-01','2019-07-01']
|
428
|
+
future_days=1
|
429
|
+
alpha=0.99
|
430
|
+
pastyears=1
|
431
|
+
|
432
|
+
|
433
|
+
def compare_VaR_normal_standard(tickerlist,shares,datelist, \
|
434
|
+
future_days=1,alpha=0.99,pastyears=1):
|
435
|
+
"""
|
436
|
+
功能:比较多个日期持有一定天数股票资产的VaR高低
|
437
|
+
输入参数:股票列表,持有股数,日期列表(日期型列表),未来持有时间(天数),置信度,使用历史数据的年数
|
438
|
+
输出:无
|
439
|
+
显示:折线图,各个资产的VaR金额和比率对比
|
440
|
+
"""
|
441
|
+
|
442
|
+
import os, sys
|
443
|
+
class HiddenPrints:
|
444
|
+
def __enter__(self):
|
445
|
+
self._original_stdout = sys.stdout
|
446
|
+
sys.stdout = open(os.devnull, 'w')
|
447
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
448
|
+
sys.stdout.close()
|
449
|
+
sys.stdout = self._original_stdout
|
450
|
+
|
451
|
+
print(" The comparison may take time, please wait ...")
|
452
|
+
markerlist=['.','o','s','*','+','x','1','2']
|
453
|
+
for t in tickerlist:
|
454
|
+
pos=tickerlist.index(t)
|
455
|
+
thismarker=markerlist[pos]
|
456
|
+
|
457
|
+
with HiddenPrints():
|
458
|
+
r=series_VaR_normal_standard(t,shares,datelist, \
|
459
|
+
future_days,alpha,pastyears,printout=False)
|
460
|
+
rr=r.copy()
|
461
|
+
rr['VaR amount']=round(rr['VaR'],2)
|
462
|
+
rr.drop(columns=['ticker','VaR','ratio'],inplace=True)
|
463
|
+
|
464
|
+
rr['date']=rr.index
|
465
|
+
import pandas as pd
|
466
|
+
rr['date']=rr['date'].apply(lambda x: pd.to_datetime(x))
|
467
|
+
rr.set_index(['date'],inplace=True)
|
468
|
+
|
469
|
+
plt.plot(rr['VaR amount'],label=ticker_name(t),lw=2,marker=thismarker)
|
470
|
+
|
471
|
+
title1="比较VaR金额,持有"+str(future_days)+"天"
|
472
|
+
plt.title(title1)
|
473
|
+
plt.ylabel('VaR金额')
|
474
|
+
plt.xticks(rotation=30)
|
475
|
+
plt.legend(loc='best')
|
476
|
+
|
477
|
+
notes="【注】VaR计算模型:标准正态法"
|
478
|
+
import datetime as dt; today=dt.date.today()
|
479
|
+
source="数据来源:新浪/stooq,"+str(today)
|
480
|
+
footnote=notes+'\n'+source
|
481
|
+
plt.xlabel(footnote)
|
482
|
+
|
483
|
+
plt.gca().set_facecolor('whitesmoke')
|
484
|
+
plt.show()
|
485
|
+
|
486
|
+
for t in tickerlist:
|
487
|
+
pos=tickerlist.index(t)
|
488
|
+
thismarker=markerlist[pos]
|
489
|
+
|
490
|
+
with HiddenPrints():
|
491
|
+
r=series_VaR_normal_standard(t,shares,datelist, \
|
492
|
+
future_days,alpha,pastyears,printout=False)
|
493
|
+
rr=r.copy()
|
494
|
+
rr['VaR ratio %']=round(rr['ratio']*100,2)
|
495
|
+
rr.drop(columns=['ticker','VaR','ratio'],inplace=True)
|
496
|
+
|
497
|
+
rr['date']=rr.index
|
498
|
+
import pandas as pd
|
499
|
+
rr['date']=rr['date'].apply(lambda x: pd.to_datetime(x))
|
500
|
+
rr.set_index(['date'],inplace=True)
|
501
|
+
|
502
|
+
|
503
|
+
plt.plot(rr['VaR ratio %'],label=ticker_name(t),lw=2,marker=thismarker)
|
504
|
+
|
505
|
+
title2="比较VaR比例,持有"+str(future_days)+"天"
|
506
|
+
plt.title(title2)
|
507
|
+
plt.ylabel('VaR比例%')
|
508
|
+
plt.xticks(rotation=30)
|
509
|
+
plt.legend(loc='best')
|
510
|
+
plt.xlabel(footnote)
|
511
|
+
|
512
|
+
plt.gca().set_facecolor('whitesmoke')
|
513
|
+
plt.show()
|
514
|
+
|
515
|
+
return
|
516
|
+
|
517
|
+
if __name__ == '__main__':
|
518
|
+
tickerlist=['BABA','PDD','JD']
|
519
|
+
datelist=['2019-01-01','2019-02-01','2019-03-01','2019-04-01', \
|
520
|
+
'2019-05-01','2019-06-01','2019-07-01']
|
521
|
+
compare_VaR_normal_standard(tickerlist,10000,datelist,1,0.99)
|
522
|
+
|
523
|
+
#==============================================================================
|
524
|
+
def ES_normal_standard(position,ret_series,future_days=1,alpha=0.99):
|
525
|
+
"""
|
526
|
+
功能:计算ES,标准正态法
|
527
|
+
输入参数:持有头寸金额,日收益率序列,未来持有日期,置信度
|
528
|
+
输出:预期损失(金额)
|
529
|
+
"""
|
530
|
+
import numpy as np
|
531
|
+
from scipy import stats
|
532
|
+
|
533
|
+
z=stats.norm.ppf(1-alpha)
|
534
|
+
miu_daily=np.mean(ret_series)
|
535
|
+
miu_days=np.power(miu_daily+1,future_days)-1
|
536
|
+
sigma_daily=np.std(ret_series)
|
537
|
+
sigma_days=np.sqrt(future_days)*sigma_daily
|
538
|
+
|
539
|
+
zES=-stats.norm.pdf(z)/(1-alpha)
|
540
|
+
ratio=miu_days+zES*sigma_days
|
541
|
+
ES_days=position*ratio
|
542
|
+
|
543
|
+
#return -ES_days
|
544
|
+
return ES_days
|
545
|
+
|
546
|
+
#==============================================================================
|
547
|
+
def stock_ES(ticker,shares,today,future_days=1,alpha=0.99, \
|
548
|
+
pastyears=1,printout=True,random=10000,mctype='random', \
|
549
|
+
model="normal_standard"):
|
550
|
+
"""
|
551
|
+
功能:持有股票的ES,支持选择模型
|
552
|
+
输入参数:股票代码,持有股数,当前日期,未来持有时间(天),置信度,
|
553
|
+
使用历史数据的年数,是否打印结果,模型方法
|
554
|
+
模型方法:标准正态法normal_standard
|
555
|
+
输出:VaR(负数金额,单位与股价的金额单位相同),VaR比率
|
556
|
+
"""
|
557
|
+
# 检查支持的模型
|
558
|
+
modellist=['normal_standard']
|
559
|
+
if not (model in modellist):
|
560
|
+
print(" #Error(stock_VaR): unsupported model",model)
|
561
|
+
print(" Supported models at present:",modellist)
|
562
|
+
return None,None
|
563
|
+
|
564
|
+
if model in ['normal_standard']:
|
565
|
+
ES,ratio=stock_ES_normal_standard(ticker=ticker,shares=shares, \
|
566
|
+
today=today,future_days=future_days,alpha=alpha, \
|
567
|
+
pastyears=pastyears,printout=printout)
|
568
|
+
return ES,ratio
|
569
|
+
|
570
|
+
#==============================================================================
|
571
|
+
def security_ES(ticker,shares,today,future_days=1,alpha=0.99, \
|
572
|
+
pastyears=1,printout=True,random=10000,mctype='random', \
|
573
|
+
model="normal_standard"):
|
574
|
+
"""
|
575
|
+
功能:持有证券的预期不足ES,支持选择模型
|
576
|
+
输入参数:证券代码,持有股数,当前日期,未来持有时间(天),置信度,
|
577
|
+
使用历史数据的年数,是否打印结果,模型方法
|
578
|
+
模型方法:默认标准正态法normal_standard
|
579
|
+
说明:套壳函数stock_ES
|
580
|
+
"""
|
581
|
+
ES,ESratio=stock_ES(ticker=ticker,shares=shares,today=today,future_days=future_days,alpha=alpha, \
|
582
|
+
pastyears=pastyears,printout=printout,random=random,mctype=mctype, \
|
583
|
+
model=model)
|
584
|
+
|
585
|
+
return
|
586
|
+
|
587
|
+
#==============================================================================
|
588
|
+
if __name__ == '__main__':
|
589
|
+
ticker='JD'
|
590
|
+
shares=1000
|
591
|
+
today='2023-2-6'
|
592
|
+
future_days=1
|
593
|
+
alpha=0.99
|
594
|
+
pastyears=1
|
595
|
+
printout=True
|
596
|
+
|
597
|
+
|
598
|
+
def stock_ES_normal_standard(ticker,shares,today, \
|
599
|
+
future_days=1,alpha=0.99,pastyears=1,printout=True):
|
600
|
+
"""
|
601
|
+
功能:计算持有股票资产的ES,标准正态法
|
602
|
+
输入参数:股票代码,持有股数,当前日期,未来持有日期(天数),置信度,使用历史数据的年数
|
603
|
+
#输出参数:预期损失(金额)负数,预期损失与头寸的比率
|
604
|
+
"""
|
605
|
+
start=get_start_date(today,pastyears)
|
606
|
+
p=get_stock_quotes(ticker,start,today)
|
607
|
+
|
608
|
+
if p is None:
|
609
|
+
print(" #Error(stock_ES_normal_standard): no obs retrieved.")
|
610
|
+
return None,None
|
611
|
+
if len(p)==0:
|
612
|
+
print(" #Error(stock_ES_normal_standard): zero obs retrieved.")
|
613
|
+
return None,None
|
614
|
+
|
615
|
+
r=get_ret_series(p)
|
616
|
+
p_end=get_end_price(p)
|
617
|
+
position=shares*p_end
|
618
|
+
ES=ES_normal_standard(position,r,future_days,alpha)
|
619
|
+
#最大为全损
|
620
|
+
if abs(ES) > position: ES=-position
|
621
|
+
|
622
|
+
ratio=abs(ES/position)
|
623
|
+
|
624
|
+
if printout == True:
|
625
|
+
"""
|
626
|
+
print("\n=== 计算预期不足ES:标准正态模型 ===")
|
627
|
+
print("持有股票 :",ticker_name(ticker))
|
628
|
+
print("持有股数 :",format(shares,','))
|
629
|
+
print("持有日期 :",today)
|
630
|
+
print("预计持有天数:",future_days)
|
631
|
+
print("置信度 : ",alpha*100,'%',sep='')
|
632
|
+
print("ES金额 :",format(round(ES,2),','))
|
633
|
+
print("ES比例 : ",round(ratio*100,2),'%',sep='')
|
634
|
+
|
635
|
+
import datetime as dt; today=dt.date.today()
|
636
|
+
print("数据来源:新浪/stooq,"+str(today))
|
637
|
+
"""
|
638
|
+
|
639
|
+
titletxt="预期不足:标准正态模型"
|
640
|
+
import datetime as dt; todaydt=dt.date.today()
|
641
|
+
footnote="数据来源:新浪/stooq,"+str(todaydt)
|
642
|
+
|
643
|
+
data_dict={'持有股票:':ticker_name(ticker), \
|
644
|
+
'持有股数:':format(shares,','), \
|
645
|
+
'持有日期:':today, \
|
646
|
+
'预计持有天数:':future_days, \
|
647
|
+
'置信度:':str(alpha*100)+'%', \
|
648
|
+
'预期不足ES金额:':format(round(ES,2),','), \
|
649
|
+
'预期不足ES比例:':str(round(ratio*100,2))+'%'}
|
650
|
+
|
651
|
+
print2CSS(data_dict,titletxt=titletxt,footnote=footnote)
|
652
|
+
|
653
|
+
return ES,ratio
|
654
|
+
|
655
|
+
|
656
|
+
#==============================================================================
|
657
|
+
def series_ES_normal_standard(ticker,shares,datelist, \
|
658
|
+
future_days=1,alpha=0.99,pastyears=1,printout=True):
|
659
|
+
"""
|
660
|
+
功能:一步计算在多个日期持有一定天数股票资产的ES,标准正态法
|
661
|
+
输入参数:股票代码,持有股数,日期列表(日期型列表),未来持有时间(天数),置信度,使用历史数据的年数
|
662
|
+
输出:多个日期的ES(日期,股票代码,ES金额,ES比率(即单位头寸的ES))
|
663
|
+
# datelist是datetime类型日期的列表
|
664
|
+
"""
|
665
|
+
|
666
|
+
import pandas as pd
|
667
|
+
result=pd.DataFrame(columns=['date','ticker','ES','ratio'])
|
668
|
+
for d in datelist:
|
669
|
+
ES,ratio=stock_ES_normal_standard(ticker,shares,d, \
|
670
|
+
future_days,alpha,pastyears,printout=False)
|
671
|
+
if (ES is None) or (ratio is None): continue
|
672
|
+
|
673
|
+
s = pd.Series({'date':d,'ticker':ticker,'ES':ES,'ratio':ratio})
|
674
|
+
try:
|
675
|
+
result=result.append(s,ignore_index=True)
|
676
|
+
except:
|
677
|
+
result=result._append(s,ignore_index=True)
|
678
|
+
|
679
|
+
result2=result.set_index(['date'])
|
680
|
+
# result2是dateframe类型
|
681
|
+
|
682
|
+
if printout == False: return result2
|
683
|
+
|
684
|
+
#打印
|
685
|
+
result3=result2.copy()
|
686
|
+
result3['ES金额']=round(result3['ES'],2)
|
687
|
+
result3['ES比例%']=round(result3['ratio']*100,2)
|
688
|
+
result3.drop(columns=['ticker','ES','ratio'],inplace=True)
|
689
|
+
|
690
|
+
text1="=== "+ticker_name(ticker)+": ES比例,持有"+str(future_days)+"天 ==="
|
691
|
+
print(text1)
|
692
|
+
print(result3)
|
693
|
+
import datetime as dt; today=dt.date.today()
|
694
|
+
print("数据来源:新浪/stooq,"+str(today))
|
695
|
+
|
696
|
+
#绘图
|
697
|
+
#VaR金额绘图
|
698
|
+
plt.plot(result3['ES金额'],c='r',lw=2)
|
699
|
+
title1=ticker_name(ticker)+": ES金额的变化,持有"+str(future_days)+"天"
|
700
|
+
plt.title(title1)
|
701
|
+
plt.ylabel('ES金额')
|
702
|
+
plt.xticks(rotation=30)
|
703
|
+
|
704
|
+
plt.gca().set_facecolor('whitesmoke')
|
705
|
+
plt.show()
|
706
|
+
|
707
|
+
plt.plot(result3['ES比例%'],c='r',lw=2)
|
708
|
+
title2=ticker_name(ticker)+": ES比例的变化,持有"+str(future_days)+"天"
|
709
|
+
plt.title(title2)
|
710
|
+
plt.ylabel('ES比例%')
|
711
|
+
plt.xticks(rotation=30)
|
712
|
+
|
713
|
+
plt.gca().set_facecolor('whitesmoke')
|
714
|
+
plt.show()
|
715
|
+
|
716
|
+
return result2
|
717
|
+
|
718
|
+
if __name__ == '__main__':
|
719
|
+
datelist=['2018-01-01','2018-04-01','2018-07-01','2018-10-01', \
|
720
|
+
'2019-01-01','2019-04-01','2019-07-01']
|
721
|
+
result=series_ES_normal_standard('BABA',10000,datelist,1,0.99)
|
722
|
+
|
723
|
+
#==============================================================================
|
724
|
+
def compare_ES(ticker,shares,datelist, \
|
725
|
+
future_days=1,alpha=0.99,pastyears=1,model='normal_standard'):
|
726
|
+
"""
|
727
|
+
功能:比较多个日期持有一定天数股票资产的ES高低
|
728
|
+
输入参数:股票列表,持有股数,日期列表(日期型列表),未来持有时间(天数),
|
729
|
+
置信度,使用历史数据的年数,选择的模型方法
|
730
|
+
模型方法:目前仅支持normal_standard
|
731
|
+
输出:无
|
732
|
+
显示:折线图,各个资产的ES金额和比率对比
|
733
|
+
"""
|
734
|
+
tickerlist=ticker
|
735
|
+
|
736
|
+
modellist=['normal_standard']
|
737
|
+
if not (model in modellist):
|
738
|
+
print(" #Error(compare_ES): unsupported model",model)
|
739
|
+
print(" Supported models at present:",modellist)
|
740
|
+
return
|
741
|
+
|
742
|
+
if model in ['normal_standard']:
|
743
|
+
compare_ES_normal_standard(tickerlist=tickerlist,shares=shares, \
|
744
|
+
datelist=datelist,future_days=future_days,alpha=alpha, \
|
745
|
+
pastyears=pastyears)
|
746
|
+
return
|
747
|
+
|
748
|
+
#==============================================================================
|
749
|
+
def compare_ES_normal_standard(tickerlist,shares,datelist, \
|
750
|
+
future_days=1,alpha=0.99,pastyears=1):
|
751
|
+
"""
|
752
|
+
功能:比较多个日期持有一定天数股票资产的ES高低,标准正态法
|
753
|
+
输入参数:股票列表,持有股数,日期列表(日期型列表),未来持有时间(天数),置信度,使用历史数据的年数
|
754
|
+
输出:无
|
755
|
+
显示:折线图,各个资产的ES金额和比率对比
|
756
|
+
"""
|
757
|
+
|
758
|
+
import os, sys
|
759
|
+
class HiddenPrints:
|
760
|
+
def __enter__(self):
|
761
|
+
self._original_stdout = sys.stdout
|
762
|
+
sys.stdout = open(os.devnull, 'w')
|
763
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
764
|
+
sys.stdout.close()
|
765
|
+
sys.stdout = self._original_stdout
|
766
|
+
|
767
|
+
print(" The comparison may take time, please wait ...")
|
768
|
+
markerlist=['.','o','s','*','+','x','1','2']
|
769
|
+
for t in tickerlist:
|
770
|
+
pos=tickerlist.index(t)
|
771
|
+
thismarker=markerlist[pos]
|
772
|
+
|
773
|
+
with HiddenPrints():
|
774
|
+
r=series_ES_normal_standard(t,shares,datelist, \
|
775
|
+
future_days,alpha,pastyears,printout=False)
|
776
|
+
rr=r.copy()
|
777
|
+
rr['ES amount']=round(rr['ES'],2)
|
778
|
+
rr.drop(columns=['ticker','ES','ratio'],inplace=True)
|
779
|
+
plt.plot(rr['ES amount'],label=ticker_name(t),lw=2,marker=thismarker)
|
780
|
+
|
781
|
+
title1="比较ES金额,持有"+str(future_days)+"天"
|
782
|
+
plt.title(title1)
|
783
|
+
plt.ylabel('ES金额')
|
784
|
+
plt.xticks(rotation=30)
|
785
|
+
plt.legend(loc='best')
|
786
|
+
|
787
|
+
notes="【注】ES计算模型:标准正态法"
|
788
|
+
import datetime as dt; today=dt.date.today()
|
789
|
+
source="数据来源:新浪/stooq,"+str(today)
|
790
|
+
footnote=notes+'\n'+source
|
791
|
+
plt.xlabel(footnote)
|
792
|
+
plt.show()
|
793
|
+
|
794
|
+
for t in tickerlist:
|
795
|
+
pos=tickerlist.index(t)
|
796
|
+
thismarker=markerlist[pos]
|
797
|
+
|
798
|
+
with HiddenPrints():
|
799
|
+
r=series_ES_normal_standard(t,shares,datelist, \
|
800
|
+
future_days,alpha,pastyears,printout=False)
|
801
|
+
rr=r.copy()
|
802
|
+
rr['ES ratio %']=round(rr['ratio']*100,2)
|
803
|
+
rr.drop(columns=['ticker','ES','ratio'],inplace=True)
|
804
|
+
plt.plot(rr['ES ratio %'],label=ticker_name(t),lw=2,marker=thismarker)
|
805
|
+
|
806
|
+
title2="比较ES比例,持有"+str(future_days)+"天"
|
807
|
+
plt.title(title2)
|
808
|
+
plt.ylabel('ES比例%')
|
809
|
+
plt.xticks(rotation=30)
|
810
|
+
plt.legend(loc='best')
|
811
|
+
plt.xlabel(footnote)
|
812
|
+
plt.show()
|
813
|
+
|
814
|
+
return
|
815
|
+
|
816
|
+
if __name__ == '__main__':
|
817
|
+
tickerlist=['BABA','PDD','JD']
|
818
|
+
datelist=['2019-01-01','2019-02-01','2019-03-01','2019-04-01', \
|
819
|
+
'2019-05-01','2019-06-01','2019-07-01']
|
820
|
+
compare_ES_normal_standard(tickerlist,10000,datelist,1,0.99)
|
821
|
+
|
822
|
+
|
823
|
+
#==============================================================================
|
824
|
+
def normfunc(x,mu,sigma):
|
825
|
+
#计算正态分布的概率密度,带有均值和标准差
|
826
|
+
import numpy as np
|
827
|
+
pdf = np.exp(-((x - mu)**2)/(2*sigma**2)) / (sigma * np.sqrt(2*np.pi))
|
828
|
+
return pdf
|
829
|
+
|
830
|
+
#==============================================================================
|
831
|
+
def plot_rets_histogram(ticker,start,end,num_bins=20):
|
832
|
+
"""
|
833
|
+
功能:绘制收益率分布的直方图,并于相应的正态分布图对照
|
834
|
+
输入:股票代码,开始/结束时间
|
835
|
+
显示:收益率分布的直方图(实线),相应的正态分布图(虚线)
|
836
|
+
x轴为收益率(非百分比),y轴为频度(Frequency)
|
837
|
+
"""
|
838
|
+
#抓取股价并计算收益率
|
839
|
+
quotes=get_stock_quotes(ticker,start,end)
|
840
|
+
if (quotes is None) or (len(quotes)==0):
|
841
|
+
print("#Error(plot_rets_histogram): Fetching data failed")
|
842
|
+
print("Information:",ticker,start,end)
|
843
|
+
return
|
844
|
+
rets=get_ret_series(quotes)
|
845
|
+
|
846
|
+
#计算收益率的均值和标准差
|
847
|
+
mu=rets.mean()
|
848
|
+
sigma=rets.std()
|
849
|
+
|
850
|
+
#绘制股票收益率直方图
|
851
|
+
#plt.figure(figsize=(8,4))
|
852
|
+
plt.figure(figsize=(12.8,6.4))
|
853
|
+
n,bins,patches=plt.hist(rets,num_bins,facecolor='blue',alpha=0.5,label=ticker_name(ticker))
|
854
|
+
|
855
|
+
#生成与直方图柱子对应的正态分布概率密度
|
856
|
+
y=normfunc(bins,mu,sigma)
|
857
|
+
#绘制正态分布曲线
|
858
|
+
plt.plot(bins,y,'r--',label='正态分布',lw=2)
|
859
|
+
plt.ylabel('Frequency')
|
860
|
+
plt.xlabel('Stock return')
|
861
|
+
titletxt="正态性检验:"+ticker_name(ticker)+"收益率, "+start+"至"+end
|
862
|
+
plt.title(titletxt)
|
863
|
+
plt.legend(loc='best')
|
864
|
+
|
865
|
+
import datetime as dt; today=dt.date.today()
|
866
|
+
footnote="数据来源:新浪/stooq,"+str(today)
|
867
|
+
plt.xlabel(footnote)
|
868
|
+
plt.show()
|
869
|
+
|
870
|
+
return
|
871
|
+
|
872
|
+
if __name__ == '__main__':
|
873
|
+
plot_rets_histogram('JD','2019-1-1','2019-6-30')
|
874
|
+
|
875
|
+
#==============================================================================
|
876
|
+
def plot_rets_curve(ticker,start='MRY',end='today'):
|
877
|
+
"""
|
878
|
+
功能:绘制收益率分布的曲线,并于相应的正态分布图对照
|
879
|
+
输入:股票代码,开始/结束时间
|
880
|
+
显示:收益率分布的直方图(实线),相应的正态分布图(虚线)
|
881
|
+
x轴为收益率(非百分比),y轴为频度(Frequency)
|
882
|
+
"""
|
883
|
+
start,end=start_end_preprocess(start,end)
|
884
|
+
|
885
|
+
#抓取股价并计算收益率
|
886
|
+
quotes=get_stock_quotes(ticker,start,end)
|
887
|
+
if (quotes is None) or (len(quotes)==0):
|
888
|
+
print("#Error(plot_rets_curve): Fetching data failed")
|
889
|
+
print("Information:",ticker,start,end)
|
890
|
+
return
|
891
|
+
rets=get_ret_series(quotes)
|
892
|
+
|
893
|
+
#计算收益率的均值和标准差
|
894
|
+
mu=rets.mean()
|
895
|
+
sigma=rets.std()
|
896
|
+
|
897
|
+
#生成符合正态分布的随机数,符合股票收益率的均值和标准差
|
898
|
+
import numpy as np
|
899
|
+
x=mu+sigma*np.random.randn(1000000)
|
900
|
+
|
901
|
+
#plt.figure(figsize=(12.8,6.4))
|
902
|
+
import seaborn as sns
|
903
|
+
#绘制曲线:股票收益率
|
904
|
+
sns.kdeplot(data=rets,shade=True,color='blue',legend=True,label=ticker_name(ticker),lw=2)
|
905
|
+
#绘制曲线:对应的正态分布
|
906
|
+
sns.kdeplot(data=x,shade=True,color='r',legend=True,label='正态分布',ls='--')
|
907
|
+
#设置标题、图例、坐标轴标签
|
908
|
+
plt.ylabel('',fontsize=ylabel_txt_size)
|
909
|
+
#plt.xlabel('收益率')
|
910
|
+
plt.legend(loc='best',fontsize=legend_txt_size)
|
911
|
+
titletxt="正态性检验: "+ticker_name(ticker)+",收益率, "+start+"至"+end
|
912
|
+
plt.title(titletxt,fontsize=title_txt_size)
|
913
|
+
|
914
|
+
import datetime as dt; today=dt.date.today()
|
915
|
+
footnote="数据来源:新浪/stooq,"+str(today)
|
916
|
+
plt.xlabel(footnote,fontsize=xlabel_txt_size)
|
917
|
+
plt.show()
|
918
|
+
|
919
|
+
return
|
920
|
+
|
921
|
+
|
922
|
+
if __name__ == '__main__':
|
923
|
+
plot_rets_curve('JD','2019-1-1','2019-6-30')
|
924
|
+
|
925
|
+
|
926
|
+
#===========================================================================
|
927
|
+
def stock_ret_Normality_SW(ticker,start='MRY',end='today',siglevel=0.05):
|
928
|
+
"""
|
929
|
+
功能:测试一个日收益率序列是否符合正态分布,原假设:符合正态分布
|
930
|
+
输入参数:股票代码,开始日期,结束日期
|
931
|
+
输出:日收益率序列正态性检验的p-value
|
932
|
+
start_date,end_date均为datetime类型
|
933
|
+
【Shapiro-Wilk正态性检验】原假设:服从正态分布
|
934
|
+
"""
|
935
|
+
start_date,end_date=start_end_preprocess(start,end)
|
936
|
+
|
937
|
+
from scipy import stats
|
938
|
+
|
939
|
+
quotes=get_stock_quotes(ticker,start_date,end_date)
|
940
|
+
if quotes is None: return None
|
941
|
+
if len(quotes) == 0: return None
|
942
|
+
|
943
|
+
ret=get_ret_series(quotes)
|
944
|
+
(W,p_value)=stats.shapiro(ret)
|
945
|
+
|
946
|
+
"""
|
947
|
+
print("\n= Shapiro-Wilk正态性检验: 股票收益率 =")
|
948
|
+
print("股票 :",ticker_name(ticker))
|
949
|
+
print("期间 :",start_date,"至",end_date)
|
950
|
+
print("原假设: 符合正态分布")
|
951
|
+
print("W值 :",round(W,4))
|
952
|
+
print("p值 :",round(p_value,4))
|
953
|
+
if p_value >= siglevel:
|
954
|
+
print("结果 : 接受原假设, 符合正态分布")
|
955
|
+
else:
|
956
|
+
print("结果 : 拒绝原假设, 不符合正态分布")
|
957
|
+
import datetime as dt; today=dt.date.today()
|
958
|
+
print("*数据来源:新浪/stooq,"+str(today))
|
959
|
+
"""
|
960
|
+
|
961
|
+
titletxt="Shapiro-Wilk检验: 收益率"
|
962
|
+
data_dict1={'股票:':ticker_name(ticker), \
|
963
|
+
'期间:':start_date+"至"+end_date, \
|
964
|
+
'原假设:':'符合正态分布', \
|
965
|
+
'W值:':round(W,4), \
|
966
|
+
'p值:':round(p_value,4)}
|
967
|
+
|
968
|
+
if p_value >= siglevel:
|
969
|
+
result="接受原假设, 符合正态分布"
|
970
|
+
else:
|
971
|
+
result="拒绝原假设, 不符合正态分布"
|
972
|
+
data_dict2={'结果:':result}
|
973
|
+
data_dict={**data_dict1,**data_dict2}
|
974
|
+
|
975
|
+
import datetime as dt; todaydt=dt.date.today()
|
976
|
+
footnote="数据来源:新浪/stooq,"+str(todaydt)
|
977
|
+
|
978
|
+
print2CSS(data_dict,titletxt=titletxt,footnote=footnote)
|
979
|
+
|
980
|
+
return p_value
|
981
|
+
|
982
|
+
#==============================================================================
|
983
|
+
def VaR_normal_modified(position,ret_series,future_days=1,alpha=0.99):
|
984
|
+
"""
|
985
|
+
功能:VaR基本算法,修正正态法
|
986
|
+
#输入参数:持有头寸金额,日收益率序列,未来持有日期,置信度
|
987
|
+
#输出参数:VaR(金额)
|
988
|
+
"""
|
989
|
+
#去掉空值
|
990
|
+
r=ret_series[~ret_series.isnull()]
|
991
|
+
|
992
|
+
from scipy import stats
|
993
|
+
import numpy as np
|
994
|
+
|
995
|
+
z=np.abs(stats.norm.ppf(1-alpha))
|
996
|
+
S=stats.skew(r)
|
997
|
+
K=stats.kurtosis(r)
|
998
|
+
|
999
|
+
t1=1/6*(np.power(z,2)-1)*S
|
1000
|
+
t2=1/24*(np.power(z,3)-3*z)*K
|
1001
|
+
t3=1/36*(2*np.power(z,3)-5*z)*np.power(S,2)
|
1002
|
+
t=z+t1+t2-t3
|
1003
|
+
|
1004
|
+
miu_daily=np.mean(r)
|
1005
|
+
miu_days=np.power(miu_daily+1,future_days)-1
|
1006
|
+
sigma_daily=np.std(r)
|
1007
|
+
sigma_days=np.sqrt(future_days)*sigma_daily
|
1008
|
+
|
1009
|
+
ratio=miu_days+t*sigma_days
|
1010
|
+
VaR_days=position*ratio
|
1011
|
+
|
1012
|
+
return -abs(VaR_days)
|
1013
|
+
|
1014
|
+
#==============================================================================
|
1015
|
+
def stock_VaR_normal_modified(ticker,shares,today, \
|
1016
|
+
future_days=1,alpha=0.99,pastyears=1,printout=True):
|
1017
|
+
"""
|
1018
|
+
功能:计算持有一定量股票若干天的VaR,修正正态法
|
1019
|
+
输入参数:股票代码,持有股数,当前日期,未来持有天数,置信度,使用历史数据的年数
|
1020
|
+
注:当前日期可以为过去的任意一天,历史年数为使用几年的历史数据来分析
|
1021
|
+
输出:VaR(金额,负数),VaR比率
|
1022
|
+
注释:VaR比率,即每单位持有金额的在险价值
|
1023
|
+
"""
|
1024
|
+
start=get_start_date(today,pastyears)
|
1025
|
+
p=get_stock_quotes(ticker,start,today)
|
1026
|
+
if (p is None) or (len(p)==0):
|
1027
|
+
print(" #Error(stock_VaR_normal_modified): no observation retrieved.")
|
1028
|
+
return None,None
|
1029
|
+
|
1030
|
+
r=get_ret_series(p)
|
1031
|
+
p_end=get_end_price(p)
|
1032
|
+
position=shares*p_end
|
1033
|
+
VaR=VaR_normal_modified(position,r,future_days,alpha)
|
1034
|
+
#最大为全损
|
1035
|
+
if abs(VaR) > position: VaR=-position
|
1036
|
+
|
1037
|
+
VaR_ratio=abs(VaR/position)
|
1038
|
+
|
1039
|
+
if printout == True:
|
1040
|
+
"""
|
1041
|
+
print("\n=== 在险价值VaR: 修正正态模型 ===")
|
1042
|
+
print("持有股票 :",ticker_name(ticker))
|
1043
|
+
print("持有股数 :",format(shares,','))
|
1044
|
+
print("持有日期 :",today)
|
1045
|
+
print("预计持有天数:",future_days)
|
1046
|
+
print("置信度 : ",alpha*100,'%',sep='')
|
1047
|
+
print("VaR金额 :",format(round(VaR,2),','))
|
1048
|
+
print("VaR比例 : ",round(VaR_ratio*100,2),'%',sep='')
|
1049
|
+
|
1050
|
+
import datetime as dt; today=dt.date.today()
|
1051
|
+
print("*数据来源:新浪/stooq,"+str(today))
|
1052
|
+
"""
|
1053
|
+
titletxt="在险价值:修正正态模型"
|
1054
|
+
import datetime as dt; todaydt=dt.date.today()
|
1055
|
+
footnote="数据来源:新浪/stooq,"+str(todaydt)
|
1056
|
+
|
1057
|
+
data_dict={'持有股票:':ticker_name(ticker), \
|
1058
|
+
'持有股数:':format(shares,','), \
|
1059
|
+
'持有日期:':today, \
|
1060
|
+
'预计持有天数:':future_days, \
|
1061
|
+
'置信度:':str(alpha*100)+'%', \
|
1062
|
+
'在险价值VaR金额:':format(round(VaR,2),','), \
|
1063
|
+
'在险价值VaR比例:':str(round(VaR_ratio*100,2))+'%'}
|
1064
|
+
|
1065
|
+
print2CSS(data_dict,titletxt=titletxt,footnote=footnote)
|
1066
|
+
|
1067
|
+
return VaR,VaR_ratio
|
1068
|
+
|
1069
|
+
#==============================================================================
|
1070
|
+
def series_VaR_normal_modified(ticker,shares,datelist, \
|
1071
|
+
future_days=1,alpha=0.99,pastyears=1,printout=True):
|
1072
|
+
"""
|
1073
|
+
功能:一步计算多个日期的VaR,修正正态分布法
|
1074
|
+
输入参数:股票代码,持有股数,日期列表,未来持有天数,置信度,使用历史数据的年数
|
1075
|
+
注释:当前日期可以为过去的任意一天,历史年数为使用几年的历史数据来分析
|
1076
|
+
输出:VaR信息表(日期,股票代码,VaR金额,VaR比率)
|
1077
|
+
"""
|
1078
|
+
# datelist是datetime类型日期的列表
|
1079
|
+
import pandas as pd
|
1080
|
+
result=pd.DataFrame(columns=['date','ticker','VaR','ratio'])
|
1081
|
+
for d in datelist:
|
1082
|
+
VaR,ratio=stock_VaR_normal_modified(ticker,shares,d, \
|
1083
|
+
future_days,alpha,pastyears,printout=False)
|
1084
|
+
s = pd.Series({'date':d,'ticker':ticker,'VaR':VaR,'ratio':ratio})
|
1085
|
+
try:
|
1086
|
+
result=result.append(s,ignore_index=True)
|
1087
|
+
except:
|
1088
|
+
result=result._append(s,ignore_index=True)
|
1089
|
+
|
1090
|
+
result2=result.set_index(['date'])
|
1091
|
+
# result2是dateframe类型
|
1092
|
+
|
1093
|
+
if printout == False: return result2
|
1094
|
+
|
1095
|
+
#打印
|
1096
|
+
result3=result2.copy()
|
1097
|
+
result3['VaR金额']=round(result3['VaR'],2)
|
1098
|
+
result3['VaR比例%']=round(result3['ratio']*100,2)
|
1099
|
+
result3.drop(columns=['ticker','VaR','ratio'],inplace=True)
|
1100
|
+
|
1101
|
+
text1="= "+ticker_name(ticker)+": VaR比例,持有"+str(future_days)+"天 ="
|
1102
|
+
print(text1)
|
1103
|
+
print(result3)
|
1104
|
+
import datetime as dt; today=dt.date.today()
|
1105
|
+
print("数据来源:新浪/stooq,"+str(today))
|
1106
|
+
|
1107
|
+
#绘图
|
1108
|
+
#VaR金额绘图
|
1109
|
+
plt.plot(result3['VaR金额'],c='r',lw=2)
|
1110
|
+
title1=ticker_name(ticker)+": VaR金额的变化,持有"+str(future_days)+"天"
|
1111
|
+
plt.title(title1)
|
1112
|
+
plt.ylabel('VaR金额')
|
1113
|
+
plt.xticks(rotation=30)
|
1114
|
+
|
1115
|
+
footnote="数据来源:新浪/stooq,"+str(today)
|
1116
|
+
plt.xlabel(footnote)
|
1117
|
+
plt.show()
|
1118
|
+
|
1119
|
+
plt.plot(result3['VaR比例%'],c='r',lw=2)
|
1120
|
+
title2=ticker_name(ticker)+": VaR比例的变化,持有"+str(future_days)+"天"
|
1121
|
+
plt.title(title2)
|
1122
|
+
plt.ylabel('VaR比例%')
|
1123
|
+
plt.xticks(rotation=30)
|
1124
|
+
plt.xlabel(footnote)
|
1125
|
+
plt.show()
|
1126
|
+
|
1127
|
+
return result2
|
1128
|
+
|
1129
|
+
|
1130
|
+
#==============================================================================
|
1131
|
+
def compare_VaR_normal_modified(tickerlist,shares,datelist, \
|
1132
|
+
future_days=1,alpha=0.99,pastyears=1):
|
1133
|
+
"""
|
1134
|
+
功能:比较多个日期持有一定天数股票资产的VaR高低,修正正态法
|
1135
|
+
输入参数:股票列表,持有股数,日期列表(日期型列表),未来持有时间(天数),置信度,使用历史数据的年数
|
1136
|
+
输出:无
|
1137
|
+
显示:折线图,各个资产的VaR金额和比率对比
|
1138
|
+
"""
|
1139
|
+
|
1140
|
+
import os, sys
|
1141
|
+
class HiddenPrints:
|
1142
|
+
def __enter__(self):
|
1143
|
+
self._original_stdout = sys.stdout
|
1144
|
+
sys.stdout = open(os.devnull, 'w')
|
1145
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
1146
|
+
sys.stdout.close()
|
1147
|
+
sys.stdout = self._original_stdout
|
1148
|
+
|
1149
|
+
print(" The comparison may take time, please wait ...")
|
1150
|
+
markerlist=['.','o','s','*','+','x','1','2']
|
1151
|
+
for t in tickerlist:
|
1152
|
+
pos=tickerlist.index(t)
|
1153
|
+
thismarker=markerlist[pos]
|
1154
|
+
|
1155
|
+
with HiddenPrints():
|
1156
|
+
r=series_VaR_normal_modified(t,shares,datelist, \
|
1157
|
+
future_days,alpha,pastyears,printout=False)
|
1158
|
+
rr=r.copy()
|
1159
|
+
rr['VaR amount']=round(rr['VaR'],2)
|
1160
|
+
rr.drop(columns=['ticker','VaR','ratio'],inplace=True)
|
1161
|
+
plt.plot(rr['VaR amount'],label=ticker_name(t),lw=2,marker=thismarker)
|
1162
|
+
|
1163
|
+
title1="比较VaR金额,持有"+str(future_days)+"天"
|
1164
|
+
plt.title(title1)
|
1165
|
+
plt.ylabel('VaR金额')
|
1166
|
+
plt.xticks(rotation=30)
|
1167
|
+
plt.legend(loc='best')
|
1168
|
+
|
1169
|
+
notes="【注】VaR计算模型:修正正态法"
|
1170
|
+
import datetime as dt; today=dt.date.today()
|
1171
|
+
source="数据来源:新浪/stooq,"+str(today)
|
1172
|
+
footnote=notes+'\n'+source
|
1173
|
+
plt.xlabel(footnote)
|
1174
|
+
plt.show()
|
1175
|
+
|
1176
|
+
for t in tickerlist:
|
1177
|
+
pos=tickerlist.index(t)
|
1178
|
+
thismarker=markerlist[pos]
|
1179
|
+
|
1180
|
+
with HiddenPrints():
|
1181
|
+
r=series_VaR_normal_modified(t,shares,datelist, \
|
1182
|
+
future_days,alpha,pastyears,printout=False)
|
1183
|
+
rr=r.copy()
|
1184
|
+
rr['VaR ratio %']=round(rr['ratio']*100,2)
|
1185
|
+
rr.drop(columns=['ticker','VaR','ratio'],inplace=True)
|
1186
|
+
plt.plot(rr['VaR ratio %'],label=ticker_name(t),lw=2,marker=thismarker)
|
1187
|
+
|
1188
|
+
title2="比较VaR比例,持有"+str(future_days)+"天"
|
1189
|
+
plt.title(title2)
|
1190
|
+
plt.ylabel('VaR比例%')
|
1191
|
+
plt.xticks(rotation=45)
|
1192
|
+
plt.legend(loc='best')
|
1193
|
+
plt.xlabel(footnote)
|
1194
|
+
plt.show()
|
1195
|
+
|
1196
|
+
return
|
1197
|
+
|
1198
|
+
if __name__ == '__main__':
|
1199
|
+
tickerlist=['BABA','PDD','JD']
|
1200
|
+
datelist=['2019-01-01','2019-02-01','2019-03-01','2019-04-01', \
|
1201
|
+
'2019-05-01','2019-06-01','2019-07-01']
|
1202
|
+
compare_VaR_normal_standard(tickerlist,10000,datelist,1,0.99)
|
1203
|
+
|
1204
|
+
|
1205
|
+
#==============================================================================
|
1206
|
+
def VaR_historical_1d(position,ret_series,alpha=0.99):
|
1207
|
+
"""
|
1208
|
+
功能:计算VaR基本算法,历史模拟法,持有1天
|
1209
|
+
输入参数:持有头寸金额,历史日收益率序列,置信度
|
1210
|
+
输出:持有一天的VaR(金额)
|
1211
|
+
"""
|
1212
|
+
#去掉空值
|
1213
|
+
r=ret_series[~ret_series.isnull()]
|
1214
|
+
|
1215
|
+
import numpy as np
|
1216
|
+
n=len(r)
|
1217
|
+
t=int(n*(1-alpha))
|
1218
|
+
SR=np.sort(r)
|
1219
|
+
if t>=1:
|
1220
|
+
A=SR[t-1] #SR的第一个元素的序号是0
|
1221
|
+
else:
|
1222
|
+
A=SR[0]
|
1223
|
+
VaR_1d=position*A
|
1224
|
+
|
1225
|
+
return -abs(VaR_1d)
|
1226
|
+
|
1227
|
+
|
1228
|
+
#==============================================================================
|
1229
|
+
def stock_VaR_historical_1d(ticker,shares,today,alpha=0.99, \
|
1230
|
+
pastyears=1,printout=True):
|
1231
|
+
"""
|
1232
|
+
功能:计算持有股票的VaR,历史模拟法,持有1天
|
1233
|
+
输入参数:股票代码,持有股数,当前日期,置信度,使用历史数据的年数
|
1234
|
+
输出:持有一天的VaR(金额和比率)
|
1235
|
+
"""
|
1236
|
+
start=get_start_date(today,pastyears)
|
1237
|
+
p=get_stock_quotes(ticker,start,today)
|
1238
|
+
if (p is None) or (len(p)==0):
|
1239
|
+
print("#Error(stock_VaR_historical_1d): no obs retrieved.")
|
1240
|
+
return None,None
|
1241
|
+
|
1242
|
+
r=get_ret_series(p)
|
1243
|
+
p_end=get_end_price(p)
|
1244
|
+
position=shares*p_end
|
1245
|
+
VaR_1d=VaR_historical_1d(position,r,alpha)
|
1246
|
+
#最高为全损
|
1247
|
+
if abs(VaR_1d)>position: VaR_1d=-position
|
1248
|
+
VaR_ratio=abs(VaR_1d/position)
|
1249
|
+
|
1250
|
+
if printout == True:
|
1251
|
+
"""
|
1252
|
+
print("\n=== 计算在险价值VaR:历史模拟方法 ===")
|
1253
|
+
print("持有股票 :",ticker_name(ticker))
|
1254
|
+
print("持有股数 :",format(shares,','))
|
1255
|
+
print("持有日期 :",today)
|
1256
|
+
future_days=1
|
1257
|
+
print("预计持有日期:",future_days)
|
1258
|
+
print("置信度 : ",alpha*100,'%',sep='')
|
1259
|
+
print("VaR金额 :",format(round(VaR_1d,2),','))
|
1260
|
+
print("VaR比例 : ",round(VaR_ratio*100,2),'%',sep='')
|
1261
|
+
|
1262
|
+
import datetime as dt; today=dt.date.today()
|
1263
|
+
print("*数据来源:新浪/stooq,"+str(today))
|
1264
|
+
"""
|
1265
|
+
titletxt="在险价值:历史模拟方法"
|
1266
|
+
import datetime as dt; todaydt=dt.date.today()
|
1267
|
+
footnote="数据来源:新浪/stooq,"+str(todaydt)
|
1268
|
+
|
1269
|
+
future_days=1
|
1270
|
+
data_dict={'持有股票:':ticker_name(ticker), \
|
1271
|
+
'持有股数:':format(shares,','), \
|
1272
|
+
'持有日期:':today, \
|
1273
|
+
'预计持有天数:':future_days, \
|
1274
|
+
'置信度:':str(alpha*100)+'%', \
|
1275
|
+
'在险价值VaR金额:':format(round(VaR_1d,2),','), \
|
1276
|
+
'在险价值VaR比例:':str(round(VaR_ratio*100,2))+'%'}
|
1277
|
+
|
1278
|
+
print2CSS(data_dict,titletxt=titletxt,footnote=footnote)
|
1279
|
+
|
1280
|
+
return VaR_1d,VaR_ratio
|
1281
|
+
|
1282
|
+
if __name__ == '__main__':
|
1283
|
+
ticker='BABA'
|
1284
|
+
shares=1000
|
1285
|
+
today='2020-7-1'
|
1286
|
+
alpha=0.99
|
1287
|
+
pastyears=1
|
1288
|
+
printout=True
|
1289
|
+
VaR,Ratio=stock_VaR_historical_1d(ticker,shares,today)
|
1290
|
+
#==============================================================================
|
1291
|
+
def VaR_historical_grouping(position,ret_series,future_days=1,alpha=0.99):
|
1292
|
+
"""
|
1293
|
+
功能:计算VaR基本算法,历史模拟法,持有多天(基本分组法,无组内收益率波动调整)
|
1294
|
+
输入参数:当前头寸金额,历史日收益率序列,未来持有天数,置信度
|
1295
|
+
注意:分组法需要更长时间的历史数据,不然准确度不高!
|
1296
|
+
输出:持有多天的VaR(金额)
|
1297
|
+
"""
|
1298
|
+
#去掉空值
|
1299
|
+
r=ret_series[~ret_series.isnull()]
|
1300
|
+
|
1301
|
+
import pandas as pd
|
1302
|
+
#将收益率序列转变为df
|
1303
|
+
r1=pd.DataFrame(r)
|
1304
|
+
|
1305
|
+
#定义组内累计收益率计算方法
|
1306
|
+
cumret=lambda x:(x+1.0).prod()-1.0
|
1307
|
+
#使用滚动窗口分组,计算各组收益率gret
|
1308
|
+
r1['gret']=r1.rolling(future_days).apply(cumret)
|
1309
|
+
#将各组收益率转换为组收益率序列
|
1310
|
+
r2=pd.Series(r1['gret'])
|
1311
|
+
|
1312
|
+
#利用单日历史模拟法计算VaR
|
1313
|
+
VaR_days=VaR_historical_1d(position,r2,alpha)
|
1314
|
+
|
1315
|
+
return -abs(VaR_days)
|
1316
|
+
|
1317
|
+
if __name__ == '__main__':
|
1318
|
+
ticker='BABA'
|
1319
|
+
start='2020-6-1'
|
1320
|
+
today='2020-7-1'
|
1321
|
+
p=get_stock_quotes(ticker,start,today)
|
1322
|
+
ret_series=get_ret_series(p)
|
1323
|
+
future_days=2
|
1324
|
+
|
1325
|
+
shares=1000
|
1326
|
+
alpha=0.99
|
1327
|
+
pastyears=1
|
1328
|
+
printout=True
|
1329
|
+
position=1
|
1330
|
+
|
1331
|
+
VaR=VaR_historical_grouping(position,ret_series,future_days)
|
1332
|
+
|
1333
|
+
|
1334
|
+
#==============================================================================
|
1335
|
+
def VaR_historical(position,ret_series,future_days=1,alpha=0.99):
|
1336
|
+
"""
|
1337
|
+
功能:同名函数,为了后面函数名称合成方便
|
1338
|
+
"""
|
1339
|
+
VaR=VaR_historical_grouping(position,ret_series,future_days,alpha)
|
1340
|
+
return VaR
|
1341
|
+
|
1342
|
+
#==============================================================================
|
1343
|
+
def stock_VaR_historical_grouping(ticker,shares,today, \
|
1344
|
+
future_days=1,alpha=0.99,pastyears=1,printout=True):
|
1345
|
+
"""
|
1346
|
+
功能:计算持有股票的VaR,历史模拟法,持有多天(基本分组法,无组内收益率波动调整)
|
1347
|
+
输入参数:股票代码,持有股数,当前日期,未来持有天数,置信度,使用历史数据的年数
|
1348
|
+
注意:分组法需要更长时间的历史数据,不然准确度不高!
|
1349
|
+
建议:pastyears的数值大于等于future_days的数值
|
1350
|
+
输出:基本分组法,持有多天的VaR(金额和比率)
|
1351
|
+
"""
|
1352
|
+
start=get_start_date(today,pastyears)
|
1353
|
+
p=get_stock_quotes(ticker,start,today)
|
1354
|
+
if (p is None) or (len(p)==0):
|
1355
|
+
print("#Error(stock_VaR_historical_grouping): no obs retrieved.")
|
1356
|
+
return None,None
|
1357
|
+
p_end=get_end_price(p); position=shares*p_end
|
1358
|
+
|
1359
|
+
#产生历史收益率序列
|
1360
|
+
r=get_ret_series(p)
|
1361
|
+
#分组计算
|
1362
|
+
VaR_days=VaR_historical_grouping(position,r,future_days,alpha)
|
1363
|
+
#最大为全损
|
1364
|
+
if abs(VaR_days) > position: VaR_days=-position
|
1365
|
+
|
1366
|
+
VaR_ratio=abs(VaR_days/position)
|
1367
|
+
|
1368
|
+
if future_days == 1:
|
1369
|
+
modeltxt="历史排序模拟法"
|
1370
|
+
else:
|
1371
|
+
modeltxt="分组历史模拟法"
|
1372
|
+
|
1373
|
+
if printout == True:
|
1374
|
+
"""
|
1375
|
+
print("\n=== 在险价值VaR:"+modeltxt+" ===")
|
1376
|
+
print("持有股票 :",ticker_name(ticker))
|
1377
|
+
print("持有股数 :",format(shares,','))
|
1378
|
+
print("持有日期 :",today)
|
1379
|
+
print("预计持有天数:",future_days)
|
1380
|
+
print("置信度 :",alpha*100,'%',sep='')
|
1381
|
+
print("VaR金额 :",format(round(VaR_days,2),','))
|
1382
|
+
print("VaR比例 :",round(VaR_ratio*100,2),'%',sep='')
|
1383
|
+
|
1384
|
+
import datetime as dt; today=dt.date.today()
|
1385
|
+
print("*数据来源:新浪/stooq,"+str(today))
|
1386
|
+
"""
|
1387
|
+
|
1388
|
+
titletxt="在险价值:"+modeltxt
|
1389
|
+
import datetime as dt; todaydt=dt.date.today()
|
1390
|
+
footnote="数据来源:新浪/stooq,"+str(todaydt)
|
1391
|
+
|
1392
|
+
data_dict={'持有股票:':ticker_name(ticker), \
|
1393
|
+
'持有股数:':format(shares,','), \
|
1394
|
+
'持有日期:':today, \
|
1395
|
+
'预计持有天数:':future_days, \
|
1396
|
+
'置信度:':str(alpha*100)+'%', \
|
1397
|
+
'在险价值VaR金额:':format(round(VaR_days,2),','), \
|
1398
|
+
'在险价值VaR比例:':str(round(VaR_ratio*100,2))+'%'}
|
1399
|
+
|
1400
|
+
print2CSS(data_dict,titletxt=titletxt,footnote=footnote)
|
1401
|
+
|
1402
|
+
|
1403
|
+
return -abs(VaR_days),VaR_ratio
|
1404
|
+
|
1405
|
+
#==============================================================================
|
1406
|
+
def VaR_montecarlo(position,ret_series, \
|
1407
|
+
future_days=1,alpha=0.99,random=10000,mctype='random'):
|
1408
|
+
"""
|
1409
|
+
功能:计算VaR基本算法,蒙特卡洛模拟法,持有多日
|
1410
|
+
输入参数:当前头寸金额,历史日收益率序列,未来持有天数,置信度,重复模拟次数
|
1411
|
+
注:重复模拟次数越多,准确率就越高,但耗时也越多
|
1412
|
+
输出:持有多天的VaR(金额)
|
1413
|
+
"""
|
1414
|
+
#去掉空值
|
1415
|
+
r=ret_series[~ret_series.isnull()]
|
1416
|
+
|
1417
|
+
import pandas as pd
|
1418
|
+
#蒙特卡洛模拟类型:随机数or超采样
|
1419
|
+
if mctype=='random': #随机数产生新的序列
|
1420
|
+
import numpy as np
|
1421
|
+
#取得历史日收益率的均值和标准差
|
1422
|
+
miu=np.mean(r)
|
1423
|
+
sigma=np.std(r)
|
1424
|
+
#生成随机数序列
|
1425
|
+
np.random.seed(12345)
|
1426
|
+
#按照历史日收益率的均值和标准差重复模拟一定次数,生成新的日收益率序列
|
1427
|
+
RR=pd.Series(np.random.normal(miu,sigma,random))
|
1428
|
+
else: #超采样产生新的序列
|
1429
|
+
#将收益率序列转变为df
|
1430
|
+
r1=pd.DataFrame(r)
|
1431
|
+
r2=r1.sample(n=random,replace=True)
|
1432
|
+
r2.sort_index(inplace=True)
|
1433
|
+
RR=pd.Series(r2.iloc[:,0])
|
1434
|
+
|
1435
|
+
#基于新的日收益率序列,使用标准正态法计算VaR
|
1436
|
+
VaR_days=VaR_historical_grouping(position,RR,future_days,alpha)
|
1437
|
+
|
1438
|
+
return -abs(VaR_days)
|
1439
|
+
|
1440
|
+
|
1441
|
+
#==============================================================================
|
1442
|
+
def stock_VaR_montecarlo(ticker,shares,today,future_days=1,alpha=0.99, \
|
1443
|
+
pastyears=1,random=10000,printout=True,mctype='random'):
|
1444
|
+
"""
|
1445
|
+
功能:计算持有股票的VaR,蒙特卡洛模拟法,持有多日
|
1446
|
+
输入参数:股票代码,持有股数,当前日期,未来持有天数,置信度,使用历史数据的年数,
|
1447
|
+
重复模拟次数
|
1448
|
+
输出:持有股票多天的VaR(金额和比率)
|
1449
|
+
"""
|
1450
|
+
start=get_start_date(today,pastyears)
|
1451
|
+
p=get_stock_quotes(ticker,start,today)
|
1452
|
+
if (p is None) or (len(p)==0):
|
1453
|
+
print("#Error(stock_VaR_montecarlo): no obs retrieved.")
|
1454
|
+
return None,None
|
1455
|
+
r=get_ret_series(p)
|
1456
|
+
p_end=get_end_price(p)
|
1457
|
+
position=shares*p_end
|
1458
|
+
|
1459
|
+
VaR_days=VaR_montecarlo(position,r,future_days,alpha,random,mctype=mctype)
|
1460
|
+
ratio=abs(VaR_days/position)
|
1461
|
+
#最大为全损
|
1462
|
+
if abs(VaR_days) > position: VaR_days=-position
|
1463
|
+
|
1464
|
+
if printout == True:
|
1465
|
+
"""
|
1466
|
+
print("\n=== 在险价值VaR:蒙特卡洛模拟法 ===")
|
1467
|
+
print("持有日期 :",today)
|
1468
|
+
print("持有股票 :",ticker_name(ticker))
|
1469
|
+
print("持有股数 :",format(shares,','))
|
1470
|
+
print("持有头寸 :",format(round(position,2),','))
|
1471
|
+
print("预计持有天数:",future_days)
|
1472
|
+
print("置信度 : ",alpha*100,'%',sep='')
|
1473
|
+
print("序列生成方法:",mctype)
|
1474
|
+
print("VaR金额 :",format(round(VaR_days,2),','))
|
1475
|
+
#四舍五不入
|
1476
|
+
print("VaR比例 : ",(int(ratio*10000))/100,'%',sep='')
|
1477
|
+
|
1478
|
+
import datetime as dt; today=dt.date.today()
|
1479
|
+
print("*数据来源:新浪/stooq,"+str(today))
|
1480
|
+
"""
|
1481
|
+
titletxt="在险价值:蒙特卡洛模拟法"
|
1482
|
+
import datetime as dt; todaydt=dt.date.today()
|
1483
|
+
footnote="数据来源:新浪/stooq,"+str(todaydt)
|
1484
|
+
|
1485
|
+
data_dict={'持有股票:':ticker_name(ticker), \
|
1486
|
+
'持有股数:':format(shares,','), \
|
1487
|
+
'持有头寸:':format(round(position,2),','), \
|
1488
|
+
'持有日期:':today, \
|
1489
|
+
'预计持有天数:':future_days, \
|
1490
|
+
'序列生成方法:':mctype, \
|
1491
|
+
'置信度:':str(alpha*100)+'%', \
|
1492
|
+
'在险价值VaR金额:':format(round(VaR_days,2),','), \
|
1493
|
+
'在险价值VaR比例:':str(round(ratio*100,2))+'%'}
|
1494
|
+
|
1495
|
+
print2CSS(data_dict,titletxt=titletxt,footnote=footnote)
|
1496
|
+
|
1497
|
+
return VaR_days,ratio
|
1498
|
+
|
1499
|
+
|
1500
|
+
#==============================================================================
|
1501
|
+
def calc_VaR_tlcp(ticker,today,alpha=0.99, \
|
1502
|
+
pastyears=1,model="normal_standard",printout=True):
|
1503
|
+
"""
|
1504
|
+
功能:计算一只股票的VaR全损临界点,即VaR达到持有全部头寸的最小天数
|
1505
|
+
输入:股票代码,持有日期,置信度,使用历史数据的年数,
|
1506
|
+
使用的模型,是否打印结果
|
1507
|
+
输出:股票的VaR全损临界点天数,与使用的模型有关
|
1508
|
+
显示:无
|
1509
|
+
"""
|
1510
|
+
print("\n...Calculatiing tlcp of",ticker,"\b, please wait...")
|
1511
|
+
|
1512
|
+
#检查model类型
|
1513
|
+
modellist=['normal_standard','normal_modified','montecarlo']
|
1514
|
+
model=model.lower()
|
1515
|
+
if model not in modellist:
|
1516
|
+
print("#Error(calc_VaR_tlcp): Unsupported type of model")
|
1517
|
+
print("Information:",model)
|
1518
|
+
print("Supported models:",modellist)
|
1519
|
+
return
|
1520
|
+
|
1521
|
+
#抓取股价,计算历史收益率序列和当前头寸
|
1522
|
+
start=get_start_date(today,pastyears)
|
1523
|
+
p=get_stock_quotes(ticker,start,today)
|
1524
|
+
if (p is None) or (len(p)==0):
|
1525
|
+
print("#Error(calc_VaR_tlcp): no obs retrieved.")
|
1526
|
+
return None
|
1527
|
+
r=get_ret_series(p)
|
1528
|
+
p_end=get_end_price(p)
|
1529
|
+
shares=1
|
1530
|
+
position=shares*p_end
|
1531
|
+
|
1532
|
+
#第1轮搜索,步长1000天
|
1533
|
+
func="VaR_"+model
|
1534
|
+
import numpy as np
|
1535
|
+
stop1=0; max1=9000
|
1536
|
+
for d in np.arange(0,max1+1000,1000):
|
1537
|
+
VaR=eval(func)(position,r,d,alpha)
|
1538
|
+
ratio=abs(VaR)/position*100
|
1539
|
+
if ratio >= 100.0:
|
1540
|
+
stop1=d
|
1541
|
+
break
|
1542
|
+
if stop1==0:
|
1543
|
+
if printout == True:
|
1544
|
+
"""
|
1545
|
+
print("\n=== VaR的全损临界点TLCP ===")
|
1546
|
+
print("持有股票 :",ticker_name(ticker))
|
1547
|
+
print("持有日期 :",today)
|
1548
|
+
print("使用的模型:",model)
|
1549
|
+
print("置信度 : ",alpha*100,'%',sep='')
|
1550
|
+
print("TLCP天数 : >",format(max1,','))
|
1551
|
+
|
1552
|
+
print("*注:实际发生全损的概率极小")
|
1553
|
+
import datetime as dt; today=dt.date.today()
|
1554
|
+
print("数据来源:新浪/stooq,"+str(today))
|
1555
|
+
"""
|
1556
|
+
titletxt="VaR全损临界点"
|
1557
|
+
import datetime as dt; todaydt=dt.date.today()
|
1558
|
+
ft0="*注:实际发生全损的概率极小"
|
1559
|
+
footnote=ft0+'\n'+"数据来源:新浪/stooq,"+str(todaydt)
|
1560
|
+
|
1561
|
+
data_dict={'持有股票:':ticker_name(ticker), \
|
1562
|
+
'持有日期:':today, \
|
1563
|
+
'使用的模型:':model, \
|
1564
|
+
'置信度:':str(alpha*100)+'%', \
|
1565
|
+
'全损临界点TLCP天数 >':format(max1,',')}
|
1566
|
+
|
1567
|
+
print2CSS(data_dict,titletxt=titletxt,footnote=footnote)
|
1568
|
+
|
1569
|
+
return max1
|
1570
|
+
|
1571
|
+
#第2轮搜索,步长100天
|
1572
|
+
stop2=100
|
1573
|
+
for d in np.arange(stop1-1000,stop1+100,100):
|
1574
|
+
VaR=eval(func)(position,r,d,alpha)
|
1575
|
+
ratio=abs(VaR)/position*100
|
1576
|
+
if ratio >= 100.0:
|
1577
|
+
stop2=d
|
1578
|
+
break
|
1579
|
+
|
1580
|
+
#第3轮搜索,步长10天
|
1581
|
+
stop3=10
|
1582
|
+
for d in np.arange(stop2-100,stop2+10,10):
|
1583
|
+
VaR=eval(func)(position,r,d,alpha)
|
1584
|
+
ratio=abs(VaR)/position*100
|
1585
|
+
if ratio >= 100.0:
|
1586
|
+
stop3=d
|
1587
|
+
break
|
1588
|
+
|
1589
|
+
#第4轮搜索,步长1天
|
1590
|
+
stop4=1
|
1591
|
+
for d in np.arange(stop3-10,stop3+1,1):
|
1592
|
+
VaR=eval(func)(position,r,d,alpha)
|
1593
|
+
ratio=abs(VaR)/position*100
|
1594
|
+
if ratio >= 100.0:
|
1595
|
+
stop4=d
|
1596
|
+
break
|
1597
|
+
|
1598
|
+
if printout == True:
|
1599
|
+
"""
|
1600
|
+
print("\n=== VaR的全损临界点TLCP ===")
|
1601
|
+
print("持有股票 :",ticker_name(ticker))
|
1602
|
+
print("持有日期 :",today)
|
1603
|
+
print("使用的模型:",model)
|
1604
|
+
print("置信度 : ",alpha*100,'%',sep='')
|
1605
|
+
print("TLCP天数 : >",format(stop4,','))
|
1606
|
+
"""
|
1607
|
+
|
1608
|
+
titletxt="VaR全损临界点"
|
1609
|
+
import datetime as dt; todaydt=dt.date.today()
|
1610
|
+
ft0="*注:实际发生全损的概率极小"
|
1611
|
+
footnote=ft0+'\n'+"数据来源:新浪/stooq,"+str(todaydt)
|
1612
|
+
|
1613
|
+
data_dict={'持有股票:':ticker_name(ticker), \
|
1614
|
+
'持有日期:':today, \
|
1615
|
+
'使用的模型:':model, \
|
1616
|
+
'置信度:':str(alpha*100)+'%', \
|
1617
|
+
'全损临界点TLCP天数 >':format(stop4,',')}
|
1618
|
+
|
1619
|
+
print2CSS(data_dict,titletxt=titletxt,footnote=footnote)
|
1620
|
+
|
1621
|
+
|
1622
|
+
return stop4
|
1623
|
+
|
1624
|
+
if __name__ == "__main__":
|
1625
|
+
ticker='AAPL'
|
1626
|
+
today='2020-7-20'
|
1627
|
+
alpha=0.99
|
1628
|
+
pastyears=1
|
1629
|
+
model="montecarlo"
|
1630
|
+
printout=True
|
1631
|
+
tlcp=calc_VaR_tlcp('MSFT','2019-8-8')
|
1632
|
+
|
1633
|
+
#==============================================================================
|
1634
|
+
def series_VaR_tlcp(tickerlist,today,alpha=0.99,pastyears=1,model="montecarlo"):
|
1635
|
+
"""
|
1636
|
+
功能:计算股票列表中各个股票的VaR全损临界点(天数)
|
1637
|
+
输出:各个股票的VaR全损临界点(天数)列表
|
1638
|
+
显示:柱状图,从小到大排序
|
1639
|
+
"""
|
1640
|
+
|
1641
|
+
#仅测试用
|
1642
|
+
#tickerlist=['BABA','JD','VIPS','PDD']
|
1643
|
+
#today='2019-8-8'
|
1644
|
+
#alpha=0.99
|
1645
|
+
#pastyears=1
|
1646
|
+
#model="montecarlo"
|
1647
|
+
print("\n*** It may need very long time to calculate, please wait ...")
|
1648
|
+
|
1649
|
+
import pandas as pd
|
1650
|
+
tlcpdf=pd.DataFrame(columns=['Ticker','Holding date','Alpha','Model','TLCP'])
|
1651
|
+
for t in tickerlist:
|
1652
|
+
tlcp=calc_VaR_tlcp(t,today,alpha,pastyears,model,printout=False)
|
1653
|
+
row=pd.Series({'Ticker':t,'Holding date':today,'Alpha':alpha, \
|
1654
|
+
'Model':model,'TLCP':tlcp})
|
1655
|
+
try:
|
1656
|
+
tlcpdf=tlcpdf.append(row,ignore_index=True)
|
1657
|
+
except:
|
1658
|
+
tlcpdf=tlcpdf._append(row,ignore_index=True)
|
1659
|
+
|
1660
|
+
#print("TLCP for stock ",t,": ",tlcp," days",sep='')
|
1661
|
+
#tlcpdf=tlcpdf.set_index('Ticker')
|
1662
|
+
tlcpdf=tlcpdf.sort_values(axis=0,ascending=True,by = 'TLCP')
|
1663
|
+
#tlcpdf.reset_index(drop=True)
|
1664
|
+
tlcpdf.set_index('Ticker',drop=True,inplace=True)
|
1665
|
+
"""
|
1666
|
+
#ax=plt.figure(figsize=(12.8,6.4))
|
1667
|
+
tlcpdf.plot.barh(x='Ticker',y='TLCP',color='r',grid=True)
|
1668
|
+
plt.ylabel('股票')
|
1669
|
+
titlel1="VaR全损临界点(TLCP)"
|
1670
|
+
titlel2="\n"+str(today)+", 使用模型"+model+", 置信度"+str(alpha*100)+"%"
|
1671
|
+
titletxt=titlel1+titlel2
|
1672
|
+
plt.title(titletxt)
|
1673
|
+
|
1674
|
+
import datetime as dt; today=dt.date.today()
|
1675
|
+
footnote='TLCP天数-->'+"\n数据来源:新浪/stooq,"+str(today)
|
1676
|
+
plt.xlabel(footnote)
|
1677
|
+
plt.show()
|
1678
|
+
"""
|
1679
|
+
titlel1="VaR全损临界点(TLCP)"
|
1680
|
+
titlel2="\n"+str(today)+", 使用模型"+model+", 置信度"+str(alpha*100)+"%"
|
1681
|
+
titletxt=titlel1+titlel2
|
1682
|
+
import datetime as dt; today=dt.date.today()
|
1683
|
+
footnote='TLCP天数-->'+"\n数据来源:新浪/stooq,"+str(today)
|
1684
|
+
import siat.grafix as g
|
1685
|
+
g.plot_barh(tlcpdf,'TLCP',titletxt,footnote)
|
1686
|
+
|
1687
|
+
"""
|
1688
|
+
print("===== VaR全损临界点(TLCP, 天数) =====")
|
1689
|
+
print("持有日期 :",today)
|
1690
|
+
print("使用的模型:",model)
|
1691
|
+
print("置信度 :",alpha*100,'%',sep='')
|
1692
|
+
print(tlcpdf)
|
1693
|
+
print("数据来源:新浪/stooq,"+str(today))
|
1694
|
+
"""
|
1695
|
+
|
1696
|
+
titletxt="VaR全损临界点(TLCP, 天数)"
|
1697
|
+
import datetime as dt; todaydt=dt.date.today()
|
1698
|
+
footnote="数据来源:新浪/stooq,"+str(todaydt)
|
1699
|
+
|
1700
|
+
data_dict={'持有日期:':today, \
|
1701
|
+
'使用的计算模型:':model, \
|
1702
|
+
'置信度:':str(alpha*100)+'%'}
|
1703
|
+
|
1704
|
+
print2CSS(data_dict,titletxt='',footnote='')
|
1705
|
+
df_display_CSS(tlcpdf,titletxt=titletxt,footnote=footnote,facecolor='papayawhip',decimals=2, \
|
1706
|
+
hide_columns=False,
|
1707
|
+
first_col_align='left',second_col_align='right', \
|
1708
|
+
last_col_align='right',other_col_align='right', \
|
1709
|
+
titile_font_size='14px',heading_font_size='14px', \
|
1710
|
+
data_font_size='14px',footnote_font_size='12px')
|
1711
|
+
|
1712
|
+
return tlcpdf
|
1713
|
+
|
1714
|
+
|
1715
|
+
#==============================================================================
|
1716
|
+
def plot_VaR_days_changes(ticker,shares,today,dayslist,alpha=0.99, \
|
1717
|
+
pastyears=1,model="montecarlo", \
|
1718
|
+
printout=True, markertype='.'):
|
1719
|
+
"""
|
1720
|
+
功能:计算持有股票的VaR,持有多个日期
|
1721
|
+
输入参数:股票代码,持有股数,当前日期,未来持有天数列表,置信度,
|
1722
|
+
使用历史数据的年数,算法,默认为蒙特卡洛模拟法
|
1723
|
+
输出:持有股票多个日期的VaR(金额和比率),数据框格式
|
1724
|
+
"""
|
1725
|
+
|
1726
|
+
#仅测试用
|
1727
|
+
#ticker='AAPL'
|
1728
|
+
#shares=1000
|
1729
|
+
#today='2019-8-8'
|
1730
|
+
#dayslist=[1,5,10,15,30,90,180]
|
1731
|
+
#alpha=0.99
|
1732
|
+
#pastyears=1
|
1733
|
+
#model="montecarlo"
|
1734
|
+
#printout=True
|
1735
|
+
|
1736
|
+
print("\n... Calculating VaR may take great time, please wait...")
|
1737
|
+
#抓取股价,计算历史收益率序列和当前头寸
|
1738
|
+
start=get_start_date(today,pastyears)
|
1739
|
+
p=get_stock_quotes(ticker,start,today)
|
1740
|
+
if (p is None) or (len(p)==0): return None
|
1741
|
+
r=get_ret_series(p)
|
1742
|
+
p_end=get_end_price(p)
|
1743
|
+
position=shares*p_end
|
1744
|
+
|
1745
|
+
#计算各个持有天数的VaR
|
1746
|
+
import pandas as pd
|
1747
|
+
vardf=pd.DataFrame(columns=['ticker','holding date','holding days','VaR','ratio%'])
|
1748
|
+
for d in dayslist:
|
1749
|
+
func="VaR_"+model
|
1750
|
+
#print(ticker,shares,today,d,alpha)
|
1751
|
+
VaR=round((eval(func)(position,r,d,alpha)),0)
|
1752
|
+
ratio=round((abs(VaR)/position*100),2)
|
1753
|
+
if ratio >= 100.0: ratio=100.0
|
1754
|
+
s = pd.Series({'ticker':ticker,'holding date':today,'holding day(s)':d, \
|
1755
|
+
'VaR':VaR,'ratio%':ratio})
|
1756
|
+
try:
|
1757
|
+
vardf=vardf.append(s,ignore_index=True)
|
1758
|
+
except:
|
1759
|
+
vardf=vardf._append(s,ignore_index=True)
|
1760
|
+
|
1761
|
+
vardf=vardf.set_index(['holding day(s)'])
|
1762
|
+
|
1763
|
+
#绘图
|
1764
|
+
if printout == True:
|
1765
|
+
#plt.figure(figsize=(8,4))
|
1766
|
+
plt.figure(figsize=(12.8,6.4))
|
1767
|
+
#绘制曲线:ratio
|
1768
|
+
plt.plot(vardf['ratio%'],color='red',lw=2,label=ticker_name(ticker),marker=markertype)
|
1769
|
+
#设置标题、图例、坐标轴标签
|
1770
|
+
plt.ylabel('VaR比例%')
|
1771
|
+
plt.legend(loc='best')
|
1772
|
+
titlel1="持有股票期间与VaR比例的变化趋势"
|
1773
|
+
titlel2="\n"+ticker_name(ticker)+", "+today+", 使用模型"+model
|
1774
|
+
titletxt=titlel1+titlel2
|
1775
|
+
plt.title(titletxt)
|
1776
|
+
|
1777
|
+
import datetime as dt; today=dt.date.today()
|
1778
|
+
footnote='持有天数-->'+"\n数据来源:新浪/stooq,"+str(today)
|
1779
|
+
plt.xlabel(footnote)
|
1780
|
+
plt.show()
|
1781
|
+
|
1782
|
+
return vardf
|
1783
|
+
|
1784
|
+
#==============================================================================
|
1785
|
+
def compare_VaR_days_changes(tickerlist,shares,today,dayslist,alpha=0.99, \
|
1786
|
+
pastyears=1,model="montecarlo"):
|
1787
|
+
"""
|
1788
|
+
功能:计算多个股票的持有VaR,分别持有多个日期
|
1789
|
+
输入参数:股票代码列表,持有股数,当前日期,未来持有天数列表,置信度,
|
1790
|
+
使用历史数据的年数,算法,默认为蒙特卡洛模拟法
|
1791
|
+
输出:比较多支股票多个日期的VaR(金额和比率),数据框格式
|
1792
|
+
显示:绘图比较
|
1793
|
+
"""
|
1794
|
+
|
1795
|
+
#仅测试用
|
1796
|
+
#tickerlist=['BABA','JD','PDD']
|
1797
|
+
#shares=1000
|
1798
|
+
#today='2019-8-8'
|
1799
|
+
#dayslist=[1,5,10,15,30,60,90,120,150,180,210,240,270,300,330,365]
|
1800
|
+
#alpha=0.99
|
1801
|
+
#pastyears=1
|
1802
|
+
#model="montecarlo"
|
1803
|
+
|
1804
|
+
#检查model类型
|
1805
|
+
modellist=['normal_standard','normal_modified','historical','montecarlo']
|
1806
|
+
model=model.lower()
|
1807
|
+
if model not in modellist:
|
1808
|
+
print("#Error(compare_VaR_days_changes): Unsupported type of model")
|
1809
|
+
print("Information:",model)
|
1810
|
+
print("Supported models:",modellist)
|
1811
|
+
return
|
1812
|
+
|
1813
|
+
#依次绘图tickerlist中的各个股票
|
1814
|
+
#plt.figure(figsize=(8,4))
|
1815
|
+
plt.figure(figsize=(12.8,6.4))
|
1816
|
+
|
1817
|
+
markerlist=['.','o','s','*','+','x','1','2']
|
1818
|
+
for t in tickerlist:
|
1819
|
+
pos=tickerlist.index(t)
|
1820
|
+
thismarker=markerlist[pos]
|
1821
|
+
vardf=plot_VaR_days_changes(t,shares,today,dayslist,alpha, \
|
1822
|
+
pastyears,model,printout=False,markertype=thismarker)
|
1823
|
+
if vardf is None:
|
1824
|
+
print("#Error(compare_VaR_days_changes): No available data")
|
1825
|
+
print("Information:",t,today,pastyears,model)
|
1826
|
+
continue
|
1827
|
+
|
1828
|
+
plt.plot(vardf['ratio%'],lw=2,label=ticker_name(t),marker=thismarker)
|
1829
|
+
|
1830
|
+
#设置标题、图例、坐标轴标签
|
1831
|
+
plt.ylabel('VaR比例%')
|
1832
|
+
plt.legend(loc='best')
|
1833
|
+
titlel1="持有股票期间与VaR比例的变化趋势"
|
1834
|
+
titlel2="\n股票: "+str(tickerlist)+", "+today+", 使用模型"+model
|
1835
|
+
titlel3="\n注: 水平线表示VaR全损"
|
1836
|
+
titletxt=titlel1+titlel2+titlel3
|
1837
|
+
plt.title(titletxt)
|
1838
|
+
|
1839
|
+
import datetime as dt; today=dt.date.today()
|
1840
|
+
footnote='持有天数-->'+"\n数据来源:新浪/stooq,"+str(today)
|
1841
|
+
plt.xlabel(footnote)
|
1842
|
+
plt.show()
|
1843
|
+
|
1844
|
+
return
|
1845
|
+
|
1846
|
+
if __name__ =="__main__":
|
1847
|
+
dayslist=[1,5,15,30,60,90,180,365]
|
1848
|
+
compare_VaR_days_changes('AAPL','JD',1000,'2019-8-8',dayslist)
|
1849
|
+
|
1850
|
+
#==============================================================================
|
1851
|
+
def get_VaR_allmodels(ticker,shares,today, \
|
1852
|
+
future_days=1,alpha=0.99,pastyears=1):
|
1853
|
+
"""
|
1854
|
+
功能:一次性计算各种模型方法的VaR
|
1855
|
+
输入参数:股票代码,持有股数,当前日期,未来持有天数,置信度,使用历史数据的年数
|
1856
|
+
股票代码:美股代码格式
|
1857
|
+
当前日期:datetime类型
|
1858
|
+
输出参数:各种模型的VaR金额和比率
|
1859
|
+
模型:标准正态法,修正正态法,历史模拟基本分组法,蒙特卡洛模拟法
|
1860
|
+
"""
|
1861
|
+
#计算最低年数以保证历史模拟法至少拥有30个分组来确保结果的有效性
|
1862
|
+
py=int(future_days*30/252)+1
|
1863
|
+
if py > pastyears:
|
1864
|
+
print("Warning: Historical samples too few, historical simulation may not work properly")
|
1865
|
+
|
1866
|
+
#抓取开始日期
|
1867
|
+
start=get_start_date(today,pastyears)
|
1868
|
+
#抓取股价序列
|
1869
|
+
quotes=get_stock_quotes(ticker,start,today)
|
1870
|
+
if (quotes is None) or (len(quotes)==0):
|
1871
|
+
print("#Error(get_VaR_allmodels): no obs retrieved.")
|
1872
|
+
return None,None
|
1873
|
+
#计算当前头寸
|
1874
|
+
position=shares*get_end_price(quotes)
|
1875
|
+
#计算收益率序列
|
1876
|
+
rets=get_ret_series(quotes)
|
1877
|
+
|
1878
|
+
#模型1:标准正太法。加0.5取整相当于四舍五入
|
1879
|
+
VaR1=int(VaR_normal_standard(position,rets,future_days,alpha)+0.5)
|
1880
|
+
ratio1=abs(round(VaR1/position,4))
|
1881
|
+
|
1882
|
+
#模型2:修正正太法
|
1883
|
+
VaR2=int(VaR_normal_modified(position,rets,future_days,alpha)+0.5)
|
1884
|
+
ratio2=abs(round(VaR2/position,4))
|
1885
|
+
|
1886
|
+
#模型3:历史模拟法-分组法
|
1887
|
+
#VaR3=int(VaR_historical_cumulative(position,rets,future_days,alpha)+0.5)
|
1888
|
+
VaR3=int(VaR_historical_grouping(position,rets,future_days,alpha)+0.5)
|
1889
|
+
ratio3=abs(round(VaR3/position,4))
|
1890
|
+
|
1891
|
+
#模型4:蒙特卡洛模拟法(标准正态分布)
|
1892
|
+
VaR4=int(VaR_montecarlo(position,rets,future_days,alpha)+0.5)
|
1893
|
+
ratio4=abs(round(VaR4/position,4))
|
1894
|
+
|
1895
|
+
"""
|
1896
|
+
print("\n==== 不同模型下VaR的计算结果 ====")
|
1897
|
+
print("持有日期 :",today)
|
1898
|
+
print("持有股票 :",ticker_name(ticker))
|
1899
|
+
print("持有股数 :",format(shares,','))
|
1900
|
+
print("持有头寸 :",format(round(position,2),','))
|
1901
|
+
print("预计持有天数:",future_days)
|
1902
|
+
print("置信度 : ",alpha*100,'%',sep='')
|
1903
|
+
|
1904
|
+
print("\n*** 各模型计算的VaR金额:")
|
1905
|
+
print("标准正态模型 :",format(VaR1,','))
|
1906
|
+
print("修正正态模型 :",format(VaR2,','))
|
1907
|
+
print("历史模拟法 :",format(VaR3,','))
|
1908
|
+
print("蒙特卡洛模拟法:",format(VaR4,','))
|
1909
|
+
|
1910
|
+
print("\n*** 各模型计算的VaR比例:")
|
1911
|
+
print("标准正态模型 :",round(ratio1*100,2),'%',sep='')
|
1912
|
+
print("修正正态模型 :",round(ratio2*100,2),'%',sep='')
|
1913
|
+
print("历史模拟法 :",round(ratio3*100,2),'%',sep='')
|
1914
|
+
print("蒙特卡洛模拟法:",round(ratio4*100,2),'%',sep='')
|
1915
|
+
|
1916
|
+
import datetime as dt; today=dt.date.today()
|
1917
|
+
print("\n数据来源:新浪/stooq,"+str(today))
|
1918
|
+
"""
|
1919
|
+
|
1920
|
+
import datetime as dt; todaydt=dt.date.today()
|
1921
|
+
footnote="数据来源:新浪/stooq,"+str(todaydt)
|
1922
|
+
|
1923
|
+
titletxt="持有资产的状况"
|
1924
|
+
data_dict={'持有日期:':today, \
|
1925
|
+
"持有股票:":ticker_name(ticker), \
|
1926
|
+
"持有股数:":format(shares,','), \
|
1927
|
+
"持有股数:":format(shares,','), \
|
1928
|
+
"持有头寸:":format(round(position,2),','), \
|
1929
|
+
"预计持有天数:":future_days, \
|
1930
|
+
'置信度:':str(alpha*100)+'%'}
|
1931
|
+
print2CSS(data_dict,titletxt=titletxt,footnote='')
|
1932
|
+
|
1933
|
+
titletxt="各模型的VaR金额"
|
1934
|
+
data_dict={"标准正态模型:":format(VaR1,','), \
|
1935
|
+
"修正正态模型:":format(VaR2,','), \
|
1936
|
+
"历史模拟模型:":format(VaR3,','), \
|
1937
|
+
"蒙特卡洛模拟:":format(VaR4,',')}
|
1938
|
+
print2CSS(data_dict,titletxt=titletxt,footnote='')
|
1939
|
+
|
1940
|
+
titletxt="各模型的VaR比例"
|
1941
|
+
data_dict={"标准正态模型:":str(round(ratio1*100,2))+'%', \
|
1942
|
+
"修正正态模型:":str(round(ratio2*100,2))+'%', \
|
1943
|
+
"历史模拟模型:":str(round(ratio3*100,2))+'%', \
|
1944
|
+
"蒙特卡洛模拟:":str(round(ratio4*100,2))+'%'}
|
1945
|
+
print2CSS(data_dict,titletxt=titletxt,footnote=footnote)
|
1946
|
+
|
1947
|
+
VaRlist=[VaR1,VaR2,VaR3,VaR4]
|
1948
|
+
ratiolist=[ratio1,ratio2,ratio3,ratio4]
|
1949
|
+
|
1950
|
+
return VaRlist,ratiolist
|
1951
|
+
|
1952
|
+
#==============================================================================
|
1953
|
+
def get_ret_portfolio(tickerlist,shareslist,today,pastyears=1):
|
1954
|
+
"""
|
1955
|
+
功能:计算投资组合的历史收益率序列和当前头寸金额
|
1956
|
+
输入参数:投资组合的股票列表,投资组合的持有股数列表,当前日期,使用历史数据的年数
|
1957
|
+
输出参数:投资组合的历史日收益率序列,当前持有头寸金额
|
1958
|
+
注意:
|
1959
|
+
#tickerlist为字符串列表类型,内容为投资组合内各个股票的代码
|
1960
|
+
#shareslist为整数列表类型,内容为投资组合内各个股票的持股数量,不是金额
|
1961
|
+
#tickerlist和shareslist中的元素个数必须一一对等
|
1962
|
+
"""
|
1963
|
+
import pandas as pd
|
1964
|
+
start=get_start_date(today,pastyears)
|
1965
|
+
prices=pd.DataFrame()
|
1966
|
+
for t in range(len(tickerlist)):
|
1967
|
+
ticker=tickerlist[t]
|
1968
|
+
q=get_stock_quotes(ticker,start,today)
|
1969
|
+
q['date']=q.index.strftime('%Y'+'%m'+'%d')
|
1970
|
+
q['ticker']=ticker
|
1971
|
+
q['price']=q['Close']
|
1972
|
+
q['shares']=shareslist[t]
|
1973
|
+
q['value']=q['price']*q['shares']
|
1974
|
+
try:
|
1975
|
+
prices=prices.append(q)
|
1976
|
+
except:
|
1977
|
+
prices=prices._append(q)
|
1978
|
+
|
1979
|
+
prices2=prices.sort_values('date')
|
1980
|
+
groupKey='date'; groupWay=prices2.groupby(groupKey)
|
1981
|
+
prices3=groupWay['value'].sum()
|
1982
|
+
position=prices3[-1]
|
1983
|
+
ret=prices3.pct_change()
|
1984
|
+
ret=ret.dropna()
|
1985
|
+
return ret,position
|
1986
|
+
|
1987
|
+
#==============================================================================
|
1988
|
+
def get_ret_portfolio2(tickerlist,shareslist,today,pastyears=1):
|
1989
|
+
"""
|
1990
|
+
功能:计算投资组合的历史收益率序列、当前头寸金额和各个成分股的历史日收益率序列
|
1991
|
+
输入参数:投资组合的股票列表,投资组合的持有股数列表,当前日期,使用历史数据的年数
|
1992
|
+
输出:投资组合的历史日收益率序列,当前持有头寸金额,各个成分股的历史日收益率序列
|
1993
|
+
注意:
|
1994
|
+
#tickerlist为字符串列表类型,内容为投资组合内各个股票的代码
|
1995
|
+
#shareslist为整数列表类型,内容为投资组合内各个股票的持股数量,不是金额
|
1996
|
+
#tickerlist和shareslist中的元素个数必须一一对等
|
1997
|
+
"""
|
1998
|
+
import pandas as pd
|
1999
|
+
start=get_start_date(today,pastyears)
|
2000
|
+
prices=pd.DataFrame()
|
2001
|
+
for t in range(len(tickerlist)):
|
2002
|
+
ticker=tickerlist[t]
|
2003
|
+
q=get_stock_quotes(ticker,start,today)
|
2004
|
+
q['date']=q.index.strftime('%Y'+'%m'+'%d')
|
2005
|
+
q['ticker']=ticker
|
2006
|
+
q['price']=q['Close']
|
2007
|
+
q['shares']=shareslist[t]
|
2008
|
+
q['value']=q['price']*q['shares']
|
2009
|
+
q['ret']=q['price'].pct_change()
|
2010
|
+
try:
|
2011
|
+
prices=prices.append(q)
|
2012
|
+
except:
|
2013
|
+
prices=prices._append(q)
|
2014
|
+
|
2015
|
+
#portfolio收益率
|
2016
|
+
prices2=prices.sort_values('date')
|
2017
|
+
groupKey='date'; groupWay=prices2.groupby(groupKey)
|
2018
|
+
prices3=groupWay['value'].sum()
|
2019
|
+
pret=prices3.pct_change()
|
2020
|
+
pret=pret.dropna()
|
2021
|
+
|
2022
|
+
#portfolio当前持有头寸
|
2023
|
+
position=prices3[-1]
|
2024
|
+
|
2025
|
+
#成分股收益率
|
2026
|
+
sret=prices[['date','ticker','price','shares','value','ret']]
|
2027
|
+
sret=sret.dropna()
|
2028
|
+
|
2029
|
+
return pret,position,sret
|
2030
|
+
|
2031
|
+
#==============================================================================
|
2032
|
+
def get_portfolio_info(tickerlist,today,pastyears=1):
|
2033
|
+
"""
|
2034
|
+
功能:返回一组股票的历史价格和历史日收益率
|
2035
|
+
输入参数:股票列表,当前日期,使用历史数据的年数
|
2036
|
+
输出:成分股收益率数据表
|
2037
|
+
"""
|
2038
|
+
import pandas as pd
|
2039
|
+
start=get_start_date(today,pastyears)
|
2040
|
+
prices=pd.DataFrame()
|
2041
|
+
|
2042
|
+
for t in range(len(tickerlist)):
|
2043
|
+
ticker=tickerlist[t]
|
2044
|
+
q=get_stock_quotes(ticker,start,today)
|
2045
|
+
q['date']=q.index.strftime('%Y'+'%m'+'%d')
|
2046
|
+
q['ticker']=ticker
|
2047
|
+
q['price']=q['Close']
|
2048
|
+
q['shares']=0
|
2049
|
+
q['value']=0
|
2050
|
+
q['ret']=q['price'].pct_change()
|
2051
|
+
try:
|
2052
|
+
prices=prices.append(q)
|
2053
|
+
except:
|
2054
|
+
prices=prices._append(q)
|
2055
|
+
|
2056
|
+
#成分股收益率
|
2057
|
+
tickers_info=prices[['date','ticker','price','shares','value','ret']]
|
2058
|
+
return tickers_info
|
2059
|
+
|
2060
|
+
#==============================================================================
|
2061
|
+
def get_portfolio_rets(tickerlist,shareslist,tickers_info,today,pastyears=1):
|
2062
|
+
"""
|
2063
|
+
功能:计算投资组合的历史日收益率序列和当前头寸金额,不重新抓取股价
|
2064
|
+
输入参数:股票列表,持有股数列表,股票信息数据框,当前日期,使用历史数据的年数
|
2065
|
+
输出:投资组合的历史日收益率序列和当前头寸
|
2066
|
+
注意:
|
2067
|
+
#tickerlist为字符串列表类型,内容为投资组合内各个股票的代码
|
2068
|
+
#shareslist为整数列表类型,内容为投资组合内各个股票的持股数量,不是金额
|
2069
|
+
#tickerlist和shareslist中的元素个数必须一一对等
|
2070
|
+
"""
|
2071
|
+
import pandas as pd
|
2072
|
+
start=get_start_date(today,pastyears)
|
2073
|
+
startdate=start.strftime('%Y'+'%m'+'%d')
|
2074
|
+
enddate =today.strftime('%Y'+'%m'+'%d')
|
2075
|
+
ti=pd.DataFrame()
|
2076
|
+
for index, row in tickers_info.iterrows():
|
2077
|
+
if (row['date'] >= startdate) and (row['date'] <= enddate):
|
2078
|
+
ticker=row['ticker']
|
2079
|
+
p=tickerlist.index(ticker)
|
2080
|
+
shares=shareslist[p]
|
2081
|
+
row['shares']=shares
|
2082
|
+
row['value']=row['price']*shares
|
2083
|
+
try:
|
2084
|
+
ti=ti.append(row,ignore_index=True)
|
2085
|
+
except:
|
2086
|
+
ti=ti._append(row,ignore_index=True)
|
2087
|
+
|
2088
|
+
ti1=ti.sort_values('date')
|
2089
|
+
groupKey='date'; groupWay=ti1.groupby(groupKey)
|
2090
|
+
ti2=groupWay['value'].sum()
|
2091
|
+
position=ti2[-1]
|
2092
|
+
pret=ti2.pct_change()
|
2093
|
+
pret=pret.dropna()
|
2094
|
+
|
2095
|
+
return pret,position
|
2096
|
+
|
2097
|
+
#==============================================================================
|
2098
|
+
def get_ret_cumulative(ret_series):
|
2099
|
+
"""
|
2100
|
+
功能:计算累计收益率
|
2101
|
+
输入参数:收益率序列
|
2102
|
+
输出:累积收益率
|
2103
|
+
"""
|
2104
|
+
c=1
|
2105
|
+
for r in ret_series:
|
2106
|
+
c=c*(1+r)
|
2107
|
+
retcum=c-1
|
2108
|
+
return retcum
|
2109
|
+
|
2110
|
+
#==============================================================================
|
2111
|
+
def get_portfolio_ret_risk(tickerlist,shareslist,tickers_info, \
|
2112
|
+
today,future_days=1,alpha=0.99,pastyears=1):
|
2113
|
+
"""
|
2114
|
+
功能:一次性计算投资组合的所有收益率和风险信息,避免重复抓取网络股价
|
2115
|
+
输入参数:投资组合的股票列表,投资组合的持有股数列表,投资组合的成分股信息,当前日期,
|
2116
|
+
未来持有天数,置信度,使用历史数据的年数
|
2117
|
+
输出:投资组合的累积收益率、VaR金额与比率、ES金额与比率;各个成分股的累积收益率、
|
2118
|
+
VaR金额与比率、ES金额与比率
|
2119
|
+
"""
|
2120
|
+
#合成投资组合历史信息
|
2121
|
+
Pret,Pposition=get_portfolio_rets(tickerlist,shareslist, \
|
2122
|
+
tickers_info,today,pastyears)
|
2123
|
+
|
2124
|
+
#投资组合累积收益率:例13.82%
|
2125
|
+
Pretcum=round(get_ret_cumulative(Pret),4)
|
2126
|
+
#投资组合VaR金额和比率:例-$8851,0.0304
|
2127
|
+
PVaR=int(VaR_normal_standard(Pposition,Pret,future_days,alpha)+0.5)
|
2128
|
+
PVaRratio=round(abs(PVaR/Pposition),4)
|
2129
|
+
#投资组合ES金额和比率:例-$10166,0.035
|
2130
|
+
PES=int(ES_normal_standard(Pposition,Pret,future_days,alpha)+0.5)
|
2131
|
+
PESratio=round(abs(PES/Pposition),4)
|
2132
|
+
|
2133
|
+
#成分股累积回报率列表:[-0.0113,0.3278]
|
2134
|
+
retlist=[]
|
2135
|
+
for t in tickerlist:
|
2136
|
+
r=tickers_info[tickers_info['ticker']==t]['ret']
|
2137
|
+
r=r.dropna()
|
2138
|
+
retcum=round(get_ret_cumulative(r),4)
|
2139
|
+
retlist=retlist+[retcum]
|
2140
|
+
|
2141
|
+
#VaR金额和比率列表:例[-$4650,-$6691],[0.0329,0.0447]
|
2142
|
+
VaRlist=[]
|
2143
|
+
VaRratiolist=[]
|
2144
|
+
for t in range(len(tickerlist)):
|
2145
|
+
ticker=tickerlist[t]
|
2146
|
+
r=tickers_info[tickers_info['ticker']==ticker]['ret']
|
2147
|
+
r=r.dropna()
|
2148
|
+
|
2149
|
+
shares=shareslist[t]
|
2150
|
+
price=tickers_info[tickers_info['ticker']==ticker]['price'][-1]
|
2151
|
+
position=shares*price
|
2152
|
+
|
2153
|
+
VaR=int(VaR_normal_standard(position,r,future_days,alpha)+0.5)
|
2154
|
+
ratio=round(abs(VaR/position),4)
|
2155
|
+
VaRlist=VaRlist+[VaR]
|
2156
|
+
VaRratiolist=VaRratiolist+[ratio]
|
2157
|
+
|
2158
|
+
#ES金额和比率列表:例[-$5329,-$7695],[0.0377,0.0515]
|
2159
|
+
ESlist=[]
|
2160
|
+
ESratiolist=[]
|
2161
|
+
for t in range(len(tickerlist)):
|
2162
|
+
ticker=tickerlist[t]
|
2163
|
+
r=tickers_info[tickers_info['ticker']==ticker]['ret']
|
2164
|
+
r=r.dropna()
|
2165
|
+
|
2166
|
+
shares=shareslist[t]
|
2167
|
+
price=tickers_info[tickers_info['ticker']==ticker]['price'][-1]
|
2168
|
+
position=shares*price
|
2169
|
+
|
2170
|
+
ES=int(ES_normal_standard(position,r,future_days,alpha)+0.5)
|
2171
|
+
ratio=round(abs(ES/position),4)
|
2172
|
+
ESlist=ESlist+[ES]
|
2173
|
+
ESratiolist=ESratiolist+[ratio]
|
2174
|
+
|
2175
|
+
return Pretcum,PVaR,PVaRratio,PES,PESratio, \
|
2176
|
+
retlist,VaRlist,VaRratiolist,ESlist,ESratiolist
|
2177
|
+
|
2178
|
+
|
2179
|
+
#==============================================================================
|
2180
|
+
def get_portfolio_ret_risk2(tickerlist,shareslist,tickers_info, \
|
2181
|
+
today,future_days=1,alpha=0.99,pastyears=1):
|
2182
|
+
"""
|
2183
|
+
功能:一次性计算投资组合的所有收益率和风险信息,仅返回比率
|
2184
|
+
输入参数:投资组合的股票列表,投资组合的持有股数列表,投资组合的成分股信息,当前日期,
|
2185
|
+
未来持有天数,置信度,使用历史数据的年数
|
2186
|
+
输出参数:投资组合的累积收益率、VaR比率、ES比率;各个成分股的累积收益率、VaR比率、ES比率
|
2187
|
+
"""
|
2188
|
+
Pretcum,PVaR,PVaRratio,PES,PESratio, \
|
2189
|
+
retlist,VaRlist,VaRratiolist,ESlist,ESratiolist= \
|
2190
|
+
get_portfolio_ret_risk(tickerlist,shareslist,tickers_info, \
|
2191
|
+
today,future_days,alpha,pastyears)
|
2192
|
+
|
2193
|
+
return Pretcum,PVaRratio,PESratio,retlist,VaRratiolist,ESratiolist
|
2194
|
+
|
2195
|
+
|
2196
|
+
#==============================================================================
|
2197
|
+
def get_portfolio_ret_risk3(tickerlist,shareslist,tickers_info, \
|
2198
|
+
today,future_days=1,alpha=0.99,pastyears=1):
|
2199
|
+
"""
|
2200
|
+
功能:一次性计算投资组合的所有收益率和风险信息,仅返回投资组合的比率
|
2201
|
+
输入参数:投资组合的股票列表,投资组合的持有股数列表,投资组合的成分股信息,当前日期,
|
2202
|
+
未来持有天数,置信度,使用历史数据的年数
|
2203
|
+
输出:投资组合的累积收益率、VaR比率、ES比率
|
2204
|
+
"""
|
2205
|
+
Pretcum,PVaR,PVaRratio,PES,PESratio, \
|
2206
|
+
retlist,VaRlist,VaRratiolist,ESlist,ESratiolist= \
|
2207
|
+
get_portfolio_ret_risk(tickerlist,shareslist,tickers_info, \
|
2208
|
+
today,future_days,alpha,pastyears)
|
2209
|
+
|
2210
|
+
return Pretcum,PVaRratio,PESratio
|
2211
|
+
|
2212
|
+
|
2213
|
+
#==============================================================================
|
2214
|
+
|
2215
|
+
|
2216
|
+
|
2217
|
+
|
2218
|
+
|