siat 3.10.131__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 +136 -3
- 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/exchange_bond_china.pickle +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.pickle +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 +1 -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_info.pickle +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.131.dist-info → siat-3.10.132.dist-info}/METADATA +235 -227
- siat-3.10.132.dist-info/RECORD +218 -0
- {siat-3.10.131.dist-info → siat-3.10.132.dist-info}/WHEEL +1 -1
- {siat-3.10.131.dist-info → siat-3.10.132.dist-info/licenses}/LICENSE +0 -0
- siat-3.10.132.dist-info/top_level.txt +4 -0
- siat-3.10.131.dist-info/RECORD +0 -76
- siat-3.10.131.dist-info/top_level.txt +0 -1
@@ -0,0 +1,915 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
"""
|
3
|
+
本模块功能:证券资产的流动性风险与溢价
|
4
|
+
所属工具包:证券投资分析工具SIAT
|
5
|
+
SIAT:Security Investment Analysis Tool
|
6
|
+
创建日期:2019年6月18日
|
7
|
+
最新修订日期:2020年7月16日
|
8
|
+
作者:王德宏 (WANG Dehong, Peter)
|
9
|
+
作者单位:北京外国语大学国际商学院
|
10
|
+
版权所有:王德宏
|
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.grafix import *
|
21
|
+
from siat.security_prices import *
|
22
|
+
from siat.security_price2 import *
|
23
|
+
#==============================================================================
|
24
|
+
import matplotlib.pyplot as plt
|
25
|
+
|
26
|
+
#处理绘图汉字乱码问题
|
27
|
+
import sys; czxt=sys.platform
|
28
|
+
if czxt in ['win32','win64']:
|
29
|
+
plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置默认字体
|
30
|
+
mpfrc={'font.family': 'SimHei'}
|
31
|
+
|
32
|
+
if czxt in ['darwin']: #MacOSX
|
33
|
+
plt.rcParams['font.family']= ['Heiti TC']
|
34
|
+
mpfrc={'font.family': 'Heiti TC'}
|
35
|
+
|
36
|
+
if czxt in ['linux']: #website Jupyter
|
37
|
+
plt.rcParams['font.family']= ['Heiti TC']
|
38
|
+
mpfrc={'font.family':'Heiti TC'}
|
39
|
+
|
40
|
+
# 解决保存图像时'-'显示为方块的问题
|
41
|
+
plt.rcParams['axes.unicode_minus'] = False
|
42
|
+
#==============================================================================
|
43
|
+
|
44
|
+
def calc_roll_spread(pfdf):
|
45
|
+
"""
|
46
|
+
功能:从给定的股票或投资组合portfolio的数据集df中按期间计算罗尔价差
|
47
|
+
投资组合的结构:{'Market':('US','^GSPC'),'AAPL':0.5,'MSFT':0.3,'IBM':0.2}
|
48
|
+
输入:投资组合价格df,单一数据集有利于计算滚动指数
|
49
|
+
输出:罗尔价差%
|
50
|
+
注意:不包括爬虫部分
|
51
|
+
"""
|
52
|
+
sp=pfdf.copy()
|
53
|
+
#计算价格序列的价差
|
54
|
+
sp['dP']=sp['Close'].diff()
|
55
|
+
sp['dP_1']=sp['dP'].shift(1)
|
56
|
+
sp2=sp[['Close','dP','dP_1']].copy()
|
57
|
+
sp2.dropna(inplace=True)
|
58
|
+
if len(sp2) == 0: return None
|
59
|
+
|
60
|
+
#计算指标,注意cov函数的结果是一个矩阵
|
61
|
+
import numpy as np
|
62
|
+
rs_cov=abs(np.cov(sp2['dP'],sp2['dP_1'])[0,1])
|
63
|
+
#计算价格均值
|
64
|
+
p_mean=sp2['Close'].mean()
|
65
|
+
rs=2*np.sqrt(rs_cov)/p_mean
|
66
|
+
|
67
|
+
rs_pct=round(rs*100.0,4)
|
68
|
+
return rs_pct
|
69
|
+
|
70
|
+
if __name__=='__main__':
|
71
|
+
pf={'Market':('US','^GSPC'),'AAPL':1.0}
|
72
|
+
start='2020-1-1'; end='2020-3-31'
|
73
|
+
pfdf=get_portfolio_prices(pf,start,end)
|
74
|
+
rs=calc_roll_spread(pfdf)
|
75
|
+
|
76
|
+
#==============================================================================
|
77
|
+
|
78
|
+
def calc_liquidity(portfolio,indicator='amihud', \
|
79
|
+
start='MRY',end='today',printout=True):
|
80
|
+
"""
|
81
|
+
功能;计算投资组合的流动性,包括罗尔价差、阿米胡德非流动性以及PS流动性
|
82
|
+
"""
|
83
|
+
start,end=start_end_preprocess(start,end)
|
84
|
+
|
85
|
+
if not isinstance(portfolio,dict):
|
86
|
+
print(f" #Error(calc_liquidity): unrecognizable form of portfolio {portfolio}")
|
87
|
+
print(f" Only support securities in the form of a portfolio")
|
88
|
+
return
|
89
|
+
|
90
|
+
indicator1=indicator.lower()
|
91
|
+
|
92
|
+
liquidity_list=['roll','amihud','ps']
|
93
|
+
if not (indicator1 in liquidity_list):
|
94
|
+
print(f" #Error(calc_liquidity): unsupported liquidity type {indicator}")
|
95
|
+
print(f" Supported liquidity: {liquidity_list}")
|
96
|
+
return
|
97
|
+
|
98
|
+
if 'roll' in indicator1:
|
99
|
+
liq=roll_spread_portfolio(portfolio,start,end,printout)
|
100
|
+
return
|
101
|
+
|
102
|
+
if 'amihud' in indicator1:
|
103
|
+
liq=amihud_illiquidity_portfolio(portfolio,start,end,printout)
|
104
|
+
return
|
105
|
+
|
106
|
+
if 'ps' in indicator1:
|
107
|
+
liq=ps_liquidity_portfolio(portfolio,start,end,printout)
|
108
|
+
return
|
109
|
+
|
110
|
+
#==============================================================================
|
111
|
+
def roll_spread_portfolio(portfolio,start,end,printout=True):
|
112
|
+
"""
|
113
|
+
功能:按期间计算一个投资组合的罗尔价差,并输出结果
|
114
|
+
投资组合的结构:{'Market':('US','^GSPC'),'AAPL':0.5,'MSFT':0.3,'IBM':0.2}
|
115
|
+
输入:投资组合,开始日期,结束日期,是否打印结果(默认打印)
|
116
|
+
输出:罗尔价差%
|
117
|
+
注意:含爬虫部分,调用其他函数
|
118
|
+
"""
|
119
|
+
print(f" Calculating Roll spread ...")
|
120
|
+
|
121
|
+
#仅为测试用
|
122
|
+
#portfolio={'Market':('US','^GSPC'),'EDU':0.7,'TAL':0.3}
|
123
|
+
#start='2019-06-01'
|
124
|
+
#end ='2019-06-30'
|
125
|
+
|
126
|
+
#检查日期和期间的合理性
|
127
|
+
flag,start2,end2=check_period(start,end)
|
128
|
+
if not flag:
|
129
|
+
print(" #Error(roll_spread_portfolio): invalid period for,", start, end)
|
130
|
+
return None
|
131
|
+
|
132
|
+
#抓取股票价格,构建投资组合价格
|
133
|
+
#屏蔽函数内print信息输出的类
|
134
|
+
import os, sys
|
135
|
+
class HiddenPrints:
|
136
|
+
def __enter__(self):
|
137
|
+
self._original_stdout = sys.stdout
|
138
|
+
sys.stdout = open(os.devnull, 'w')
|
139
|
+
|
140
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
141
|
+
sys.stdout.close()
|
142
|
+
sys.stdout = self._original_stdout
|
143
|
+
|
144
|
+
with HiddenPrints():
|
145
|
+
sp=get_portfolio_prices(portfolio,start2,end2)
|
146
|
+
if sp is None:
|
147
|
+
print(" #Error(roll_spread_portfolio): portfolio info not available")
|
148
|
+
return None
|
149
|
+
|
150
|
+
#计算罗尔价差指标
|
151
|
+
rs_pct=calc_roll_spread(sp)
|
152
|
+
|
153
|
+
#打印报告
|
154
|
+
if printout == True:
|
155
|
+
date_start=str(sp.index[0].year)+'-'+str(sp.index[0].month)+ \
|
156
|
+
'-'+str(sp.index[0].day)
|
157
|
+
date_end=str(sp.index[-1].year)+'-'+str(sp.index[-1].month)+ \
|
158
|
+
'-'+str(sp.index[-1].day)
|
159
|
+
print("\n===== 投资组合的流动性风险 =====")
|
160
|
+
|
161
|
+
"""
|
162
|
+
_,_,tickerlist,sharelist,ticker_type=decompose_portfolio(portfolio)
|
163
|
+
if len(tickerlist)==1:
|
164
|
+
product=str(ticker_name(tickerlist,'bond'))
|
165
|
+
else:
|
166
|
+
product=str(ticker_name(tickerlist))+' by '+str(sharelist)
|
167
|
+
"""
|
168
|
+
print("证券资产:",portfolio_name(portfolio))
|
169
|
+
print("计算期间:",date_start,"to",date_end, \
|
170
|
+
"(可用日期)")
|
171
|
+
#print("罗尔价差%:",rs_pct)
|
172
|
+
print("罗尔价差比率:",str(rs_pct)+'%')
|
173
|
+
|
174
|
+
return rs_pct
|
175
|
+
|
176
|
+
if __name__=='__main__':
|
177
|
+
pf_aapl={'Market':('US','^GSPC'),'AAPL':1.0}
|
178
|
+
rs_aapl=roll_spread_portfolio(pf_aapl,'2019-01-01','2019-01-31')
|
179
|
+
|
180
|
+
pfA={'Market':('US','^GSPC'),'AAPL':0.5,'MSFT':0.3,'IBM':0.2}
|
181
|
+
rsA=roll_spread_portfolio(pfA,'2019-01-01','2019-01-31')
|
182
|
+
|
183
|
+
#==============================================================================
|
184
|
+
def calc_amihud_illiquidity(pfdf):
|
185
|
+
"""
|
186
|
+
功能:从给定的投资组合pfdf中计算阿米胡德非流动性
|
187
|
+
投资组合的结构:{'Market':('US','^GSPC'),'AAPL':0.5,'MSFT':0.3,'IBM':0.2}
|
188
|
+
输入:投资组合价格pfdf
|
189
|
+
输出:阿米胡德非流动性
|
190
|
+
注意:不包括爬虫部分
|
191
|
+
"""
|
192
|
+
sp=pfdf.copy()
|
193
|
+
#计算阿米胡德非流动性
|
194
|
+
sp2=sp[['Ret%','Amount']]
|
195
|
+
sp2=sp2.dropna()
|
196
|
+
if len(sp2) == 0: return None
|
197
|
+
|
198
|
+
import numpy as np
|
199
|
+
sp2['Ret/Amt']=np.abs(sp2['Ret%'])/np.log10(sp2['Amount'])
|
200
|
+
amihud=round(sp2['Ret/Amt'].mean(),4)
|
201
|
+
|
202
|
+
return amihud
|
203
|
+
|
204
|
+
if __name__=='__main__':
|
205
|
+
pf={'Market':('US','^GSPC'),'AAPL':1.0}
|
206
|
+
start='2020-1-1'; end='2020-3-31'
|
207
|
+
pfdf=get_portfolio_prices(pf,start,end)
|
208
|
+
amihud=calc_amihud_illiquidity(pfdf)
|
209
|
+
|
210
|
+
#==============================================================================
|
211
|
+
def amihud_illiquidity_portfolio(portfolio,start,end,printout=True):
|
212
|
+
"""
|
213
|
+
功能:按天计算一个投资组合的阿米胡德非流动性指数
|
214
|
+
投资组合的结构:{'Market':('US','^GSPC'),'AAPL':0.5,'MSFT':0.3,'IBM':0.2}
|
215
|
+
输入:投资组合,开始日期,结束日期,是否打印结果
|
216
|
+
输出:阿米胡德非流动性指数
|
217
|
+
注意:含爬虫部分,调用其他函数
|
218
|
+
"""
|
219
|
+
print(f" Calculating Amihud illiquidity ...")
|
220
|
+
|
221
|
+
#仅为测试用
|
222
|
+
#portfolio={'Market':('US','^GSPC'),'EDU':0.7,'TAL':0.3}
|
223
|
+
#start='2019-06-01'
|
224
|
+
#end ='2019-06-30'
|
225
|
+
|
226
|
+
#检查日期和期间的合理性
|
227
|
+
flag,start2,end2=check_period(start,end)
|
228
|
+
if not flag:
|
229
|
+
print(" #Error(amihud_illiquidity_portfolio): invalid period for,", start, end)
|
230
|
+
return None
|
231
|
+
|
232
|
+
#抓取股票价格,构建投资组合价格
|
233
|
+
#屏蔽函数内print信息输出的类
|
234
|
+
import os, sys
|
235
|
+
class HiddenPrints:
|
236
|
+
def __enter__(self):
|
237
|
+
self._original_stdout = sys.stdout
|
238
|
+
sys.stdout = open(os.devnull, 'w')
|
239
|
+
|
240
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
241
|
+
sys.stdout.close()
|
242
|
+
sys.stdout = self._original_stdout
|
243
|
+
|
244
|
+
with HiddenPrints():
|
245
|
+
sp=get_portfolio_prices(portfolio,start2,end2)
|
246
|
+
if sp is None:
|
247
|
+
print(" #Error(amihud_illiquidity_portfolio): portfolio info not available")
|
248
|
+
return None
|
249
|
+
"""
|
250
|
+
with HiddenPrints():
|
251
|
+
sp,found=get_price_1portfolio(portfolio,start2,end2)
|
252
|
+
if found != 'Found':
|
253
|
+
print(" #Error(amihud_illiquidity_portfolio): portfolio info not available")
|
254
|
+
return None
|
255
|
+
"""
|
256
|
+
#计算指标
|
257
|
+
amihud=calc_amihud_illiquidity(sp)
|
258
|
+
|
259
|
+
#打印报告
|
260
|
+
if printout == True:
|
261
|
+
date_start=str(sp.index[0].year)+'-'+str(sp.index[0].month)+ \
|
262
|
+
'-'+str(sp.index[0].day)
|
263
|
+
date_end=str(sp.index[-1].year)+'-'+str(sp.index[-1].month)+ \
|
264
|
+
'-'+str(sp.index[-1].day)
|
265
|
+
print("\n===== 投资组合的流动性风险 =====")
|
266
|
+
"""
|
267
|
+
_,_,tickerlist,sharelist,ticker_type=decompose_portfolio(portfolio)
|
268
|
+
if len(tickerlist)==1:
|
269
|
+
product=str(ticker_name(tickerlist,'bond'))
|
270
|
+
else:
|
271
|
+
product=str(ticker_name(tickerlist))+' by '+str(sharelist)
|
272
|
+
"""
|
273
|
+
print("证券资产:",portfolio_name(portfolio))
|
274
|
+
print("计算期间:",date_start,"至",date_end, \
|
275
|
+
"(可用日期)")
|
276
|
+
print("阿米胡德非流动性:",amihud,"(对数算法)")
|
277
|
+
|
278
|
+
return amihud
|
279
|
+
|
280
|
+
if __name__=='__main__':
|
281
|
+
pf_aapl={'Market':('US','^GSPC'),'AAPL':1.0}
|
282
|
+
amihud_aapl=amihud_illiquidity_portfolio(pf_aapl,'2019-01-01','2019-01-31')
|
283
|
+
|
284
|
+
#==============================================================================
|
285
|
+
def calc_ps_liquidity(pfdf):
|
286
|
+
"""
|
287
|
+
功能:从给定的投资组合pfdf中计算帕斯托-斯坦堡流动性,原始公式
|
288
|
+
投资组合的结构:{'Market':('US','^GSPC'),'AAPL':0.5,'MSFT':0.3,'IBM':0.2}
|
289
|
+
输入:投资组合价格df
|
290
|
+
输出:帕斯托-斯坦堡流动性
|
291
|
+
注意:不包括爬虫部分
|
292
|
+
"""
|
293
|
+
reg=pfdf.copy()
|
294
|
+
|
295
|
+
reg['Ret-RF']=reg['Ret%']-reg['RF']
|
296
|
+
reg['Ret-RF_1']=reg['Ret-RF'].shift(1)
|
297
|
+
reg['Mkt']=reg['Mkt-RF']+reg['RF']
|
298
|
+
reg['Mkt_1']=reg['Mkt'].shift(1)
|
299
|
+
reg['Amount_1']=reg['Amount'].shift(1)
|
300
|
+
|
301
|
+
import numpy as np
|
302
|
+
reg1=reg[['Ret-RF','Mkt_1','Ret-RF_1','Amount_1']]
|
303
|
+
reg1=reg1.dropna()
|
304
|
+
if len(reg1) == 0: return None
|
305
|
+
reg1['signAmt_1']=np.sign(reg1['Ret-RF_1'])*np.log10(reg1['Amount_1'])
|
306
|
+
reg2=reg1[['Ret-RF','Mkt_1','signAmt_1']].copy()
|
307
|
+
|
308
|
+
#回归前彻底删除带有NaN和inf等无效值的样本,否则回归中可能出错
|
309
|
+
reg2=reg2[~reg2.isin([np.nan, np.inf, -np.inf]).any(axis=1)].dropna()
|
310
|
+
if len(reg2) == 0: return None
|
311
|
+
|
312
|
+
##计算帕斯托-斯坦堡流动性PSL
|
313
|
+
import statsmodels.api as sm
|
314
|
+
y=reg2['Ret-RF']
|
315
|
+
X=reg2[['Mkt_1','signAmt_1']]
|
316
|
+
X1=sm.add_constant(X)
|
317
|
+
results=sm.OLS(y,X1).fit()
|
318
|
+
try:
|
319
|
+
[alpha,beta,psl]=results.params
|
320
|
+
except:
|
321
|
+
print(" #Error(calc_ps_liquidity): failed to extract reg parms",results.params)
|
322
|
+
return None
|
323
|
+
|
324
|
+
return round(psl,4)
|
325
|
+
|
326
|
+
if __name__=='__main__':
|
327
|
+
pf={'Market':('US','^GSPC'),'AAPL':1.0}
|
328
|
+
start='2020-1-1'; end='2020-3-31'
|
329
|
+
pfdf=get_portfolio_prices(pf,start,end)
|
330
|
+
psl=calc_ps_liquidity(pfdf)
|
331
|
+
|
332
|
+
#==============================================================================
|
333
|
+
def calc_ps_liquidity_modified(pfdf):
|
334
|
+
"""
|
335
|
+
功能:从给定的投资组合pfdf中计算修正的帕斯托-斯坦堡流动性
|
336
|
+
投资组合的结构:{'Market':('US','^GSPC'),'AAPL':0.5,'MSFT':0.3,'IBM':0.2}
|
337
|
+
输入:投资组合价格df
|
338
|
+
输出:修正的帕斯托-斯坦堡流动性,符号内含
|
339
|
+
注意:不包括爬虫部分
|
340
|
+
"""
|
341
|
+
reg=pfdf.copy()
|
342
|
+
|
343
|
+
reg['Ret-RF']=reg['Ret%']-reg['RF']
|
344
|
+
reg['Ret-RF_1']=reg['Ret-RF'].shift(1)
|
345
|
+
reg['Mkt']=reg['Mkt-RF']+reg['RF']
|
346
|
+
reg['Mkt_1']=reg['Mkt'].shift(1)
|
347
|
+
reg['Amount_1']=reg['Amount'].shift(1)
|
348
|
+
|
349
|
+
import numpy as np
|
350
|
+
reg1=reg[['Ret-RF','Mkt_1','Ret-RF_1','Amount_1']]
|
351
|
+
reg1=reg1.dropna()
|
352
|
+
if len(reg1) == 0: return None
|
353
|
+
|
354
|
+
#修正psl,符号内含
|
355
|
+
#reg1['signAmt_1']=np.sign(reg1['Ret-RF_1'])*np.log10(reg1['Amount_1'])
|
356
|
+
reg1['signAmt_1']=np.log10(reg1['Amount_1'])
|
357
|
+
reg2=reg1[['Ret-RF','Mkt_1','signAmt_1']].copy()
|
358
|
+
|
359
|
+
#回归前彻底删除带有NaN和inf等无效值的样本,否则回归中可能出错
|
360
|
+
reg2=reg2[~reg2.isin([np.nan, np.inf, -np.inf]).any(1)].dropna()
|
361
|
+
if len(reg2) == 0: return None
|
362
|
+
|
363
|
+
##计算帕斯托-斯坦堡流动性PSL
|
364
|
+
import statsmodels.api as sm
|
365
|
+
y=reg2['Ret-RF']
|
366
|
+
X=reg2[['Mkt_1','signAmt_1']]
|
367
|
+
X1=sm.add_constant(X)
|
368
|
+
results=sm.OLS(y,X1).fit()
|
369
|
+
[alpha,beta,psl]=results.params
|
370
|
+
|
371
|
+
return round(psl,4)
|
372
|
+
|
373
|
+
if __name__=='__main__':
|
374
|
+
pf={'Market':('US','^GSPC'),'AAPL':1.0}
|
375
|
+
start='2020-1-1'; end='2020-3-31'
|
376
|
+
pfdf=get_portfolio_prices(pf,start,end)
|
377
|
+
psl=calc_ps_liquidity_modified(pfdf)
|
378
|
+
|
379
|
+
#==============================================================================
|
380
|
+
def ps_liquidity_portfolio(portfolio,start,end,printout=True):
|
381
|
+
"""
|
382
|
+
功能:按天计算一个投资组合的帕斯托-斯坦堡流动性
|
383
|
+
投资组合的结构:{'Market':('US','^GSPC'),'AAPL':0.5,'MSFT':0.3,'IBM':0.2}
|
384
|
+
输入:投资组合,开始日期,结束日期,是否打印结果
|
385
|
+
输出:帕斯托-斯坦堡流动性
|
386
|
+
注意:含爬虫部分,调用其他函数
|
387
|
+
"""
|
388
|
+
print(f" Calculating Pastor-Stambaugh liquidity ...")
|
389
|
+
#仅为测试用
|
390
|
+
#portfolio={'Market':('US','^GSPC'),'EDU':0.7,'TAL':0.3}
|
391
|
+
#start='2019-06-01'
|
392
|
+
#end ='2019-06-30'
|
393
|
+
|
394
|
+
#检查日期和期间的合理性
|
395
|
+
flag,start2,end2=check_period(start,end)
|
396
|
+
if not flag:
|
397
|
+
print(" #Error(amihud_illiquidity_portfolio): invalid period for,", start, end)
|
398
|
+
return None
|
399
|
+
|
400
|
+
#抓取股票价格,构建投资组合价格
|
401
|
+
#屏蔽函数内print信息输出的类
|
402
|
+
import os, sys
|
403
|
+
class HiddenPrints:
|
404
|
+
def __enter__(self):
|
405
|
+
self._original_stdout = sys.stdout
|
406
|
+
sys.stdout = open(os.devnull, 'w')
|
407
|
+
|
408
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
409
|
+
sys.stdout.close()
|
410
|
+
sys.stdout = self._original_stdout
|
411
|
+
|
412
|
+
with HiddenPrints():
|
413
|
+
sp=get_portfolio_prices(portfolio,start2,end2)
|
414
|
+
if sp is None:
|
415
|
+
print(" #Error(ps_liquidity_portfolio): portfolio info not available")
|
416
|
+
return None
|
417
|
+
|
418
|
+
#计算帕斯托-斯坦堡流动性
|
419
|
+
psl=calc_ps_liquidity(sp)
|
420
|
+
|
421
|
+
#打印报告
|
422
|
+
if printout == True:
|
423
|
+
date_start=str(sp.index[0].year)+'-'+str(sp.index[0].month)+ \
|
424
|
+
'-'+str(sp.index[0].day)
|
425
|
+
date_end=str(sp.index[-1].year)+'-'+str(sp.index[-1].month)+ \
|
426
|
+
'-'+str(sp.index[-1].day)
|
427
|
+
print("\n===== 投资组合的流动性风险 =====")
|
428
|
+
|
429
|
+
"""
|
430
|
+
_,_,tickerlist,sharelist,ticker_type=decompose_portfolio(portfolio)
|
431
|
+
if len(tickerlist)==1:
|
432
|
+
product=str(ticker_name(tickerlist,'bond'))
|
433
|
+
else:
|
434
|
+
product=str(ticker_name(tickerlist))+' by '+str(sharelist)
|
435
|
+
"""
|
436
|
+
print("证券资产:",portfolio_name(portfolio))
|
437
|
+
print("计算期间:",date_start,"to",date_end, \
|
438
|
+
"(可用日期)")
|
439
|
+
print("Pastor-Stambaugh流动性:",psl,"(对数算法)")
|
440
|
+
|
441
|
+
return psl
|
442
|
+
|
443
|
+
if __name__=='__main__':
|
444
|
+
pf_aapl={'Market':('US','^GSPC'),'AAPL':1.0}
|
445
|
+
psl_aapl=ps_liquidity_portfolio(pf_aapl,'2019-01-01','2019-01-31')
|
446
|
+
|
447
|
+
|
448
|
+
#==============================================================================
|
449
|
+
#==============================================================================
|
450
|
+
def plot_liquidity_monthly(portfolio,start,end,liquidity_type):
|
451
|
+
"""
|
452
|
+
功能:将资产流动性指标逐月绘图对比
|
453
|
+
输入:投资组合,开始/结束日期,流动性指标类别
|
454
|
+
输出:流动性指标的逐月数据框
|
455
|
+
显示:按月绘图投资组合的流动性指标
|
456
|
+
"""
|
457
|
+
#仅为测试用
|
458
|
+
#portfolio={'Market':('US','^GSPC'),'JD':0.3,'BABA':0.7}
|
459
|
+
#start='2019-01-01'
|
460
|
+
#end='2019-03-31'
|
461
|
+
#liquidity_type='roll_spread'
|
462
|
+
|
463
|
+
#检查日期和期间的合理性
|
464
|
+
flag,start2,end2=check_period(start,end)
|
465
|
+
if not flag:
|
466
|
+
print(" #Error(plot_liquidity_monthly): invalid period for,", start, end)
|
467
|
+
return None
|
468
|
+
|
469
|
+
#检查:支持的liquidity_type
|
470
|
+
liquidity_list=['roll_spread','amihud_illiquidity','ps_liquidity']
|
471
|
+
if liquidity_type not in liquidity_list:
|
472
|
+
print(" #Error(plot_liquidity_monthly): not supported liquidity type")
|
473
|
+
print(" Supported liquidity type:",liquidity_list)
|
474
|
+
return None
|
475
|
+
|
476
|
+
#抓取投资组合信息
|
477
|
+
print("\n Searching for portfolio information ...")
|
478
|
+
df=get_portfolio_prices(portfolio,start,end)
|
479
|
+
|
480
|
+
#拆分start/end之间的各个年份和月份
|
481
|
+
mdlist=calc_monthly_date_range(start,end)
|
482
|
+
if len(mdlist) == 0:
|
483
|
+
print(" #Error(plot_liquidity_monthly): start/end dates inappropriate")
|
484
|
+
return None
|
485
|
+
|
486
|
+
#用于保存流动性指标
|
487
|
+
import pandas as pd
|
488
|
+
print(" Calculating monthly",liquidity_type,"...")
|
489
|
+
rarfunc='calc_'+liquidity_type
|
490
|
+
rars=pd.DataFrame(columns=('YM','rar'))
|
491
|
+
zeroline=False
|
492
|
+
for i in range(0,len(mdlist)):
|
493
|
+
startym=mdlist[i][0]
|
494
|
+
YM=startym.strftime("%Y-%m")
|
495
|
+
#print(YM,end=' ')
|
496
|
+
endym=mdlist[i][1]
|
497
|
+
pfdf=sample_selection(df,startym,endym)
|
498
|
+
rar=eval(rarfunc)(pfdf)
|
499
|
+
if rar is None: continue
|
500
|
+
|
501
|
+
if rar < 0: zeroline=True
|
502
|
+
row=pd.Series({'YM':YM,'rar':rar})
|
503
|
+
try:
|
504
|
+
rars=rars.append(row,ignore_index=True)
|
505
|
+
except:
|
506
|
+
rars=rars._append(row,ignore_index=True)
|
507
|
+
#print("completed.")
|
508
|
+
rars.set_index('YM',inplace=True)
|
509
|
+
|
510
|
+
#绘图
|
511
|
+
colname='rar'
|
512
|
+
collabel=ectranslate(liquidity_type)
|
513
|
+
ylabeltxt=ectranslate(liquidity_type)
|
514
|
+
titletxt="证券流动性风险的月度指标"
|
515
|
+
|
516
|
+
_,_,tickerlist,sharelist,ticker_type=decompose_portfolio(portfolio)
|
517
|
+
if len(tickerlist)==1:
|
518
|
+
product=str(ticker_name(tickerlist,'bond'))
|
519
|
+
else:
|
520
|
+
product=str(ticker_name(tickerlist))+' by '+str(sharelist)
|
521
|
+
import datetime as dt; today=dt.date.today()
|
522
|
+
footnote="证券="+product+"\n数据来源:新浪/stooq, "+str(today)
|
523
|
+
datatag=False
|
524
|
+
power=4
|
525
|
+
plot_line(rars,colname,collabel,ylabeltxt,titletxt,footnote,datatag, \
|
526
|
+
power,zeroline)
|
527
|
+
|
528
|
+
return rars
|
529
|
+
|
530
|
+
|
531
|
+
if __name__=='__main__':
|
532
|
+
portfolio={'Market':('US','^GSPC'),'AAPL':1.0}
|
533
|
+
start='2019-01-01'; end='2020-6-30'
|
534
|
+
liquidity_type='roll_spread'
|
535
|
+
liq1=plot_liquidity_monthly(portfolio,start,end,liquidity_type)
|
536
|
+
liquidity_type='amihud_illiquidity'
|
537
|
+
liq2=plot_liquidity_monthly(portfolio,start,end,liquidity_type)
|
538
|
+
liquidity_type='ps_liquidity'
|
539
|
+
liq3=plot_liquidity_monthly(portfolio,start,end,liquidity_type)
|
540
|
+
|
541
|
+
pf1={'Market':('US','^GSPC'),'VIPS':0.1,'PDD':0.2,'JD':0.3,'BABA':0.4}
|
542
|
+
al1=plot_liquidity_monthly(pf1,start,end,'roll_spread')
|
543
|
+
|
544
|
+
pf2={'Market':('US','^GSPC'),'^GSPC':1.0}
|
545
|
+
al2=plot_liquidity_monthly(pf2,start,end,'roll_spread')
|
546
|
+
al3=plot_liquidity_monthly(pf2,start,end,'amihud_illiquidity')
|
547
|
+
al4=plot_liquidity_monthly(pf2,start,end,'ps_liquidity')
|
548
|
+
|
549
|
+
#==============================================================================
|
550
|
+
def plot_liquidity_annual(portfolio,start,end,liquidity_type):
|
551
|
+
"""
|
552
|
+
功能:将流动性指标逐年绘图对比
|
553
|
+
输入:投资组合,开始/结束日期,流动性指标类别
|
554
|
+
输出:流动性指标的逐年数据框
|
555
|
+
显示:按年绘图投资组合的流动性指标
|
556
|
+
"""
|
557
|
+
#仅为测试用
|
558
|
+
#portfolio={'Market':('US','^GSPC'),'TSLA':1.0}
|
559
|
+
#start='2009-07-01'
|
560
|
+
#end='2019-06-30'
|
561
|
+
#liquidity_type='roll_spread'
|
562
|
+
|
563
|
+
#检查日期和期间的合理性
|
564
|
+
flag,start2,end2=check_period(start,end)
|
565
|
+
if not flag:
|
566
|
+
print(" #Error(plot_liquidity_annual): invalid period for,", start, end)
|
567
|
+
return None
|
568
|
+
|
569
|
+
#检查:支持的liquidity_type
|
570
|
+
liquidity_list=['roll_spread','amihud_illiquidity','ps_liquidity']
|
571
|
+
if liquidity_type not in liquidity_list:
|
572
|
+
print(" #Error(plot_liquidity_annual): not supported liquidity type")
|
573
|
+
print(" Supported liquidity type:",liquidity_list)
|
574
|
+
return None
|
575
|
+
|
576
|
+
#抓取投资组合信息
|
577
|
+
print("\n Searching for portfolio information ...")
|
578
|
+
df=get_portfolio_prices(portfolio,start,end)
|
579
|
+
|
580
|
+
#拆分start/end之间的各个年份和月份
|
581
|
+
mdlist=calc_yearly_date_range(start,end)
|
582
|
+
if len(mdlist) == 0:
|
583
|
+
print(" #Error(plot_liquidity_annual): start/end dates inappropriate")
|
584
|
+
return None
|
585
|
+
|
586
|
+
#用于保存指标
|
587
|
+
print(" Calculating annual",liquidity_type,"...")
|
588
|
+
rarfunc='calc_'+liquidity_type
|
589
|
+
import pandas as pd
|
590
|
+
rars=pd.DataFrame(columns=('YR','liquidity'))
|
591
|
+
zeroline=False
|
592
|
+
for i in range(0,len(mdlist)):
|
593
|
+
startyr=mdlist[i][0]
|
594
|
+
YR=startyr.strftime("%Y")
|
595
|
+
#print(YR,end=' ')
|
596
|
+
endyr=mdlist[i][1]
|
597
|
+
pfdf=sample_selection(df,startyr,endyr)
|
598
|
+
rar=eval(rarfunc)(pfdf)
|
599
|
+
if rar is not None:
|
600
|
+
if rar < 0: zeroline=True
|
601
|
+
row=pd.Series({'YR':YR,'liquidity':rar})
|
602
|
+
try:
|
603
|
+
rars=rars.append(row,ignore_index=True)
|
604
|
+
except:
|
605
|
+
rars=rars._append(row,ignore_index=True)
|
606
|
+
rars.set_index('YR',inplace=True)
|
607
|
+
|
608
|
+
#绘图
|
609
|
+
colname='liquidity'
|
610
|
+
collabel=ectranslate(liquidity_type)
|
611
|
+
ylabeltxt=ectranslate(liquidity_type)
|
612
|
+
titletxt="证券流动性风险的年度指标"
|
613
|
+
|
614
|
+
_,_,tickerlist,sharelist,ticker_type=decompose_portfolio(portfolio)
|
615
|
+
if len(tickerlist)==1:
|
616
|
+
product=str(ticker_name(tickerlist,'bond'))
|
617
|
+
else:
|
618
|
+
product=str(ticker_name(tickerlist,'bond'))+' by '+str(sharelist)
|
619
|
+
|
620
|
+
import datetime as dt; today=dt.date.today()
|
621
|
+
footnote="证券="+product+"\n数据来源:雅虎财经, "+str(today)
|
622
|
+
datatag=False
|
623
|
+
power=4
|
624
|
+
plot_line(rars,colname,collabel,ylabeltxt,titletxt,footnote,datatag, \
|
625
|
+
power,zeroline)
|
626
|
+
|
627
|
+
return rars
|
628
|
+
|
629
|
+
|
630
|
+
if __name__=='__main__':
|
631
|
+
portfolio={'Market':('US','^GSPC'),'AAPL':1.0}
|
632
|
+
start='2010-01-01'
|
633
|
+
end='2019-12-31'
|
634
|
+
liquidity_type='roll_spread'
|
635
|
+
al1=plot_liquidity_annual(portfolio,start,end,liquidity_type)
|
636
|
+
|
637
|
+
portfolio={'Market':('US','^GSPC'),'VIPS':0.1,'PDD':0.2,'JD':0.3,'BABA':0.4}
|
638
|
+
start='2013-01-01'
|
639
|
+
end='2019-06-30'
|
640
|
+
al2=plot_liquidity_annual(portfolio,start,end,liquidity_type)
|
641
|
+
|
642
|
+
|
643
|
+
#==============================================================================
|
644
|
+
def draw_liquidity(liqs):
|
645
|
+
"""
|
646
|
+
功能:绘制滚动窗口曲线
|
647
|
+
输入:滚动数据df,内含投资组合Portfolio和指数名称Type
|
648
|
+
"""
|
649
|
+
|
650
|
+
plt.figure(figsize=(8,5))
|
651
|
+
|
652
|
+
portfolio=liqs['Portfolio'][0]
|
653
|
+
colname='Ratio'
|
654
|
+
collabel=ectranslate(liqs['Type'][0])
|
655
|
+
ylabeltxt=ectranslate(liqs['Type'][0])
|
656
|
+
titletxt="证券流动性风险的滚动趋势"
|
657
|
+
|
658
|
+
_,_,tickerlist,sharelist,ticker_type=decompose_portfolio(portfolio)
|
659
|
+
if len(tickerlist)==1:
|
660
|
+
product=str(ticker_name(tickerlist))
|
661
|
+
else:
|
662
|
+
product=str(ticker_name(tickerlist,'bond'))+' by '+str(sharelist)
|
663
|
+
|
664
|
+
import datetime as dt; today=dt.date.today()
|
665
|
+
footnote="证券="+product+"\n数据来源:新浪/stooq, "+str(today)
|
666
|
+
datatag=False
|
667
|
+
power=4
|
668
|
+
|
669
|
+
zeroline=False
|
670
|
+
neg_liqs=liqs[liqs['Ratio'] < 0]
|
671
|
+
if len(neg_liqs) > 0: zeroline=True
|
672
|
+
|
673
|
+
plot_line(liqs,colname,collabel,ylabeltxt,titletxt,footnote,datatag, \
|
674
|
+
power,zeroline)
|
675
|
+
|
676
|
+
return
|
677
|
+
|
678
|
+
#==============================================================================
|
679
|
+
def liquidity_rolling(portfolio,start,end,liquidity_type, \
|
680
|
+
window=30,graph=True):
|
681
|
+
"""
|
682
|
+
功能:滚动计算一个投资组合的流动性风险
|
683
|
+
投资组合的结构:{'Market':('US','^GSPC'),'AAPL':0.5,'MSFT':0.3,'IBM':0.2}
|
684
|
+
输入:投资组合,开始日期,结束日期,指数名称,滚动窗口宽度(天数)
|
685
|
+
输出:流动性风险
|
686
|
+
注意:因需要滚动计算,开始和结束日期之间需要拉开距离
|
687
|
+
"""
|
688
|
+
|
689
|
+
#检查日期和期间的合理性
|
690
|
+
flag,start2,end2=check_period(start,end)
|
691
|
+
if not flag:
|
692
|
+
print(" #Error(liquidity_rolling): invalid period for,", start, end)
|
693
|
+
return None
|
694
|
+
|
695
|
+
#检查:支持的liquidity_type
|
696
|
+
liquidity_list=['roll_spread','amihud_illiquidity','ps_liquidity']
|
697
|
+
if liquidity_type not in liquidity_list:
|
698
|
+
print(" #Error(liquidity_rolling): not supported liquidity type")
|
699
|
+
print(" Supported liquidity type:",liquidity_list)
|
700
|
+
return None
|
701
|
+
|
702
|
+
#抓取投资组合信息
|
703
|
+
print("\n Searching information for portfolio:",portfolio)
|
704
|
+
reg=get_portfolio_prices(portfolio,start,end)
|
705
|
+
if reg is None:
|
706
|
+
print(" #Error(liquidity_rolling): failed to get portfolio info")
|
707
|
+
return None
|
708
|
+
|
709
|
+
#滚动计算
|
710
|
+
print(" Calculating its rolling ratios:",liquidity_type)
|
711
|
+
import pandas as pd; import numpy as np
|
712
|
+
datelist=reg.index.to_list()
|
713
|
+
calc_func='calc_'+liquidity_type
|
714
|
+
|
715
|
+
liqs=pd.DataFrame(columns=('Portfolio','Date','Ratio','Type'))
|
716
|
+
for i in np.arange(0,len(reg)):
|
717
|
+
i1=i+window
|
718
|
+
if i1 >= len(reg): break
|
719
|
+
|
720
|
+
#构造滚动窗口
|
721
|
+
windf=reg[reg.index >= datelist[i]]
|
722
|
+
windf=windf[windf.index < datelist[i1]]
|
723
|
+
#print(i,datelist[i],i1,datelist[i1],len(windf))
|
724
|
+
|
725
|
+
#使用滚动窗口计算
|
726
|
+
liq=eval(calc_func)(windf)
|
727
|
+
if liq is None: continue
|
728
|
+
|
729
|
+
#记录计算结果
|
730
|
+
row=pd.Series({'Portfolio':portfolio,'Date':datelist[i1-1],'Ratio':liq,'Type':liquidity_type})
|
731
|
+
try:
|
732
|
+
liqs=liqs.append(row,ignore_index=True)
|
733
|
+
except:
|
734
|
+
liqs=liqs._append(row,ignore_index=True)
|
735
|
+
|
736
|
+
liqs.set_index(['Date'],inplace=True)
|
737
|
+
|
738
|
+
#第6步:绘图
|
739
|
+
if graph == True:
|
740
|
+
draw_liquidity(liqs)
|
741
|
+
|
742
|
+
return liqs
|
743
|
+
|
744
|
+
|
745
|
+
if __name__=='__main__':
|
746
|
+
portfolio={'Market':('US','^GSPC'),'AAPL':0.5,'MSFT':0.3,'IBM':0.2}
|
747
|
+
start='2019-1-1'
|
748
|
+
end='2019-6-30'
|
749
|
+
window=30
|
750
|
+
graph=True
|
751
|
+
liquidity_type='roll_spread'
|
752
|
+
liquidity_type='ps'
|
753
|
+
liqs=liquidity_rolling(portfolio,start,end,liquidity_type)
|
754
|
+
|
755
|
+
|
756
|
+
#==============================================================================
|
757
|
+
#==============================================================================
|
758
|
+
def compare_liquidity(portfolio1,portfolio2,start='MRY',end='today', \
|
759
|
+
indicator='amihud',window=30, \
|
760
|
+
period_value=False):
|
761
|
+
"""
|
762
|
+
功能:套壳函数compare_liquidity_rolling,无差异,保持兼容性
|
763
|
+
"""
|
764
|
+
start,end=start_end_preprocess(start,end)
|
765
|
+
|
766
|
+
indicator1=indicator.lower()
|
767
|
+
if 'roll' in indicator1:
|
768
|
+
indicator1='roll_spread'
|
769
|
+
elif 'amihud' in indicator1:
|
770
|
+
indicator1='amihud_illiquidity'
|
771
|
+
else:
|
772
|
+
indicator1='ps_liquidity'
|
773
|
+
|
774
|
+
compare_liquidity_rolling(portfolio1=portfolio1,portfolio2=portfolio2, \
|
775
|
+
start=start,end=end,liquidity_type=indicator1, \
|
776
|
+
window=window)
|
777
|
+
|
778
|
+
|
779
|
+
#对比两个投资组合在期间内的流动性整体数值
|
780
|
+
if period_value:
|
781
|
+
calc_liquidity(portfolio1,start=start,end=end,indicator=indicator)
|
782
|
+
calc_liquidity(portfolio2,start=start,end=end,indicator=indicator)
|
783
|
+
|
784
|
+
return
|
785
|
+
|
786
|
+
#==============================================================================
|
787
|
+
def compare_liquidity_rolling(portfolio1,portfolio2,start='MRY',end='today', \
|
788
|
+
liquidity_type='amihud_illiquidity',window=30):
|
789
|
+
"""
|
790
|
+
功能:比较两个投资组合portfolio1和portfolio2在start至end期间的流动性指标liquidity_type
|
791
|
+
"""
|
792
|
+
start,end=start_end_preprocess(start,end)
|
793
|
+
|
794
|
+
if not(isinstance(portfolio1,dict)):
|
795
|
+
print(" #Error(compare_liquidity_rolling): unresolveable protfolio {portfolio1}")
|
796
|
+
return
|
797
|
+
|
798
|
+
if not(isinstance(portfolio2,dict)):
|
799
|
+
print(" #Error(compare_liquidity_rolling): unresolveable protfolio {portfolio2}")
|
800
|
+
return
|
801
|
+
|
802
|
+
print(" Working on the liquidities of 2 securities ...")
|
803
|
+
|
804
|
+
#屏蔽函数内print信息输出的类
|
805
|
+
import os, sys
|
806
|
+
class HiddenPrints:
|
807
|
+
def __enter__(self):
|
808
|
+
self._original_stdout = sys.stdout
|
809
|
+
sys.stdout = open(os.devnull, 'w')
|
810
|
+
|
811
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
812
|
+
sys.stdout.close()
|
813
|
+
sys.stdout = self._original_stdout
|
814
|
+
|
815
|
+
#投资组合1
|
816
|
+
pf1name=portfolio_name(portfolio1)
|
817
|
+
if pf1name == '投资组合':
|
818
|
+
pf1name == '组合1'
|
819
|
+
|
820
|
+
with HiddenPrints():
|
821
|
+
liqs1=liquidity_rolling(portfolio1,start,end,liquidity_type, \
|
822
|
+
window=window,graph=False)
|
823
|
+
if liqs1 is None:
|
824
|
+
print(f" #Error(compare_liquidity_rolling): portfolio info inaccesible for {pf1name}")
|
825
|
+
return
|
826
|
+
|
827
|
+
colname1='Ratio'
|
828
|
+
label1=ectranslate(liquidity_type)
|
829
|
+
datatag1=False
|
830
|
+
|
831
|
+
zeroline=False
|
832
|
+
neg_liqs1=liqs1[liqs1['Ratio'] < 0]
|
833
|
+
if len(neg_liqs1) > 0: zeroline=True
|
834
|
+
|
835
|
+
#投资组合2
|
836
|
+
pf2name=portfolio_name(portfolio2)
|
837
|
+
if pf2name == '投资组合':
|
838
|
+
pf2name == '组合2'
|
839
|
+
|
840
|
+
with HiddenPrints():
|
841
|
+
liqs2=liquidity_rolling(portfolio2,start,end,liquidity_type, \
|
842
|
+
window=window,graph=False)
|
843
|
+
|
844
|
+
if liqs2 is None:
|
845
|
+
print(f" #Error(compare_liquidity_rolling): portfolio info inaccesible for {pf2name}")
|
846
|
+
return
|
847
|
+
|
848
|
+
colname2='Ratio'
|
849
|
+
label2=ectranslate(liquidity_type)
|
850
|
+
datatag2=False
|
851
|
+
|
852
|
+
zeroline=False
|
853
|
+
neg_liqs2=liqs2[liqs2['Ratio'] < 0]
|
854
|
+
if len(neg_liqs2) > 0: zeroline=True
|
855
|
+
|
856
|
+
#绘图
|
857
|
+
ylabeltxt=ectranslate(liquidity_type)
|
858
|
+
titletxt="证券流动性风险:"+pf1name+' vs '+pf2name
|
859
|
+
|
860
|
+
_,_,tickerlist1,sharelist1,ticker_type1=decompose_portfolio(portfolio1)
|
861
|
+
if len(tickerlist1)==1:
|
862
|
+
product1=str(ticker_name(tickerlist1,'bond'))
|
863
|
+
else:
|
864
|
+
product1=str(ticker_name(tickerlist1,'bond'))+',持仓'+str(sharelist1)
|
865
|
+
_,_,tickerlist2,sharelist2,ticker_type2=decompose_portfolio(portfolio2)
|
866
|
+
if len(tickerlist2)==1:
|
867
|
+
product2=str(ticker_name(tickerlist2,'bond'))
|
868
|
+
else:
|
869
|
+
product2=str(ticker_name(tickerlist2,'bond'))+',持仓'+str(sharelist2)
|
870
|
+
|
871
|
+
import datetime as dt; today=dt.date.today()
|
872
|
+
"""
|
873
|
+
footnote=pf1name+":"+product1+"\n"+pf2name+":"+product2+ \
|
874
|
+
"\n数据来源:新浪/stooq, "+str(today)
|
875
|
+
"""
|
876
|
+
if liquidity_type in ['amihud_illiquidity','ps_liquidity']:
|
877
|
+
notes="注:图中流动性指标采用对数算法\n"
|
878
|
+
else:
|
879
|
+
notes=''
|
880
|
+
footnote= notes+"数据来源:新浪/stooq, "+str(today)
|
881
|
+
power=0 #不绘制趋势线
|
882
|
+
|
883
|
+
plot_line2_coaxial(liqs1,pf1name,colname1,label1, \
|
884
|
+
liqs2,pf2name,colname2,label2, \
|
885
|
+
ylabeltxt,titletxt,footnote,power,datatag1,datatag2,zeroline)
|
886
|
+
|
887
|
+
"""
|
888
|
+
print("\n*** 投资组合的成分股与持仓比例")
|
889
|
+
print(pf1name+":"+list2str(product1))
|
890
|
+
print(pf2name+":"+list2str(product2))
|
891
|
+
"""
|
892
|
+
|
893
|
+
return
|
894
|
+
|
895
|
+
|
896
|
+
|
897
|
+
if __name__ =="__main__":
|
898
|
+
portfolio1={'Market':('US','^GSPC'),'PDD':1.0}
|
899
|
+
portfolio2={'Market':('US','^GSPC'),'BILI':1.0}
|
900
|
+
start='2020-1-01'
|
901
|
+
end='2020-6-30'
|
902
|
+
window=21
|
903
|
+
graph=False
|
904
|
+
liquidity_type='roll_spread'
|
905
|
+
compare_liquidity_rolling(portfolio1,portfolio2,start,end,liquidity_type)
|
906
|
+
|
907
|
+
liquidity_type='amihud_illiquidity'
|
908
|
+
compare_liquidity_rolling(portfolio1,portfolio2,start,end,liquidity_type)
|
909
|
+
|
910
|
+
liquidity_type='ps_liquidity'
|
911
|
+
compare_liquidity_rolling(portfolio1,portfolio2,start,end,liquidity_type)
|
912
|
+
#==============================================================================
|
913
|
+
#==============================================================================
|
914
|
+
|
915
|
+
|